Żądania ponownej próby Angular 4 Interceptor po odświeżeniu tokenu


85

Cześć, próbuję dowiedzieć się, jak zaimplementować nowe kątowe przechwytywacze i obsłużyć 401 unauthorizedbłędy, odświeżając token i ponawiając żądanie. Oto przewodnik, który śledziłem: https://ryanchenkie.com/angular-authentication-using-the-http-client-and-http-interceptors

Pomyślnie buforuję żądania, które nie powiodły się, i mogę odświeżyć token, ale nie mogę dowiedzieć się, jak ponownie wysłać żądania, które wcześniej zakończyły się niepowodzeniem. Chcę również, aby to działało z programami do rozwiązywania problemów, których obecnie używam.

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

Powyższy plik retryFailedRequests () jest tym, czego nie mogę zrozumieć. Jak ponownie wysłać żądania i udostępnić je dla trasy przez program rozpoznawania nazw po ponownej próbie?

To jest cały odpowiedni kod, jeśli to pomaga: https://gist.github.com/joshharms/00d8159900897dc5bed45757e30405f9


3
Mam ten sam problem i wydaje się, że nie ma odpowiedzi.
LastTribunal

Odpowiedzi:


99

Moje ostateczne rozwiązanie. Działa z równoległymi żądaniami.

AKTUALIZACJA: kod zaktualizowany za pomocą Angular 9 / RxJS 6, obsługa błędów i naprawienie zapętlenia w przypadku niepowodzenia 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, czy możesz zaktualizować odpowiedź za pomocą importsi kodu usługi AuthService?
takeshin

4
Mam wrażenie, że jeśli z jakiegoś powodu this.authService.refreshToken () zawiedzie, wszystkie równoległe zapytania oczekujące na odświeżenie będą czekać wiecznie.
Maksim Gumerov

2
Haczyk na tokenie odświeżania nigdy mnie nie wzywa. Uderzył w Obserwowalny rzut.
jamesmpw

2
Chłopaki, działa z równoległymi i sekwencyjnymi żądaniami. Wysyłasz 5 żądań, zwracają 401, następnie wykonywany jest 1 refreshToken i ponownie 5 żądań. Jeśli 5 żądań jest sekwencyjnych, po pierwszym 401 wysyłamy refreshToken, następnie ponownie pierwsze żądanie i pozostałe 4 żądania.
Andrei Ostrovski

2
Dlaczego ręcznie wprowadzasz usługę, skoro Angular mógłby to zrobić za Ciebie, gdybyś ją ozdobił @Injectable()? Również jeden catchError nic nie zwraca. Przynajmniej wróć EMPTY.
Győri Sándor

16

W najnowszej wersji Angular (7.0.0) i rxjs (6.3.3) w ten sposób stworzyłem w pełni funkcjonalny przechwytywacz automatycznego odzyskiwania sesji, zapewniający, że jeśli jednoczesne żądania zawiodą z 401, to również powinno trafić tylko do interfejsu API odświeżania tokenu raz i potokuj nieudane żądania do odpowiedzi za pomocą switchMap i Subject. Poniżej jest jak wygląda mój kod przechwytujący. Pominąłem kod mojej usługi autoryzacji i usługi przechowywania, ponieważ są to dość standardowe klasy usług.

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

Zgodnie z komentarzem @ anton-toshik, pomyślałem, że dobrym pomysłem jest wyjaśnienie działania tego kodu w artykule. Możesz przeczytać mój artykuł tutaj, aby uzyskać wyjaśnienie i zrozumienie tego kodu (jak i dlaczego działa?). Mam nadzieję, że to pomoże.


