使用Interceptor在Angular 4.3中使用新标头重试Http请求

时间:2017-08-10 13:36:41

标签: angular http rxjs interceptor

如何使用新标头处理克隆请求?

我试图在新的拦截器中执行克隆请求,并在第一个请求失败后生成新令牌!

  • 如果错误401 - >刷新令牌
  • 使用新标头
  • 发回上一个请求的副本

这是我试过的:

  refreshSessionToken() {
    return this.http.post('/connect', {})
      .map(res => {
        const token = res.json();
        localStorage.setItem('token', token);
      return token;
      });
  }


// Get the headers
const headers = getAuthenticationHeaders();
const reqMain = req.clone({headers: headers});
return next.handle(reqMain).catch(err => {

       this.refreshSessionToken()
            .subscribe(token => {
                const t = token.token_id;
                const clonedReq = req.clone({headers: req.headers.set('access-token', t)});

                return next.handle(clonedReq);
                    });
         })

获取 clonedReq 的日志我可以看到令牌已刷新,但订阅中的请求(clonedReq)未执行,为什么?!

我尝试了其他关于如何刷新JWT的方法,但在我的情况下似乎不起作用,有关如何处理它的任何帮助?

谢谢!

我期望什么是我的结果?

  • 发送1#HTTP请求
  • 1#request failed
  • 刷新令牌(获取/设置令牌)
  • 克隆上一个请求并添加刷新后的令牌
  • 发送2#HTTP请求
  • 2#要求成功

类似问题:

http-interceptor-refresh-jwt-token

3 个答案:

答案 0 :(得分:3)

以下通用方法可用于拦截以及添加/删除呼叫和响应的其他信息。

好的,这里是完整的代码。

<强> InterceptedHttp.ts

import { Injectable } from "@angular/core";
import { RequestOptions, Http, Headers, Response, RequestMethod, URLSearchParams } from "@angular/http";
import { Observable, Observer } from 'rxjs/Rx';

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import "rxjs/add/operator/mergeMap";

@Injectable()
export class InterceptedHttp {

    constructor(private http: Http) { }

    getRequestOption(method: RequestMethod | string, data?: any, params?: any): RequestOptions {
        let options = new RequestOptions();
        options.headers = new Headers();
        //options.headers.append('Content-Type', 'application/json');
        options.method = method;

        let token: string = localStorage.getItem('token');
        //if (token) options.headers.append('Authorization', 'Bearer ' + token);

        if (data) options.body = data;

        if (params) {
            options.search = new URLSearchParams();
            let keys: string[] = Object.keys(params);

            keys.forEach((key, index) => {
                options.search.set(key, params[key]);
            });
        }

        return options;
    }

    refreshSessionToken(): Observable<string> {
        //Put some user identification data
        let userData: any = {id: 'abc'};
        return this.http.post('/refreshToken', userData)
            .map(res => {
                let token = res.json();
                localStorage.setItem('token', token);
                return token;
            });
    }

    getApiResponse<T>(url: string, method: RequestMethod | string, data?: Object): Observable<T> {
        let op1: RequestOptions = this.getRequestOption(method, data);
       return  this.http.request(url, op1)
            .catch((err) => {
                // UnAuthorised, 401
                if (err.status == 401) {
                    return this.refreshSessionToken().flatMap(t => {
                        let op2 = this.getRequestOption(method, data);
                        return this.http.request(url, op2);
                    });
                }
                throw err;
            })
            .map((response: Response) => {
                let ret: T = response.json();
                return ret;
            });
    }

    get<T>(url: string): Observable<T> {
        return this.getApiResponse<T>(url, RequestMethod.Get);
    }

    post<T, R>(url: string, body: T): Observable<R> {
        return this.getApiResponse<R>(url, RequestMethod.Post, body);
    }

    put<T, R>(url: string, body: T): Observable<R> {
        return this.getApiResponse<R>(url, RequestMethod.Put, body);
    }

    delete<T>(url: string): Observable<T> {
        return this.getApiResponse<T>(url, RequestMethod.Delete);
    }
}

<强> DataService.ts

    import { Injectable } from '@angular/core';
    import { Observable } from 'rxjs/Observable';
    import { User } from './User';
    import { InterceptedHttp } from './http.interceptor';

    @Injectable()
    export class DataService {
        constructor(private apiHandler: InterceptedHttp) { }

        getAll(): Observable<User[]> {
            return this.apiHandler.get<User[]>('http://mocker.egen.io/users');
        }
    }

<强> User.ts

    export class User {
        id?: number;

        firstName?: string;

        lastName?: string;
    }

<强> AppComponent.ts

    import { Component } from '@angular/core';
    import { DataService } from './user-data.service';
    import { User } from './User';

    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      title = 'app works!';
      users: User[];

      constructor(dataService: DataService){
        dataService.getAll().subscribe((u) => {
          this.users = u;
        });
      }  
    }

<强> app.component.html

<h1>
  <table>
    <tr *ngFor="let item of users; let i = index">
      <td> {{item.firstName}} </td>
    </tr>
  </table>
</h1>

答案 1 :(得分:1)

这是我使用最新版本的Angular(7.0.0)和rxjs(6.3.3)的解决方案。希望对您有所帮助。

export class SessionRecoveryInterceptor implements HttpInterceptor {
  constructor(
    private readonly store: StoreService,
    private readonly sessionService: AuthService
  ) {}

  private _refreshSubject: Subject<any> = new Subject<any>();

  private _ifTokenExpired() {
    this._refreshSubject.subscribe({
      complete: () => {
        this._refreshSubject = new Subject<any>();
      }
    });
    if (this._refreshSubject.observers.length === 1) {
      // Hit refresh-token API passing the refresh token stored into the request
      // to get new access token and refresh token pair
      this.sessionService.refreshToken().subscribe(this._refreshSubject);
    }
    return this._refreshSubject;
  }

  private _checkTokenExpiryErr(error: HttpErrorResponse): boolean {
    return (
      error.status &&
      error.status === 401 &&
      error.error &&
      error.error.message === "TokenExpired"
    );
  }

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    if (req.url.endsWith("/logout") || req.url.endsWith("/token-refresh")) {
      return next.handle(req);
    } else {
      return next.handle(req).pipe(
        catchError((error, caught) => {
          if (error instanceof HttpErrorResponse) {
            if (this._checkTokenExpiryErr(error)) {
              return this._ifTokenExpired().pipe(
                switchMap(() => {
                  return next.handle(this.updateHeader(req));
                })
              );
            } else {
              return throwError(error);
            }
          }
          return caught;
        })
      );
    }
  }

  updateHeader(req) {
    const authToken = this.store.getAccessToken();
    req = req.clone({
      headers: req.headers.set("Authorization", `Bearer ${authToken}`)
    });
    return req;
  }
}

我回答了类似的问题here。您也可以阅读我的文章here,以了解代码。

答案 2 :(得分:0)

我回答了类似的问题here

您无法将克隆的请求传递到下一个处理程序。相反,使用HttpClient重新尝试克隆的请求。

this.http.request(clonedReq).subscribe(......);