Yêu cầu thử lại Angular 4 Interceptor sau khi làm mới mã thông báo


84

Xin chào, tôi đang cố gắng tìm ra cách triển khai các bộ chặn góc cạnh mới và xử lý 401 unauthorizedlỗi bằng cách làm mới mã thông báo và thử lại yêu cầu. Đây là hướng dẫn tôi đã làm theo: https://ryanchenkie.com/angular-authentication-using-the-http-client-and-http-interceptors

Tôi đang lưu trữ thành công các yêu cầu không thành công và có thể làm mới mã thông báo nhưng tôi không thể tìm ra cách gửi lại các yêu cầu không thành công trước đó. Tôi cũng muốn điều này hoạt động với các trình phân giải mà tôi hiện đang sử dụng.

token.interceptor.ts

return next.handle( request ).do(( event: HttpEvent<any> ) => {
        if ( event instanceof HttpResponse ) {
            // do stuff with response if you want
        }
    }, ( err: any ) => {
        if ( err instanceof HttpErrorResponse ) {
            if ( err.status === 401 ) {
                console.log( err );
                this.auth.collectFailedRequest( request );
                this.auth.refreshToken().subscribe( resp => {
                    if ( !resp ) {
                        console.log( "Invalid" );
                    } else {
                        this.auth.retryFailedRequests();
                    }
                } );

            }
        }
    } );

xác thực.service.ts

cachedRequests: Array<HttpRequest<any>> = [];

public collectFailedRequest ( request ): void {
    this.cachedRequests.push( request );
}

public retryFailedRequests (): void {
    // retry the requests. this method can
    // be called after the token is refreshed
    this.cachedRequests.forEach( request => {
        request = request.clone( {
            setHeaders: {
                Accept: 'application/json',
                'Content-Type': 'application/json',
                Authorization: `Bearer ${ this.getToken() }`
            }
        } );
        //??What to do here
    } );
}

Tệp retryFailedRequests () ở trên là những gì tôi không thể tìm ra. Làm cách nào để gửi lại các yêu cầu và cung cấp chúng cho tuyến thông qua trình phân giải sau khi thử lại?

Đây là tất cả mã có liên quan nếu điều đó hữu ích: https://gist.github.com/joshharms/00d8159900897dc5bed45757e30405f9


3
Tôi có cùng một vấn đề, và dường như không có câu trả lời.
LastTribunal

Câu trả lời:


98

Giải pháp cuối cùng của tôi. Hoạt động với các yêu cầu song song.

CẬP NHẬT: Mã được cập nhật với Angular 9 / RxJS 6, xử lý lỗi và sửa lỗi lặp khi refreshToken không thành công

import { HttpRequest, HttpHandler, HttpInterceptor, HTTP_INTERCEPTORS } from "@angular/common/http";
import { Injector } from "@angular/core";
import { Router } from "@angular/router";
import { Subject, Observable, throwError } from "rxjs";
import { catchError, switchMap, tap} from "rxjs/operators";
import { AuthService } from "./auth.service";

export class AuthInterceptor implements HttpInterceptor {

    authService;
    refreshTokenInProgress = false;

    tokenRefreshedSource = new Subject();
    tokenRefreshed$ = this.tokenRefreshedSource.asObservable();

    constructor(private injector: Injector, private router: Router) {}

    addAuthHeader(request) {
        const authHeader = this.authService.getAuthorizationHeader();
        if (authHeader) {
            return request.clone({
                setHeaders: {
                    "Authorization": authHeader
                }
            });
        }
        return request;
    }

    refreshToken(): Observable<any> {
        if (this.refreshTokenInProgress) {
            return new Observable(observer => {
                this.tokenRefreshed$.subscribe(() => {
                    observer.next();
                    observer.complete();
                });
            });
        } else {
            this.refreshTokenInProgress = true;

            return this.authService.refreshToken().pipe(
                tap(() => {
                    this.refreshTokenInProgress = false;
                    this.tokenRefreshedSource.next();
                }),
                catchError(() => {
                    this.refreshTokenInProgress = false;
                    this.logout();
                }));
        }
    }

    logout() {
        this.authService.logout();
        this.router.navigate(["login"]);
    }

