0

I have a basic HttpInterceptor in which I am using the rxjs retryWhen in order to retry a certain number of times if there are service failures. If the service call has reached the maximum retry amount then I would like to feed this back to the method that originally instigated the service call.

My question is, how can I return control back to the original instigator of the http call? I need to do this in order to centralise control of handling retries in a single place (the interceptor) and I would like to be able to call back to a success/failure method in the calling function.

My problem is that the error is swallowed by the global error handler and nothing gets passed back to my caller.

Example:

this.MyServiceCall()
        .pipe(
          map((result) => {
            console.log('this is called when the service returns success');
          }),
         )
         // If there is an error, then how can I show it?
      })
    }


export class HttpRetryInterceptorService implements HttpInterceptor {
  constructor() { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
        retryWhen(errors => errors
            .pipe(
            concatMap((err:HttpErrorResponse, count) => iif(
            () => (count < 3),
            of(err).pipe(
                map(()=>{
                    console.log(count.toString())
                }),
                delay((2 + Math.random()) ** count * 200)),
                throwError(err)
            ))
        ))
    );
  }
}
David
  • 2,602
  • 1
  • 18
  • 32
BWG
  • 304
  • 3
  • 15

2 Answers2

0

Try using catchError()

this.MyServiceCall()
    .pipe(
        map((result) => {
            console.log('this is called when the service returns success');
        }),
        catchError((error) => {
            // Do something and return either an observable or rethrow the error
            return throwError(error);
        })
    );

https://www.learnrxjs.io/operators/error_handling/catch.html

Damian C
  • 2,111
  • 2
  • 15
  • 17
0

I think you can make use of the catchError operator.
You can still handle the errors in a single place(i.e: your interceptor) and delegate the error to the caller by throwing an error in the observable that your service will subscribe to.

Note: if you use the throwError operator in the interceptor, TS should complain about this:

intercept (req: HttpRequest<any>, next: HttpHandler) {
//~~~~~~ 
    const foo = false;

    return next.handle(req)
    .pipe(
      map(ev => foo ? ev : throwError('err!')),
    )
}

The error is:

Type Observable < never > is not assignable to type 'HttpEvent'.

and the HttpEvent type looks like this:

export type HttpEvent<T> =
    HttpSentEvent | HttpHeaderResponse | HttpResponse<T>| HttpProgressEvent | HttpUserEvent<T>;

So, no error allowed there.. but here is a workaround I found in this SO post.

intercept (req: HttpRequest<any>, next: HttpHandler) {
    const foo = false;

    return next.handle(req)
      .pipe(
        map(e => {
          if (e instanceof HttpResponse && !foo) {
            throw new HttpErrorResponse({
              error: 'err'
            });
          }

          return e;
        })
    )
  }

Now, the delegated error should be caught in the catchError callback from your service.

this.MyServiceCall()
    .pipe(
        map((result) => {
            console.log('this is called when the service returns success');
        }),
        catchError(err => {
            // Do something with this error...
        })
    )

EDIT

Throwing an error from the interceptor can also be achieved with this approach:

intercept (req: HttpRequest<any>, next: HttpHandler) {
    const foo = false;

    return next.handle(req)
      .pipe(
      mergeMap(e => {
        if (!foo) {
          return throwError('err');
        }

        return of(e);
      }),
    )
  }

I missed the fact that throwError returns an observable :D.

Andrei Gătej
  • 11,116
  • 1
  • 14
  • 31
  • Thanks for the detailed answer - but what about the retryWhen - this is the complexity – BWG Nov 19 '19 at 11:24