HobojoeBr
HobojoeBr

Reputation: 619

Angular 5 Interceptor - Only call second interceptor after failed retry on first interceptor

I am building an angular 5 app where I have 2 interceptors:

I want that the second interceptor only gets called when the error is not 504 or when it is 504 and has already been retried by the first interceptor.

I have created a sample with both interceptors: https://stackblitz.com/edit/angular-interceptors-ckbvsb

Hope to get some assistance!

Thanks

UPDATE:

Thanks everyone for your assistance! I have ended up implementing it like this:

@Injectable()
export class I1 implements HttpInterceptor {
 intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
 console.log('intercepted I1');

 return next.handle(req)
  .retryWhen(error => {
    return error
      .mergeMap((err, i) => {
        if (i > 0) return Observable.throw(err)
        if (err instanceof HttpErrorResponse && err.status === 504) { // timeout
          return Observable.of(err).delay(5000);
        } else {
          return Observable.throw({ error: 'No retry', cause: err })
        }
      })
  });
 }
}

Upvotes: 4

Views: 3377

Answers (3)

Max Koretskyi
Max Koretskyi

Reputation: 105439

Here is a one way how it can be done - apply it to your code:

// an observable producer that produces an error
const producer = (obs) => {
    obs.next(0);
    obs.error(new Error('504'));
};

const i1 = Observable.create(producer).pipe(
    retryWhen((errors) => {
        return errors.pipe(
            scan((a, v, i) => {
                a.unshift(v);
                return a;
            }, []),
            map((v) => {
                if (!v[0].message.includes('504') || v.length > 1) {
                    throw v[0];
                } else {
                    return v[0].message;
                }
            }),
            delay(2000)
        )
    })
);

You can learn more about creating observables here.

Upvotes: 2

Ingo B&#252;rk
Ingo B&#252;rk

Reputation: 20033

Your issue is the order in which you provide the two interceptors. You are providing I1 first and then I2. This means that a request will flow through I1 → I2, but the response will flow through I2 and only then through I1. So just switch the order:

@NgModule({
    imports: [BrowserModule, HttpClientModule],
    declarations: [AppComponent],
    providers: [
        {
            provide: HTTP_INTERCEPTORS,
            useClass: I2,               // <-- I2 first
            multi: true
        },
        {
            provide: HTTP_INTERCEPTORS,
            useClass: I1,               // <-- And only then I1
            multi: true
        }
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

Upvotes: 3

Daniel Netzer
Daniel Netzer

Reputation: 2232

I not sure that's the right way to handle this kind of things, interceptors are used to check requests, not responses, they dont care about the response returned because you pass the request with the .next before you even see the response.

seems more fitting to be done in a service that checks the response and then does another request to the other resource.

keep the retry interceptor in place and check the response you get on the service itself or the component to use a fallback URL.

Upvotes: -2

Related Questions