    handleResponseError(error, request?, next?) {
        // Business error
        if (error.status === 400) {
            // Show message
        }

        // Invalid token error
        else if (error.status === 401) {
            return this.refreshToken().pipe(
                switchMap(() => {
                    request = this.addAuthHeader(request);
                    return next.handle(request);
                }),
                catchError(e => {
                    if (e.status !== 401) {
                        return this.handleResponseError(e);
                    } else {
                        this.logout();
                    }
                }));
        }

        // Access denied error
        else if (error.status === 403) {
            // Show message
            // Logout
            this.logout();
        }

        // Server error
        else if (error.status === 500) {
            // Show message
        }

        // Maintenance error
        else if (error.status === 503) {
            // Show message
            // Redirect to the maintenance page
        }

        return throwError(error);
    }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
        this.authService = this.injector.get(AuthService);

        // Handle request
        request = this.addAuthHeader(request);

        // Handle response
        return next.handle(request).pipe(catchError(error => {
            return this.handleResponseError(error, request, next);
        }));
    }
}

export const AuthInterceptorProvider = {
    provide: HTTP_INTERCEPTORS,
    useClass: AuthInterceptor,
    multi: true
};

3
@AndreiOstrovski, bạn có thể vui lòng cập nhật câu trả lời với importsvà mã của AuthService không?
mất từ

4
Tôi có cảm giác rằng nếu vì lý do nào đó this.authService.refreshToken () không thành công, tất cả các truy vấn song song đang chờ làm mới sẽ đợi mãi mãi.
Maksim Gumerov

2
Việc nắm bắt mã thông báo làm mới không bao giờ gọi cho tôi. Nó chạm vào .throw có thể quan sát.
jamesmpw

2
Các bạn, nó hoạt động với các yêu cầu song song và tuần tự. Bạn gửi 5 yêu cầu, chúng trả về 401, sau đó 1 refreshToken được thực hiện và 5 yêu cầu lại. Nếu 5 yêu cầu của bạn là tuần tự, sau 401 đầu tiên, chúng tôi gửi refreshToken, sau đó yêu cầu đầu tiên lại và 4 yêu cầu khác.
Andrei Ostrovski

2
Tại sao bạn lại tự tiêm dịch vụ trong khi Angular có thể làm điều đó cho bạn nếu bạn trang trí nó bằng @Injectable()? Ngoài ra, một catchError không trả về bất cứ thứ gì. Ít nhất là trở lại EMPTY.
Győri Sándor

16

Với phiên bản mới nhất của Angular (7.0.0) và rxjs (6.3.3), đây là cách tôi tạo một bộ chặn khôi phục phiên tự động đầy đủ chức năng đảm bảo, nếu các yêu cầu đồng thời không thành công với 401, thì nó cũng chỉ nên nhấn API làm mới mã thông báo một lần và chuyển các yêu cầu không thành công đến phản hồi của yêu cầu đó bằng switchMap và Subject. Dưới đây là mã đánh chặn của tôi trông như thế nào. Tôi đã bỏ qua mã cho dịch vụ xác thực và dịch vụ cửa hàng của mình vì chúng là các lớp dịch vụ khá chuẩn.

import {
  HttpErrorResponse,
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest
} from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable, Subject, throwError } from "rxjs";
import { catchError, switchMap } from "rxjs/operators";

import { AuthService } from "../auth/auth.service";
import { STATUS_CODE } from "../error-code";
import { UserSessionStoreService as StoreService } from "../store/user-session-store.service";

@Injectable()
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) {
      this.sessionService.refreshToken().subscribe(this._refreshSubject);
    }
    return this._refreshSubject;
  }

  private _checkTokenExpiryErr(error: HttpErrorResponse): boolean {
    return (
      error.status &&
      error.status === STATUS_CODE.UNAUTHORIZED &&
      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;
  }
}

Theo nhận xét của @ anton-toshik, tôi nghĩ nên giải thích hoạt động của mã này trong một bài viết. Bạn có thể đọc bài viết của tôi ở đây để biết giải thích và hiểu về mã này (cách thức và lý do nó hoạt động?). Hy vọng nó giúp.


