【问题标题】:Angular interceptors : how to manage retry (nested next.handle())?角度拦截器:如何管理重试(嵌套 next.handle())?
【发布时间】:2021-04-27 03:39:27
【问题描述】:

我一直在努力构建一个拦截器,它将:

  • 拦截每个请求
  • 在本地存储中添加身份验证令牌
  • 无论如何做请求
  • 取回此请求,记录结果以及是否存在 401/403:
    • 调用入口点刷新令牌
    • 在接收时,重试请求
    • 检查结果并记录是否有效

在这种情况下,我必须管理两种类型的重试(用于令牌管理):

  • 一个用于 django
  • 一个用于 pryv 的解决方案是另一种解决方案,并且必须在请求中添加不同的令牌

我无法管理两个 next.handle() 第二个永远不会被触发并且请求不会重试......

这是我的代码:

   export class HttpInterceptorService implements HttpInterceptor {
        constructor(private authService: AuthenticationService,
                private pryvAccessesService: PryvAccessesService) { }
    
        intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            const uuid = new Date().valueOf();
            if (!req.url.includes('api/authentication/token/')) {
                const authReq = this.addTokensRequest(req);
                console.log(uuid + " : sending request with tokens : " + req.url);
                return next.handle(authReq).pipe(
                    map(res => {console.log(uuid + ' : request succedeed ! ' + req.url); return res;}),
                    catchError(err => {
                        console.log(uuid + ' : request failed ! ' + req.url);
                        // Django invalid token
                        if (err.status === 401) {
                            console.log(uuid + " : sending django refresh request : " + req.url);
                            return this.authService.refresh().pipe(
                                map((res: any) => {
                                    const authReqRefreshed = this.addTokensRequest(req);
                                    console.log(uuid + " : sending refreshed request : " + req.url);
                                    return next.handle(authReqRefreshed).pipe(
                                        map(res => {console.log( uuid + ' : success refreshed request ! ' + req.url); return res;}),
                                        catchError(err => {console.log(uuid + " failed refreshed request : " + req.url); return throwError(err);})
                                    );
                                })
                            )
                        // Pryv invalid token
                        } else if(err.status === 400 && err.error['error'] == 'Invalid access token within Pryv usage') {
                            console.log(uuid + ' : bad pryv token : ' + req.url);
                            return this.getNewTokenForUrl(req).pipe(
                                map((res: string) => {
                                    console.log(uuid + " : success get new token for URL : " + req.url);
                                    const authReqNewPryv = this.addTokensRequest(req); // <= code goes up to here
                                    return next.handle(authReqNewPryv).pipe(
                                        map(res => {console.log(uuid + ' : success request with new pryv token ! ' + req.url); return res;}), // <= this is never triggered
                                        catchError((err) => {console.log(uuid + " : error request with new pryv token : " + req.url); return throwError(err);}) // <= this also is never triggered
                                    );
                                }), catchError((err) => {console.log(uuid + " fail get new pryv token : " + req.url); return throwError(err);})
                            );
                        // Unrelated error
                        } else {
                            console.log(uuid + " error not related to authentication : " + req.url); return throwError(err);
                        }
                    })
                );
            } else {
                console.log(uuid + " authentication, passing thru " + req.url); return next.handle(req);
            }

例如,'console.log(uuid + " : success get new token for URL : " + req.url);'永远不会显示日志,也永远不会发送重试请求。 如何管理它?我已经尝试发送另一个 HttpRequest,但拦截器正在捕获它...

谢谢!

【问题讨论】:

    标签: angular authentication angular-http-interceptors


    【解决方案1】:

    您应该将此拦截器的职责拆分为多个拦截器,如下所示:

    AppModule 中的 Inteceptors 令牌:

    // Important: Order of Interceptors matters!
    export const httpInterceptorProviders = [
        { provide: HTTP_INTERCEPTORS, useClass: RetryInterceptor, multi: true },
        { provide: HTTP_INTERCEPTORS, useClass: AuthTokensManagementInterceptor, multi: true },
        { provide: HTTP_INTERCEPTORS, useClass: TelemetryInterceptor, multi: true },
    ];
    

    身份验证令牌拦截器:

    public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            return of(req).pipe(
                mergeMap(() => getAcessToken() /*and add it to local storage*/)
                mergeMap((accessToken: string) => {
                    if (accessToken) {
                        req = req.clone({
                            headers: req.headers.set(AuthorizationHeader, `${accessToken}`)
                        })
                    }
                    return next.handle(req).pipe(
                        catchError((error: HttpErrorResponse) => {
                            if (error instanceof HttpErrorResponse) {
                                if (error.status === 401)
                                    this.authTokensManager.invalidateAccessToken(error.url);
                                }
                            }
    
                            return throwError(error);
                        })
                    );
            }));
        }
    

    重试拦截器:

    public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            return of(req).pipe(
                mergeMap((req: HttpRequest<any>) => {
                    return next.handle(req).pipe(
                        // Will continue the pipe if a request attempt has succeeded without errors, or exceeded max retries.
                        retryWhen((errors: Observable<any>) => errors.pipe(
                            // Enable only 2 retries, +1 for the original request.
                            take(this.maxRetries + 1),
                            skipWhile(() => disableRetry),
                            mergeMap((err: HttpErrorResponse, i: number) => {
                                if (i >= this.maxRetries) {
                                    return throwError(err);
                                }
    
                                const errorCode = err.status;
                                
                                if (errorCode == 401) {
                                    return of(err);
                                }
    
                                return throwError(err);
                            }),
                        ))
                    )
                })
            );
        }
    

    【讨论】:

      猜你喜欢
      • 2014-10-25
      • 1970-01-01
      • 2017-10-09
      • 2020-12-28
      • 2016-02-02
      • 1970-01-01
      • 1970-01-01
      • 2018-03-10
      • 2019-07-09
      相关资源
      最近更新 更多