3

我正在尝试拦截 401 响应,在再次尝试请求之前发送刷新令牌请求(但使用不同的标头)。我让它工作,除了retryWhen不让我修改原始请求标头。所以我一直在尝试使用catchError,但我似乎无法再次执行请求。

这是我目前的retryWhen

import {Injectable} from '@angular/core';
import {
    HttpInterceptor, HttpHandler, HttpRequest, HttpEvent, HttpErrorResponse
}
    from '@angular/common/http';

import {Observable} from 'rxjs/Observable';
import {CustomerService} from "../customer/customer.service";
import * as ApplicationSettings from "application-settings";
import {retryWhen, map} from "rxjs/operators";
import {LoginResponse} from "./LoginResponse";

/**
 * This takes a request that requires an access_token and refreshes it on 401 errors.
 */
@Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {

    public constructor(private customer: CustomerService) {}

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(req).pipe(
            retryWhen(errors => {
                return Observable.create(observer => {
                    errors.forEach((error: HttpErrorResponse) => {
                        if (error.status === 401) {
                            let refresh_token = ApplicationSettings.getString('refresh_token');

                            return this.customer.refreshToken(refresh_token).subscribe(
                                (response: LoginResponse) => {
                                    this.customer.setToken(response);

                                    let headers = req.headers.set('Authorization', `Bearer ${response.access_token}`);
                                    console.log(`Bearer ${response.access_token}`);
                                    let newReq = req.clone({headers: headers});
                                    observer.next(next.handle(newReq));
                                    observer.complete();
                                },
                                error2 => {
                                    observer.error();
                                }
                            );
                        } else {
                            observer.error();
                        }
                    });
                });
            })
        );
    }
}

如果我换掉retryWhencatchError

        catchError((err, caught) => {
            console.log('err: ' + JSON.stringify(err));

            if (err.status === 401) {
                console.log('401 !!!! REFRESH MEH!');
                let newReqOb: Observable<HttpEvent<any>> = Observable.create(observer => {
                    console.log('going to refresh token');
                    let refresh_token = ApplicationSettings.getString('refresh_token');

                    let refresh = this.customer.refreshToken(refresh_token);

                    refresh.subscribe((response: LoginResponse) => {
                        console.log('token refreshed!');
                        this.customer.setToken(response);


                        let access_token = ApplicationSettings.getString('access_token');
                        let headers = req.headers.set('Authorization', `Bearer ${access_token}`);

                        console.log(`Bearer ${access_token}`);

                        let newReq = req.clone({headers: headers});

                        observer.next(next.handle(newReq)); // <-- HERE IT WONT FIRE
                        observer.complete();

                    });

                });

                return newReqOb;
            }

            return caught;
        })

重要的是我要回来next.handle(newReq)了,它似乎没有触发请求。如果我将其切换为next.handle(newReq).subscribe(),请求将触发但不会触发回调。

这是完整的示例retryWhen

import {Inject, Injectable} from '@angular/core';
import {
    HttpInterceptor, HttpHandler, HttpRequest, HttpEvent, HttpErrorResponse, HttpClient
}
    from '@angular/common/http';

import {Observable, ObservableInput} from 'rxjs/Observable';
import {CustomerService} from "../customer/customer.service";
import * as ApplicationSettings from "application-settings";
import {retryWhen, map, catchError} from "rxjs/operators";
import {LoginResponse} from "./LoginResponse";
import {APP_CONFIG, AppConfig} from "../../app.config";

/**
 * This takes a request that requires an access_token and refreshes it on 401 errors.
 */
@Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {

    public constructor(private customer: CustomerService, private http: HttpClient, @Inject(APP_CONFIG) private config: AppConfig) {}

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(req).pipe(
            catchError((err, caught) => {
                console.log('err: ' + JSON.stringify(err));

                if (err.status === 401) {
                    console.log('401 !!!! REFRESH MEH!');
                    let newReqOb: Observable<HttpEvent<any>> = Observable.create(observer => {
                        console.log('going to refresh token');
                        let refresh_token = ApplicationSettings.getString('refresh_token');

                        let refresh = this.customer.refreshToken(refresh_token);

                        refresh.subscribe((response: LoginResponse) => {
                            console.log('token refreshed!');
                            this.customer.setToken(response);


                            let access_token = ApplicationSettings.getString('access_token');
                            let headers = req.headers.set('Authorization', `Bearer ${access_token}`);

                            console.log(`Bearer ${access_token}`);

                            let newReq = req.clone({headers: headers});

                            observer.next(next.handle(newReq));
                            observer.complete();

                        });

                    });

                    return newReqOb;
                }

                return caught;
            })
        );
    }
}
4

1 回答 1

3

我确实发现了问题,这是生成的代码:

import {Inject, Injectable} from '@angular/core';
import {
    HttpInterceptor, HttpHandler, HttpRequest, HttpEvent, HttpErrorResponse, HttpClient
}
    from '@angular/common/http';

import {Observable, ObservableInput} from 'rxjs/Observable';
import {CustomerService} from "../customer/customer.service";
import * as ApplicationSettings from "application-settings";
import {catchError, switchMap, finalize} from "rxjs/operators";
import {LoginResponse} from "./LoginResponse";
import {APP_CONFIG, AppConfig} from "../../app.config";
import {RouterExtensions} from "nativescript-angular/router";

/**
 * This takes a request that requires an access_token and refreshes it on 401 errors.
 * @TODO What happens on 400 errors?
 */
@Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {

    isRefreshingToken: boolean = false;

    public constructor(private customer: CustomerService,
                       private http: HttpClient,
                       private router: RouterExtensions,
                       @Inject(APP_CONFIG) private config: AppConfig) {}

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        let validate = {
            is_api_v1: req.url.indexOf('api/v1') > -1,
            is_not_register_end_point: !(req.url.indexOf('api/v1/customers') > -1 && req.method === 'POST')
        };

        if (validate.is_api_v1 && validate.is_not_register_end_point) {
            return next.handle(req).pipe(
                catchError((err, caught) => {
                    if (err instanceof HttpErrorResponse && err.status === 401) {
                        console.log(req.url);
                        console.log('Injecting Refresh Token');

                        return this.handle401Error(req, next);
                    }

                    // return caught;
                })
            );
        } else {
            return next.handle(req);
        }

    }

    handle401Error(req: HttpRequest<any>, next: HttpHandler) {
        //-- Test if we are refreshing so we are not stuck in an infinite loop
        if (!this.isRefreshingToken) {
            this.isRefreshingToken = true;

            let refresh_token = ApplicationSettings.getString('refresh_token');
            let refresh = this.customer.refreshToken(refresh_token);

            return refresh.pipe(
                switchMap((tokenResp: LoginResponse) => {
                    this.customer.setToken(tokenResp);

                    let access_token = ApplicationSettings.getString('access_token');
                    let headers = req.headers.set('Authorization', `Bearer ${access_token}`);

                    return next.handle(req.clone({headers: headers}));
                }),
                catchError(error => {
                    ApplicationSettings.setBoolean("authenticated", false);
                    this.router.navigate(["/login"], { clearHistory: true, queryParams: {
                        error: 'Your session is no longer valid, please log in again',
                    }});

                    return Observable.throw("");
                }),
                finalize(() => {
                    this.isRefreshingToken = false;
                })
            )
        }
    }
}
于 2018-04-09T13:05:50.637 回答