1
công việc tốt, thứ hai returnbên trong interceptchức năng nên xem xét như thế này: return next.handle(this.updateHeader(req)).pipe(. Hiện tại bạn chỉ gửi auth dấu hiệu sau khi làm mới nó ...
malimo

Tôi nghĩ rằng tôi đang làm điều đó thông qua sơ đồ chuyển đổi. Vui lòng kiểm tra lại. Hãy cho tôi biết nếu tôi hiểu sai ý của bạn.
Samarpan

vâng nó về cơ bản làm việc nhưng bạn luôn gửi yêu cầu hai lần - một lần mà không có tiêu đề, và sau đó sau khi thất bại với tiêu đề ....
malimo

@SamarpanBhattacharya Điều này hoạt động. Tôi nghĩ câu trả lời này có thể giải thích bằng ngữ nghĩa cho một người như tôi, những người không hiểu cách hoạt động của Observable.
Anton Toshik

1
@NikaKurashvili, định nghĩa Phương pháp này làm việc cho tôi:public refreshToken(){const url:string=environment.apiUrl+API_ENDPOINTS.REFRESH_TOKEN;const req:any={token:this.getAuthToken()};const head={};const header={headers:newHttpHeaders(head)};return this.http.post(url,req,header).pipe(map(resp=>{const actualToken:string=resp['data'];if(actualToken){this.setLocalStorage('authToken',actualToken);}return resp;}));}
Shrinivas

9

Tôi cũng gặp phải một vấn đề tương tự và tôi nghĩ rằng logic thu thập / thử lại quá phức tạp. Thay vào đó, chúng tôi chỉ có thể sử dụng toán tử bắt để kiểm tra 401, sau đó theo dõi quá trình làm mới mã thông báo và chạy lại yêu cầu:

return next.handle(this.applyCredentials(req))
  .catch((error, caught) => {
    if (!this.isAuthError(error)) {
      throw error;
    }
    return this.auth.refreshToken().first().flatMap((resp) => {
      if (!resp) {
        throw error;
      }
      return next.handle(this.applyCredentials(req));
    });
  }) as any;

...

private isAuthError(error: any): boolean {
  return error instanceof HttpErrorResponse && error.status === 401;
}

1
Tôi thích sử dụng mã trạng thái tùy chỉnh là 498 để xác định mã thông báo đã hết hạn so với mã 401 cũng có thể chỉ ra là không đủ priv
Joseph Carroll

1
Xin chào, tôi đang thử sử dụng return next.handle (reqClode) và không làm gì cả, mã của tôi khác với abit của bạn nhưng phần không hoạt động là phần trả về. authService.createToken (authToken, refreshToken); this.inflightAuthRequest = null; return next.handle (req.clone ({headers: req.headers.set (appGlobals.AUTH_TOKEN_KEY, authToken)}));

6
Logic thu thập / thử lại không quá phức tạp, là cách bạn phải làm nếu không muốn thực hiện nhiều yêu cầu đến điểm cuối refreshToken trong khi mã thông báo của bạn đã hết hạn. Giả sử mã thông báo của bạn đã hết hạn và bạn thực hiện 5 yêu cầu gần như cùng một lúc. Với logic trong nhận xét này, 5 mã thông báo làm mới mới sẽ được tạo phía máy chủ.
Marius Lazar

4
@JosephCarroll thường không đủ đặc quyền là 403
andrea.spot.

8

Giải pháp cuối cùng của Andrei Ostrovski hoạt động thực sự tốt, nhưng không hoạt động nếu mã thông báo làm mới cũng đã hết hạn (giả sử bạn đang thực hiện lệnh gọi api để làm mới). Sau một số lần đào, tôi nhận ra rằng lệnh gọi API mã thông báo làm mới cũng bị chặn bởi bộ đánh chặn. Tôi đã phải thêm câu lệnh if để xử lý điều này.

 intercept( request: HttpRequest<any>, next: HttpHandler ):Observable<any> {
   this.authService = this.injector.get( AuthenticationService );
   request = this.addAuthHeader(request);

   return next.handle( request ).catch( error => {
     if ( error.status === 401 ) {

     // The refreshToken api failure is also caught so we need to handle it here
       if (error.url === environment.api_url + '/refresh') {
         this.refreshTokenHasFailed = true;
         this.authService.logout();
         return Observable.throw( error );
       }

       return this.refreshAccessToken()
         .switchMap( () => {
           request = this.addAuthHeader( request );
           return next.handle( request );
         })
         .catch((err) => {
           this.refreshTokenHasFailed = true;
           this.authService.logout();
           return Observable.throw( err );
         });
     }

     return Observable.throw( error );
   });
 }

Bạn có thể chỉ nơi bạn chơi với refreshTokenHasFailedboolean thành viên không?
Stephane

1
Bạn có thể tìm thấy nó trên giải pháp của Andrei Ostrovski ở trên, về cơ bản tôi đã sử dụng nó nhưng đã thêm câu lệnh if để xử lý khi điểm cuối làm mới bị chặn.
James Lieu

Điều này không có ý nghĩa, tại sao làm mới lại trả về 401? Vấn đề là nó đang gọi làm mới sau khi xác thực không thành công, vì vậy API làm mới của bạn hoàn toàn không phải xác thực và không nên trả về 401.
MDave

Làm mới mã thông báo có thể có ngày hết hạn. Trong trường hợp sử dụng của chúng tôi, nó được đặt là hết hạn sau 4 giờ, nếu người dùng đóng trình duyệt của họ vào cuối ngày và quay lại vào sáng hôm sau, mã làm mới sẽ hết hạn vào thời điểm đó và do đó chúng tôi yêu cầu họ đăng nhập quay lại lần nữa. Nếu mã thông báo làm mới của bạn không hết hạn thì tất nhiên bạn sẽ không cần áp dụng logic này
James Lieu

4

Dựa trên ví dụ này , đây là tác phẩm của tôi

@Injectable({
    providedIn: 'root'
})
export class AuthInterceptor implements HttpInterceptor {

    constructor(private loginService: LoginService) { }

    /**
     * Intercept request to authorize request with oauth service.
     * @param req original request
     * @param next next
     */
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
        const self = this;

        if (self.checkUrl(req)) {
            // Authorization handler observable
            const authHandle = defer(() => {
                // Add authorization to request
                const authorizedReq = req.clone({
                    headers: req.headers.set('Authorization', self.loginService.getAccessToken()
                });
                // Execute
                return next.handle(authorizedReq);
            });

            return authHandle.pipe(
                catchError((requestError, retryRequest) => {
                    if (requestError instanceof HttpErrorResponse && requestError.status === 401) {
                        if (self.loginService.isRememberMe()) {
                            // Authrozation failed, retry if user have `refresh_token` (remember me).
                            return from(self.loginService.refreshToken()).pipe(
                                catchError((refreshTokenError) => {
                                    // Refresh token failed, logout
                                    self.loginService.invalidateSession();
                                    // Emit UserSessionExpiredError
                                    return throwError(new UserSessionExpiredError('refresh_token failed'));
                                }),
                                mergeMap(() => retryRequest)
                            );
                        } else {
                            // Access token failed, logout
                            self.loginService.invalidateSession();
                            // Emit UserSessionExpiredError
                            return throwError(new UserSessionExpiredError('refresh_token failed')); 
                        }
                    } else {
                        // Re-throw response error
                        return throwError(requestError);
                    }
                })
            );
        } else {
            return next.handle(req);
        }
    }

    /**
     * Check if request is required authentication.
     * @param req request
     */
    private checkUrl(req: HttpRequest<any>) {
        // Your logic to check if the request need authorization.
        return true;
    }
}

Bạn có thể muốn kiểm tra xem người dùng có cho phép Remember Mesử dụng mã làm mới để thử lại hay chỉ chuyển hướng đến trang đăng xuất.

Fyi, LoginServicecó các phương thức sau:
- getAccessToken (): string - trả về hiện tại access_token
- isRememberMe (): boolean - kiểm tra xem người dùng có refresh_token
- refreshToken (): Observable / Promise - Yêu cầu máy chủ oauth để access_tokensử dụng mới refresh_token
- validateSession (): void - xóa tất cả thông tin người dùng và chuyển hướng đến trang đăng xuất


Bạn có gặp sự cố với nhiều yêu cầu gửi nhiều yêu cầu làm mới không?
CodingGorilla

Phiên bản này tôi thích nhất nhưng tôi đang gặp sự cố trong đó tôi đưa ra yêu cầu, khi trả về 401 cố gắng làm mới, khi trả về lỗi, nó liên tục cố gắng gửi lại yêu cầu, không bao giờ dừng lại. Tôi có đang làm gì đó sai không?
jamesmpw

Xin lỗi, cái trước mình chưa test kỹ. Chỉ cần chỉnh sửa bài đăng của tôi bằng bài viết đã thử nghiệm mà tôi đang sử dụng (cũng chuyển sang mã thông báo rxjs6 và refesh, kiểm tra url).
Thanh Nhàn,

1

Tốt nhất, bạn muốn kiểm tra isTokenExpiredtrước khi yêu cầu được gửi đi. Và nếu hết hạn, hãy làm mới mã thông báo và thêm làm mới trong tiêu đề.

Khác với điều đó retry operatorcó thể giúp ích cho logic của bạn về việc làm mới mã thông báo trên phản hồi 401.

Sử dụng RxJS retry operatortrong dịch vụ của bạn khi bạn đang yêu cầu. Nó chấp nhận một retryCountđối số. Nếu không được cung cấp, nó sẽ thử lại trình tự vô thời hạn.

Trong bộ chặn của bạn khi phản hồi, hãy làm mới mã thông báo và trả lại lỗi. Khi dịch vụ của bạn gặp lại lỗi nhưng hiện tại toán tử thử lại đang được sử dụng, vì vậy, nó sẽ thử lại yêu cầu và lần này với mã thông báo được làm mới (Interceptor sử dụng mã thông báo được làm mới để thêm vào tiêu đề.)

import {HttpClient} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';

@Injectable()
export class YourService {

  constructor(private http: HttpClient) {}

  search(params: any) {
    let tryCount = 0;
    return this.http.post('https://abcdYourApiUrl.com/search', params)
      .retry(2);
  }
}

0
To support ES6 syntax the solution needs to be bit modify and that is as following also included te loader handler on multiple request


        private refreshTokenInProgress = false;
        private activeRequests = 0;
        private tokenRefreshedSource = new Subject();
        private tokenRefreshed$ = this.tokenRefreshedSource.asObservable();
        private subscribedObservable$: Subscription = new Subscription();



 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if (this.activeRequests === 0) {
            this.loaderService.loadLoader.next(true);
        }
        this.activeRequests++;

        // Handle request
        request = this.addAuthHeader(request);

        // NOTE: if the flag is true it will execute retry auth token mechanism ie. by using refresh token it will fetch new auth token and will retry failed api with new token
        if (environment.retryAuthTokenMechanism) {
            // Handle response
            return next.handle(request).pipe(
                catchError(error => {
                    if (this.authenticationService.refreshShouldHappen(error)) {
                        return this.refreshToken().pipe(
                            switchMap(() => {
                                request = this.addAuthHeader(request);
                                return next.handle(request);
                            }),
                            catchError(() => {
                                this.authenticationService.setInterruptedUrl(this.router.url);
                                this.logout();
                                return EMPTY;
                            })
                        );
                    }

                    return EMPTY;
                }),
                finalize(() => {
                    this.hideLoader();
                })
            );
        } else {
            return next.handle(request).pipe(
                catchError(() => {
                    this.logout();
                    return EMPTY;
                }),
                finalize(() => {
                    this.hideLoader();
                })
            );
        }
    }

    ngOnDestroy(): void {
        this.subscribedObservable$.unsubscribe();
    }

    /**
     * @description Hides loader when all request gets complete
     */
    private hideLoader() {
        this.activeRequests--;
        if (this.activeRequests === 0) {
            this.loaderService.loadLoader.next(false);
        }
    }

    /**
     * @description set new auth token by existing refresh token
     */
    private refreshToken() {
        if (this.refreshTokenInProgress) {
            return new Observable(observer => {
                this.subscribedObservable$.add(
                    this.tokenRefreshed$.subscribe(() => {
                        observer.next();
                        observer.complete();
                    })
                );
            });
        } else {
            this.refreshTokenInProgress = true;

            return this.authenticationService.getNewAccessTokenByRefreshToken().pipe(tap(newAuthToken => {
            this.authenticationService.updateAccessToken(newAuthToken.access_token);
            this.refreshTokenInProgress = false;
            this.tokenRefreshedSource.next();
        }));
        }
    }

    private addAuthHeader(request: HttpRequest<any>) {
        const accessToken = this.authenticationService.getAccessTokenOnly();
        return request.clone({
            setHeaders: {
                Authorization: `Bearer ${accessToken}`
            }
        });
    }

    /**
     * @todo move in common service or auth service once tested
     * logout and redirect to login
     */
    private logout() {
        this.authenticationService.removeSavedUserDetailsAndLogout();
    }

