토큰 새로 고침 후 Angular 4 인터셉터 재시도 요청


84

안녕하세요 저는 새로운 각도 인터셉터를 구현 401 unauthorized하고 토큰을 새로 고치고 요청을 다시 시도하여 오류를 처리하는 방법을 알아 내려고합니다 . 이것은 내가 따라온 가이드입니다 : https://ryanchenkie.com/angular-authentication-using-the-http-client-and-http-interceptors

실패한 요청을 성공적으로 캐싱하고 토큰을 새로 고칠 수 있지만 이전에 실패한 요청을 다시 보내는 방법을 알 수 없습니다. 또한 현재 사용중인 리졸버와 함께 작동하도록하고 싶습니다.

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();
                    }
                } );

            }
        }
    } );

authentication.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
    } );
}

위의 retryFailedRequests () 파일은 내가 알아낼 수없는 것입니다. 요청을 재전송하고 재시도 후 확인자를 통해 경로에서 사용 가능하게하려면 어떻게해야합니까?

이것이 도움이된다면 모든 관련 코드입니다 : https://gist.github.com/joshharms/00d8159900897dc5bed45757e30405f9


3
저도 같은 문제가 있는데 답이없는 것 같습니다.
LastTribunal

답변:


98

내 마지막 해결책. 병렬 요청과 함께 작동합니다.

업데이트 : Angular 9 / RxJS 6으로 업데이트 된 코드, 오류 처리 및 refreshToken 실패시 루핑 수정

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, importsAuthService의 코드 와 함께 답변을 업데이트 해 주 시겠습니까?
takeshin

4
어떤 이유로 this.authService.refreshToken ()이 실패하면 새로 고침을 기다리는 모든 병렬 쿼리가 영원히 대기한다는 느낌이 있습니다.
Maksim Gumerov

2
새로 고침 토큰에 대한 캐치가 나를 부르지 않습니다. Observable .throw를 쳤습니다.
jamesmpw

2
여러분, 병렬 및 순차적 요청으로 작동합니다. 5 개의 요청을 보내고 401을 반환 한 다음 1 개의 refreshToken이 수행되고 5 개의 요청이 다시 수행됩니다. 5 개의 요청이 순차적 인 경우 처음 401 개 이후 refreshToken을 보낸 다음 첫 번째 요청을 다시 보내고 다른 4 개의 요청을 보냅니다.
Andrei Ostrovski 19

2
Angular가 당신을 위해 그것을 꾸미면 서비스를 수동으로 주입하는 이유는 무엇 @Injectable()입니까? 또한 하나의 catchError는 아무것도 반환하지 않습니다. 최소한 return EMPTY.
Győri Sándor

16

최신 버전의 Angular (7.0.0) 및 rxjs (6.3.3)를 사용하여 동시 요청이 401과 함께 실패 할 경우 토큰 새로 고침 API 만 누르도록 보장하는 완전한 기능의 자동 세션 복구 인터셉터를 만들었습니다. 한 번 실패한 요청을 switchMap 및 Subject를 사용하여 응답으로 파이프합니다. 아래는 내 인터셉터 코드의 모습입니다. 내 인증 서비스 및 스토어 서비스에 대한 코드는 꽤 표준 서비스 클래스이므로 생략했습니다.

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;
  }
}

@ anton-toshik 코멘트에 따르면이 코드의 기능을 글로 설명하는 것이 좋은 생각이라고 생각했습니다. 이 코드에 대한 설명과 이해를 위해 여기 내 기사를 읽을 수 있습니다 (어떻게 작동 하는가?). 도움이 되었기를 바랍니다.


1
return하셨습니다. intercept함수 내부 의 두 번째 는 다음과 같이 보일 것 return next.handle(this.updateHeader(req)).pipe(입니다.. 현재는 인증 토큰을 새로 고침 한 후에 만 ​​전송합니다 ...
malimo

나는 switchmap을 통해 그것을하고 있다고 생각합니다. 다시 확인 해주세요. 내가 당신의 요지를 오해하면 알려주세요.
Samarpan

네, 그것은 기본적으로 작동하지만 당신은 항상 두 번 요청을 보낼 - 한 번 헤더없이 다음은 헤더와 함께 실패 후 ...
malimo

@SamarpanBhattacharya 이것은 작동합니다. 나는이 대답이 Observable의 작동 방식을 이해하지 못하는 나 같은 사람에 대한 의미론으로 설명 할 수 있다고 생각합니다.
Anton Toshik

1
@NikaKurashvili,이 방법의 정의는 나를 위해 일한 :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

비슷한 문제가 발생했으며 수집 / 재시도 논리가 지나치게 복잡하다고 생각합니다. 대신 catch 연산자를 사용하여 401을 확인한 다음 토큰 새로 고침을 확인하고 요청을 다시 실행할 수 있습니다.

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
사용자 지정 상태 코드 498을 사용하여 만료 된 토큰을 식별하고 401은 충분하지 않은 priv를 나타낼 수도 있습니다
Joseph Carroll 17

1
안녕하세요, return next.handle (reqClode)를 사용하려고하는데 아무것도하지 않습니다. 내 코드는 abit와 다르지만 작동하지 않는 부분은 return 부분입니다. authService.createToken (authToken, refreshToken); this.inflightAuthRequest = null; return next.handle (req.clone ({headers : req.headers.set (appGlobals.AUTH_TOKEN_KEY, authToken)}));

6
수집 / 재시도 논리는 지나치게 복잡하지 않으며 토큰이 만료되는 동안 refreshToken 엔드 포인트에 여러 요청을하지 않으려는 경우 수행해야하는 방법입니다. 토큰이 만료되었다고 가정하고 거의 동시에 5 개의 요청을합니다. 이 설명의 논리에 따라 5 개의 새 새로 고침 토큰이 서버 측에서 생성됩니다.
Marius Lazar

4
@JosephCarroll은 일반적으로 충분한 권한이없는 403
andrea.spot입니다.

8

Andrei Ostrovski의 최종 솔루션은 정말 잘 작동하지만 새로 고침 토큰도 만료되면 작동하지 않습니다 (새로 고침을 위해 api 호출을한다고 가정). 약간의 파고를 한 후, 새로 고침 토큰 API 호출도 인터셉터에 의해 차단된다는 것을 깨달았습니다. 이를 처리하기 위해 if 문을 추가해야했습니다.

 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 );
   });
 }

