0

I have two interceptors -- MainInterceptor and OptionalInterceptor.

export const httpInterceptorProviders = [
    { provide: HTTP_INTERCEPTORS, useClass: MainInterceptor, multi: true }, 
    { provide: HTTP_INTERCEPTORS, useClass: OptionalInterceptor, multi: true }
];

and I provide them in app.module:

providers: [
    ...
    httpInterceptorProviders
]

The logic inside OptionalInterceptor should be executed only in specific conditions (depending on the value of a cookie). I have a service ConfigService that reads the cookie and knows the condition. Right now I inject this service in OptionalInterceptor and execute the logic or not depending on service state.

However the cookie is available (or not) at load time, hence it feels it would be cleaner not to load the OptionalInterceptor at all.

I could create NoopInterceptor and do something like:

export const httpInterceptorProviders = [
    { provide: HTTP_INTERCEPTORS, useClass: MainInterceptor, multi: true }, 
    { provide: HTTP_INTERCEPTORS, useFactory: (srv: ConfigService) => srv.optional ? OptionalInterceptor: NoopInterceptor, multi: true, deps: [ConfigService] }
];

but it does not feel any better. Finally I could define httpInterceptorProviders as a factory:

export const httpInterceptorProviders = (srv: ConfigService) => {...}

in this case I do not see how can I inject the service in module:

httpInterceptorProviders(injectedService)

or

{provide: ???, useFactory: httpInterceptorProviders, deps:[ConfigService]}

Well there is also an approach from this answer. It provides different HttpClient instances injected with different interceptors, but for a simple case this solution looks worse than the problem. It strictly gets the job done, but with even less elegance than doing ifs to skip the logic or creating a NoopInterceptor.

(edit) Just to clarify: why I prefer forcing to load interceptors conditionally instead of applying the condition in the interceptor code? I regard applying condition during the loading of the interceptor to be more clear and maintainable. In order to avoid questions like "why our interceptor is not working, is it broken?" and going to the interceptor code for answer.

Normunds Kalnberzins
  • 1,213
  • 10
  • 20
  • 2
    You cannot conditionally apply HttpInterceptors in angular. I would suggest registering your optional Http Interceptor and then putting your conditional logic within the interceptor opposed to hacking your way through conditional injection. – mwilson Feb 05 '21 at 18:38
  • Does this answer your question? [How to add HttpClient Interceptors conditionally in Angular](https://stackoverflow.com/questions/45781379/how-to-add-httpclient-interceptors-conditionally-in-angular) – mwilson Feb 05 '21 at 18:38
  • @mwilson - as I already mentioned, I currently have my conditional code in interceptor. Just wanted to improve if possible. The answer you link to is similar to what I link to. Rather complex and solves a different problem - different interceptors for different services. Does not really help with my situation. IMO – Normunds Kalnberzins Feb 05 '21 at 22:20
  • A good question with [several examples out there on ignoring interceptors](https://stackoverflow.com/questions/46469349/how-to-make-an-angular-module-to-ignore-http-interceptor-added-in-a-core-module). While interesting, most of the solutions cross the line from "elegant" into "over-engineered" IMO. Seeing "if (cookie) { do this }" in an interceptor is way more maintainable than having to trace chains of wacky factories, injections and conditions. – Nathan Beck Feb 05 '21 at 22:52

1 Answers1

0

I ended up by coding two acceptable versions. One works by directly retrieving the cookie (no point on injecting the service for this trivial task):

export const mainHttpInterceptorProviders = [...];
export const httpInterceptorProvidersWithOptional = [
    ...mainHttpInterceptorProviders,
    { provide: HTTP_INTERCEPTORS, useClass: OptionalInterceptor, multi: true }
];
export const getHttpInterceptorProviders = () => {
    return getCookie(cookieOptional) ? httpInterceptorProvidersWithOptional : mainHttpInterceptorProviders;
}

and in module:

providers: [
    ...
    getHttpInterceptorProviders()
]

The other option is by using NoopInterceptor and useFactory. NoopInterceptor explicitly says that interceptor is just a placeholder and does no work. I already mentioned this option in my question and after evaluating the options decided to go with it:

export class NoopInterceptor implements HttpInterceptor {
 intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
   return next.handle(req);
 }
}
export const httpInterceptorProviders = [ 
    ...mainHttpInterceptorProviders,
    { 
      provide: HTTP_INTERCEPTORS, 
      useFactory: (srv: ConfigService) => srv.optional ? new OptionalInterceptor(): new NoopInterceptor(), 
      multi: true, deps: [ConfigService]
    }
];
Normunds Kalnberzins
  • 1,213
  • 10
  • 20