0

Tôi đã phải giải quyết các yêu cầu sau:

  • ✅ Làm mới mã thông báo chỉ một lần cho nhiều yêu cầu
  • ✅ Đăng xuất người dùng nếu refreshToken không thành công
  • ✅ Đăng xuất nếu người dùng gặp lỗi sau lần làm mới đầu tiên
  • ✅ Xếp hàng đợi tất cả các yêu cầu trong khi mã thông báo đang được làm mới

Do đó, tôi đã thu thập các tùy chọn khác nhau để làm mới mã thông báo trong Angular:

  • Giải pháp Brute force với tokenRefreshed$BehaviorSubject dưới dạng semaphore
  • Sử dụng caughttham số trong catchErrortoán tử RxJS để thử lại yêu cầu yêu cầu không thành công
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    let retries = 0;
    return this.authService.token$.pipe(
      map(token => req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })),
      concatMap(authReq => next.handle(authReq)),
      // Catch the 401 and handle it by refreshing the token and restarting the chain
      // (where a new subscription to this.auth.token will get the latest token).
      catchError((err, restart) => {
        // If the request is unauthorized, try refreshing the token before restarting.
        if (err.status === 401 && retries === 0) {
          retries++;
    
          return concat(this.authService.refreshToken$, restart);
        }
    
        if (retries > 0) {
          this.authService.logout();
        }
    
        return throwError(err);
      })
    );
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.authService.token$.pipe(
      map(token => req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })),
      concatMap(authReq => next.handle(authReq)),
      retryWhen((errors: Observable<any>) => errors.pipe(
        mergeMap((error, index) => {
          // any other error than 401 with {error: 'invalid_grant'} should be ignored by this retryWhen
          if (error.status !== 401) {
            return throwError(error);
          }
    
          if (index === 0) {
            // first time execute refresh token logic...
            return this.authService.refreshToken$;
          }
    
          this.authService.logout();
          return throwError(error);
        }),
        take(2)
        // first request should refresh token and retry,
        // if there's still an error the second time is the last time and should navigate to login
      )),
    );
}