refreshTokenHasFailed멤버 부울로 플레이하는 다른 곳을 보여줄 수 있습니까?
Stephane

1
위의 Andrei Ostrovski의 솔루션에서 찾을 수 있습니다. 기본적으로 사용했지만 새로 고침 끝 점이 차단 될 때 처리 할 if 문을 추가했습니다.
James Lieu

말이 안되는데 왜 새로 고침이 401을 반환합니까? 요점은 인증이 실패한 후 새로 고침을 호출하므로 새로 고침 API가 전혀 인증되지 않아야하며 401을 반환해서는 안됩니다.
MDave

새로 고침 토큰에는 만료 날짜가있을 수 있습니다. 사용 사례에서는 4 시간 후에 만료되도록 설정되어 있습니다. 사용자가 하루가 끝날 때 브라우저를 닫고 다음 날 아침에 반환하면 새로 고침 토큰이 해당 시점까지 만료되어 로그를 요구했습니다. 다시 안으로. 당신의 새로 고침 토큰이 다음 만료되지 않은 경우 물론이 논리를 적용 할 필요가 없습니다 것입니다 것
제임스 반장님

4

이 예를 바탕으로 내 작품입니다.

@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;
    }
}

사용자가 Remember Me재 시도에 새로 고침 토큰을 사용할 수 있는지 확인 하거나 로그 아웃 페이지로 리디렉션 할 수 있습니다.

참고로, LoginService다음 메소드가 있습니다.
-getAccessToken () : 문자열-현재 반환 access_token
-isRememberMe () : boolean-사용자가 있는지 확인-refreshToken refresh_token
() : Observable / Promise-새로운 access_token사용 을 위해 oauth 서버에 요청-invalidateSession refresh_token
() : void-모든 사용자 정보를 제거하고 로그 아웃 페이지로 리디렉션


여러 요청이 여러 새로 고침 요청을 보내는 데 문제가 있습니까?
CodingGorilla

이 버전은 내가 가장 좋아하지만 내 요청이 401을 반환하면 새로 고침을 시도하고 오류를 반환하면 계속해서 요청을 다시 보내려고 시도하고 멈추지 않는 문제가 있습니다. 내가 뭔가 잘못하고 있습니까?
jamesmpw

미안합니다. 전에는 신중하게 테스트하지 않았습니다. 방금 사용중인 테스트 된 게시물로 내 게시물을 편집했습니다 (또한 rxjs6 및 refesh 토큰으로 마이그레이션, URL 확인).
Thanh Nhan

1

이상적으로는 isTokenExpired요청을 보내기 전에 확인하는 것이 좋습니다 . 만료 된 경우 토큰을 새로 고치고 헤더에 새로 고침을 추가하십시오.

그 외에는 retry operator401 응답에서 토큰을 새로 고치는 논리에 도움 이 될 수 있습니다.

RxJS retry operator요청하는 서비스에서를 사용하십시오 . retryCount인수를 받습니다 . 제공되지 않으면 시퀀스를 무기한 재 시도합니다.

응답시 인터셉터에서 토큰을 새로 고치고 오류를 반환합니다. 서비스가 오류를 반환했지만 이제 재시도 연산자가 사용 중이므로 요청을 다시 시도하고 이번에는 새로 고침 된 토큰으로 (인터셉터는 새로 고침 된 토큰을 사용하여 헤더에 추가합니다.)

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

다음 요구 사항을 해결해야했습니다.

  • ✅ 여러 요청에 대해 한 번만 토큰 새로 고침
  • ✅ refreshToken이 실패하면 사용자 로그 아웃
  • ✅ 사용자가 처음 새로 고침 한 후 오류가 발생하면 로그 아웃
  • ✅ 토큰이 새로 고쳐지는 동안 모든 요청 대기열

결과적으로 Angular에서 토큰을 새로 고치기 위해 다른 옵션을 수집했습니다.

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
      )),
    );
}

이 모든 옵션은 철저히 테스트되었으며 angular-refresh-token github repo 에서 찾을 수 있습니다.


-3

실패한 요청의 URL을 기반으로 새 요청을 만들고 실패한 요청의 동일한 본문을 전송했습니다.

 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

authentication.service.ts에서 HttpClient를 종속성으로 주입해야합니다.

constructor(private http: HttpClient) { }

그런 다음 다음과 같이 요청 (retryFailedRequests 내부)을 다시 제출할 수 있습니다.

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

이것은 내 초기 생각 이었지만 http.request가 반환합니다 HttpEvent.
Antoniossss
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.