Angular 6 HttpInterceptor - при получении 401 обновите токен и создайте тот же запрос

Я использую HttpInterceptor в Angular 6 и пытаюсь создать механизм токена обновления:

Когда запрос httpClient получает 401 код состояния (неавторизованный), HttpInterceptor создаст запрос, который обновит токен, он обновит заголовки первого запроса и вызовет его снова с новым токеном.

Код работает до тех пор, пока мне не нужно снова вспомнить исходный запрос с новым токеном, полученным из запроса токена обновления. Это мой код:

export class MyInterceptor implements HttpInterceptor {

constructor(public restService:RestService){}

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
 return next.handle(request).pipe(
    tap(event => {
      if (event instanceof HttpResponse) {
        console.log('succeed');
      }
    }, error => {
        if(error.status==401){
            this.restService.refreshToken().subscribe(response => {
                this.restService.updateHeaders(response['token']);
                const newRequest = request.clone();
                return next.handle(newRequest);
              });
        }
    })
  )
}
}



Ответы (2)


Вам нужно сделать что-то вроде ниже. Вам также необходимо убедиться, что к запросу будет добавлен новый заголовок. Не уверен, где вы это делаете, так как его нет в этом перехватчике. Лучше всего добавить его в перехватчик. Даже этот, если вы действительно делаете это в сервисах.

// if multiple requests fail, do only one refresh request
private readonly refreshToken$ = this.restService
    .refreshToken() //the refresh token method could update the token in it's internal state, not sure why the later call to updateHeaders
    .pipe(
        tap(response => this.restService.updateHeaders(response['token'])),
        ignoreElements(),
        shareReplay(1)
    );

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next
        .handle(request)
        .pipe(
            catchError(error => {
                if (error.status === 401) {
                    return concat(this.refreshToken$, throwError(new RetryRequestError()));
                } else {
                    throw error;
                }
            }),
            retryWhen(error => {
                if (error instanceof RetryRequestError) {
                    // try the request again
                    return;
                }
                throw error;
            })
        );
}

class RetryRequestError extends Error {
    constructor() {
        super('retry_request');
        Object.setPrototypeOf(this, RetryRequestError.prototype);
    }
}
person Andrei Tătar    schedule 10.08.2018
comment
Thnaks Андрей Татар, - person Ariel; 06.03.2019

Я прочитал об этом больше и использовал код, который кто-то написал, и я немного изменил его и модифицировал, и, наконец, это сработало для меня. конечно, если кто-то использует этот код, он должен его модифицировать.

import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { catchError, switchMap } from 'rxjs/operators';
import { Injectable } from "@angular/core";
import { HttpInterceptor, HttpRequest, HttpHandler, HttpSentEvent, HttpHeaderResponse, HttpProgressEvent, HttpResponse, HttpUserEvent, HttpErrorResponse } from "@angular/common/http";
import { _throw as observableThrowError } from 'rxjs/observable/throw';
import { Router } from "@angular/router";
import { environment } from '../../../environments/environment'

@Injectable()
export class RequestInterceptorService implements HttpInterceptor {

    public endPoints;

    constructor(public httpClient: HttpClient, public router: Router) { this.endPoints = environment.endPoints; }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
        return <any>next.handle(req.clone({ headers: req.headers.set('Cache-Control', 'no-cache').set('Pragma', 'no-cache'), withCredentials: true })).pipe(
            catchError(error => {
                if (req.url.indexOf('refresh_token_login') != -1 && ((<HttpErrorResponse>error).status) == 401) {//the app created request for getting new token/session and got 401,meaning the refresh token/session also expired
                    this.router.navigate(['/logout']);
                }
                else if (((<HttpErrorResponse>error).status) == 401 && (<HttpErrorResponse>error).error.error_code == 401001) { // 401001 meaning that the token is invalid
                    this.router.navigate(['/logout']);
                }
                else if (((<HttpErrorResponse>error).status) == 401 && (<HttpErrorResponse>error).error.error_code == 401002) { // 401002 meaning that the token has expired
                    return this.handle401Error(req, next);
                } else {
                    return observableThrowError(error.error);
                }
            }));
    }

    handle401Error(req: HttpRequest<any>, next: HttpHandler) {
        return this.refreshToken().pipe(
            switchMap((res) => {
                if (res.status == 200) {
                    return next.handle(this.getNewRequest(req));
                }else{
                    return this.logoutUser();
                }
            }),
            catchError(error => { 
                   return next.handle(this.getNewRequest(req));
            })
        )
    }

    getNewRequest(req: HttpRequest<any>): HttpRequest<any> {
        return req.clone({ headers: req.headers.set('Cache-Control', 'no-cache').set('Pragma', 'no-cache'), withCredentials: true });
    }

    logoutUser() {
        this.router.navigate(['/logout']);
        return observableThrowError("");
    }

    refreshToken() {
        return this.httpClient.get(this.endPoints.refreshToken, { observe: 'response' }).pipe(
            catchError(error => {
                return observableThrowError(error);
            }));
    }
}
person Ariel    schedule 06.03.2019