Angular 7 - How to retry an http request on certain response status codes?
Asked Answered
C

2

7

I'm trying to catch http request's errors from an Angular interceptor and handle 401 as logout while retrying a 503 and 504 responses n times.

This is my http interceptor:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    return next.handle(req).pipe(
      catchError(error => {
        if (error.status === 401) {
          this.authenticationService.logout();
          this.router.navigate(['login']);
        }

        return throwError(error);
      }),
      retryWhen(errors => errors
        .pipe(
          concatMap((error, count) => {
            if (count < 2 && (error.status == 503 || error.status == 504)) {
              return of(error.status);
            }

            return throwError(error);
          }),
          delay(500)
        )
      )
    );
  }

I'm 100% sure this code worked when I wrote it because I tested it multiple times but now it's giving me:

UnsubscriptionErrorImpl 
{message: "1 errors occurred during unsubscription:↵1) TypeError: Cannot read property 'apply' of null", name: "UnsubscriptionError", errors: Array(1)}

errors: Array(1)
   0: TypeError: Cannot read property 'apply' of null at RetryWhenSubscriber._zoneUnsubscribe (http://localhost:4200/polyfills.js:8506:44) at RetryWhenSubscriber.push../node_modules/rxjs/_esm5/internal/Subscription.js.Subscription.unsubscribe (http://localhost:4200/polyfills.js:3739:30) at RetryWhenSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.unsubscribe (http://localhost:4200/polyfills.js:3524:38) at RetryWhenSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._unsubscribeAndRecycle (http://localhost:4200/polyfills.js:3541:14) at RetryWhenSubscriber.push../node_modules/rxjs/_esm5/internal/operators/retryWhen.js.RetryWhenSubscriber.notifyNext (http://localhost:4200/vendor.js:155632:14) at InnerSubscriber.push../node_modules/rxjs/_esm5/internal/InnerSubscriber.js.InnerSubscriber._next (http://localhost:4200/polyfills.js:2717:21) at InnerSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (http://localhost:4200/polyfills.js:3504:18) at InnerSubscriber.rxjs.Subscriber.next (http://localhost:4200/polyfills.js:8537:29) at Notification.push../node_modules/rxjs/_esm5/internal/Notification.js.Notification.observe (http://localhost:4200/polyfills.js:2769:50) at AsyncAction.push../node_modules/rxjs/_esm5/internal/operators/delay.js.DelaySubscriber.dispatch (http://localhost:4200/vendor.js:153337:40)
length: 1
__proto__: Array(0)
message: "1 errors occurred during unsubscription:↵1) TypeError: Cannot read property 'apply' of null"
name: "UnsubscriptionError"

Last time it was working, I was using angular 6. Now I'm using angular 7. I already tried different rxjs versions to no avail.

How can I fix this and implement a simple retry logic?

EDIT:

I just noticed that if I remove the import 'zone.js/dist/zone-patch-rxjs'; from the poliyfills.ts file then it works but other things stop working.

Centuple answered 18/4, 2019 at 15:16 Comment(1)
I review a similar task in my article: medium.com/@alexanderposhtaruk/…Euridice
A
0

You need to close the stream in case of 401 to avoid further logic.

        if (error.status === 401) {
          this.authenticationService.logout();
          this.router.navigate(['login']);
          return EMPTY; // <- add this
        }
Aneto answered 29/5, 2020 at 19:1 Comment(0)
T
0

Updated Interceptor with Retry Logic like below:

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      retryWhen(errors =>
        errors.pipe(
          concatMap((error, count) => {
            if (error.status === 503 || error.status === 504) {
              if (count < 2) { // Retry a maximum of 2 times
                return of(error).pipe(delay(500)); // Delay before retrying
              }
            }
            // If it's not a retryable error, throw the error
            return throwError(error);
          })
        )
      ),
      catchError(error => {
        if (error.status === 401) {
          this.authenticationService.logout();
          this.router.navigate(['login']);
        }
        return throwError(error); // Rethrow the error to be handled downstream
      })
    );
  }

in this I have put retry logic before then catch error logic and some other minor changes.

You can use above logic and check issue is resolved or not.

Thirteen answered 11/10, 2024 at 20:11 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.