1
dobra praca, drugi returnwewnątrz interceptfunkcji powinna wyglądać następująco: return next.handle(this.updateHeader(req)).pipe(. Obecnie wysyłasz token
autoryzacji

Myślę, że robię to za pomocą mapy przełączników. Prosze Sprawdź ponownie. Daj mi znać, jeśli źle zrozumiałem twój punkt widzenia.
Samarpan

tak to w zasadzie działa, ale zawsze wysyłasz żądanie dwa razy - raz bez nagłówka, a potem po nieudanym z nagłówkiem ....
malimo

@SamarpanBhattacharya To działa. Myślę, że ta odpowiedź przydałaby się z wyjaśnieniem semantyki dla kogoś takiego jak ja, który nie rozumie, jak działa Observable.
Anton Toshik

1
@NikaKurashvili, Ta definicja metody zadziałała dla mnie: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

Napotkałem również podobny problem i myślę, że logika zbierania / ponawiania jest zbyt skomplikowana. Zamiast tego możemy po prostu użyć operatora catch, aby sprawdzić 401, a następnie obserwować odświeżenie tokenu i ponownie uruchomić żądanie:

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
Lubię używać niestandardowego kodu statusu 498, aby zidentyfikować wygasły token w porównaniu z 401, który może również wskazywać na niewystarczający priv
Joseph Carroll

1
Cześć, próbuję użyć return next.handle (reqClode) i nic nie robi, mój kod różni się od twojego abit, ale część nie działa to część zwracana. authService.createToken (authToken, refreshToken); this.inflightAuthRequest = null; return next.handle (req.clone ({headers: req.headers.set (appGlobals.AUTH_TOKEN_KEY, authToken)}));

6
Logika zbierania / ponawiania nie jest zbyt skomplikowana, jest to sposób, w jaki musisz to zrobić, jeśli nie chcesz wysyłać wielu żądań do punktu końcowego refreshToken, gdy token wygasł. Powiedz, że Twój token stracił ważność i prawie w tym samym czasie wykonujesz 5 żądań. Zgodnie z logiką w tym komentarzu po stronie serwera zostanie wygenerowanych 5 nowych tokenów odświeżania.
Marius Lazar

4
@JosephCarroll zwykle nie ma wystarczających uprawnień 403
andrea.spot.

9

Ostateczne rozwiązanie Andrei Ostrovski działa naprawdę dobrze, ale nie działa, jeśli token odświeżania również wygasł (zakładając, że wykonujesz wywołanie interfejsu API w celu odświeżenia). Po pewnym czasie zdałem sobie sprawę, że wywołanie API tokena odświeżania zostało również przechwycone przez przechwytywacz. Musiałem dodać instrukcję if, aby sobie z tym poradzić.

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

Czy możesz pokazać, gdzie jeszcze grasz z refreshTokenHasFailedczłonkiem boolean?
Stephane

1
Możesz go znaleźć w powyższym rozwiązaniu Andrei Ostrovski, w zasadzie użyłem tego, ale dodałem instrukcję if, aby obsłużyć, gdy punkt końcowy odświeżania zostanie przechwycony.
James Lieu,

To nie ma sensu, dlaczego odświeżenie zwróciło 401? Chodzi o to, że wywołuje odświeżanie po niepowodzeniu uwierzytelniania, więc twój interfejs API odświeżania nie powinien w ogóle uwierzytelniać się i nie powinien zwracać 401.
MDave

Tokeny odświeżania mogą mieć daty ważności. W naszym przypadku ustawiono wygasanie po 4 godzinach, gdyby użytkownik zamknął przeglądarkę pod koniec dnia i powrócił następnego ranka, token odświeżania wygasłby do tego momentu i dlatego wymagaliśmy od niego zalogowania z powrotem. Jeśli twój token odświeżania nie wygasł, to oczywiście nie musiałbyś stosować tej logiki
James Lieu

4

Na podstawie tego przykładu , oto moja praca

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

Możesz chcieć sprawdzić, czy użytkownik ma włączoną opcję Remember Meodświeżania tokena do ponownej próby, czy po prostu przekierowuje do strony wylogowania.

Fyi, LoginServicema następujące metody:
- getAccessToken (): string - zwraca bieżący access_token
- isRememberMe (): boolean - sprawdź, czy użytkownik ma refresh_token
- refreshToken (): Observable / Promise - Żądanie do serwera oauth w celu nowego access_tokenużycia refresh_token
- invalidateSession (): void - usuń wszystkie informacje o użytkowniku i przekieruj do strony wylogowania


Czy masz problem z wieloma żądaniami wysyłającymi wiele żądań odświeżenia?
CodingGorilla

Tę wersję lubię najbardziej, ale mam problem z wysłaniem żądania, kiedy zwraca 401, próbuje odświeżyć, a kiedy zwraca błąd, ciągle próbuje wysłać żądanie ponownie, nigdy nie zatrzymując się. Czy robię coś złego?
jamesmpw

Przepraszam, ten wcześniej nie testowałem dokładnie. Właśnie zredagowałem mój post z testowanym, którego używam (również migracja do rxjs6 i refesh token, sprawdź url).
Thanh Nhan,

2

Musiałem rozwiązać następujące wymagania:

  • ✅ Odśwież token tylko raz dla wielu żądań
  • ✅ Wyloguj użytkownika, jeśli refreshToken nie powiódł się
  • ✅ Wyloguj się, jeśli po pierwszym odświeżeniu pojawi się błąd
  • ✅ Kolejkuj wszystkie żądania podczas odświeżania tokena

W rezultacie zebrałem różne opcje, aby odświeżyć token w 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
      )),
    );
}

Wszystkie te opcje są gruntownie przetestowane i można je znaleźć w repozytorium github angular -refresh-token


1

Najlepiej sprawdzić isTokenExpiredprzed wysłaniem zapytania. A jeśli wygasł, odśwież token i dodaj odświeżony w nagłówku.

Poza tym retry operatormoże pomóc w logice odświeżania tokenu w odpowiedzi 401.

Użyj RxJS retry operatorw swojej usłudze, w której składasz żądanie. Przyjmuje retryCountargument. Jeśli nie zostanie podany, będzie ponawiać sekwencję w nieskończoność.

W Twoim przechwytywaczu w odpowiedzi odśwież token i zwróć błąd. Gdy usługa zwróci błąd, ale teraz jest używany operator ponawiania, więc ponowi żądanie i tym razem z odświeżonym tokenem (przechwytujący używa odświeżonego tokenu do dodania w nagłówku).

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

czy u pls można dodać klasę authenticationService
Ajt

-3

Otrzymałem to, tworząc nowe żądanie na podstawie adresu URL nieudanego żądania i wysyłając tę ​​samą treść nieudanego żądania.

 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

W pliku authentication.service.ts, jako zależność należy wstrzyknąć HttpClient

constructor(private http: HttpClient) { }

Następnie możesz ponownie przesłać żądanie (inside retryFailedRequests) w następujący sposób:

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

To była moja pierwsza myśl, ale http.request zwraca HttpEvent.
Antoniossss
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.