Tất cả các tùy chọn này đều được kiểm tra kỹ lưỡng và có thể tìm thấy trong repo github angle -refresh-token


-3

Tôi nhận được điều này, tạo một yêu cầu mới dựa trên url của yêu cầu không thành công và gửi cùng một phần nội dung của yêu cầu không thành công.

 retryFailedRequests() {

this.auth.cachedRequests.forEach(request => {

  // get failed request body
  var payload = (request as any).payload;

  if (request.method == "POST") {
    this.service.post(request.url, payload).subscribe(
      then => {
        // request ok
      },
      error => {
        // error
      });

  }
  else if (request.method == "PUT") {

    this.service.put(request.url, payload).subscribe(
      then => {
       // request ok
      },
      error => {
        // error
      });
  }

  else if (request.method == "DELETE")

    this.service.delete(request.url, payload).subscribe(
      then => {
        // request ok
      },
      error => {
        // error
      });
});

this.auth.clearFailedRequests();        

}


-4

Trong verify.service.ts của bạn, bạn nên có một HttpClient được đưa vào làm phụ thuộc

constructor(private http: HttpClient) { }

Sau đó, bạn có thể gửi lại yêu cầu (bên trong retryFailedRequests) như sau:

this.http.request(request).subscribe((response) => {
    // You need to subscribe to observer in order to "retry" your request
});

Đây là suy nghĩ ban đầu của tôi, nhưng http.request trả về HttpEvent.
Antoniossss
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.