How to make an angular module to ignore http interceptor added in a core module

AngularAngular Http-Interceptors

Angular Problem Overview


I do have a core module with an HttpInterceptor for authorization handling and I include this module in AppModule, in this way all the other modules that use HttpClient are using this interceptor.

@NgModule({
  imports: [],
  declarations: [],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true,
    },
  ]
})
export class CoreModule { }

How to make a module bypass the default interceptor?

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: components,
  providers: [CustomService],
  exports: components,
})
export class ModuleWithoutInterceptorModule { }

Angular Solutions


Solution 1 - Angular

You can use HttpBackend.

Example:

import { HttpClient, ..., HttpBackend } from '@angular/common/http';

@Injectable()
export class TestService {

  private httpClient: HttpClient;

  constructor( handler: HttpBackend) { 
     this.httpClient = new HttpClient(handler);
  }
....

In this way the service is not intercepted by AuthInterceptor.

Solution 2 - Angular

Per this suggestion on GitHub, we've implemented a simple header to identify requests that shouldn't be intercepted. In the interceptor:

export const InterceptorSkipHeader = 'X-Skip-Interceptor';

@Injectable()
export class SkippableInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (req.headers.has(InterceptorSkipHeader)) {
      const headers = req.headers.delete(InterceptorSkipHeader);
      return next.handle(req.clone({ headers }));
    }

    ...  // intercept
  }

}

Then whenever you want to skip the interception for a particular request:

const headers = new HttpHeaders().set(InterceptorSkipHeader, '');

this.httpClient
    .get<ResponseType>(someUrl, { headers })
    ...

Note that with this method the service, not the interceptor, is choosing when the interceptor's logic gets applied; this means that the services must "know" something about the interceptors in your application. Depending on your use case, it might be better to make the interceptors decide when to apply the logic.

Solution 3 - Angular

In order to bypass all interceptors we can use HttpBackend as @deg suggested.

In other cases we can create HttpClient factory that will allows us to exclude interceptors from the interceptors chain:

import { createHttpClient } from './http-client.factory';
// ...

@Injectable({
  providedIn: 'root'
})
export class TodosApiService {
  http = createHttpClient(this.injector, [Interceptor2]);
//                                        ^^^^^^^^^^^^
//                                    Interceptors to exclude

  constructor(private injector: Injector) { }

  getTodo() {
    // Interceptor2 will be bypassed
    return this.http.get('https://jsonplaceholder.typicode.com/todos')
  }
}

Ng-run Example

Note that you can reuse this logic by creating base class:

@Injectable()
export class BasicHttpClient {
  protected http = createHttpClient(this.injector, [Interceptor2]);

  constructor(private injector: Injector) { }
}

@Injectable({ providedIn: 'root' })
export class TodosApiService extends BaseHttpClient {

  getTodo() {
    // Interceptor2 will be bypassed
    return this.http.get('https://jsonplaceholder.typicode.com/todos')
  }
}

http-client.factory.ts

import {
  HTTP_INTERCEPTORS,
  HttpBackend,
  HttpClient,
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest,
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { Injector, Type } from '@angular/core';

class HttpInterceptorHandler implements HttpHandler {
  constructor(private next: HttpHandler, private interceptor: HttpInterceptor) {}

  handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
    return this.interceptor.intercept(req, this.next);
  } 
}

class HttpInterceptingHandler implements HttpHandler {
  private chain: HttpHandler | null = null;

  constructor(
    private backend: HttpBackend,
    private injector: Injector,
    private interceptorsToExclude: Type<HttpInterceptor>[],
    private intercept?: (req: HttpRequest<any>) => HttpRequest<any>
  ) {}

  handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
    if (this.intercept) {
      req = this.intercept(req);
    }

    if (this.chain === null) {
      const interceptors = this.injector
        .get(HTTP_INTERCEPTORS, [])
        .filter(
          interceptor => !this.interceptorsToExclude.some(interceptorType => interceptor instanceof interceptorType)
        );

      this.chain = interceptors.reduceRight(
        (next, interceptor) => new HttpInterceptorHandler(next, interceptor),
        this.backend
      );
    }
    return this.chain.handle(req);
  }
}

export function createHttpClient(
  injector: Injector,
  excludedInterceptors: Type<HttpInterceptor>[],
  intercept?: (req: HttpRequest<any>) => HttpRequest<any>
) {
  return new HttpClient(
    new HttpInterceptingHandler(injector.get(HttpBackend), injector, excludedInterceptors, intercept)
  );
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlexandru OlaruView Question on Stackoverflow
Solution 1 - AngulardegView Answer on Stackoverflow
Solution 2 - AngularjonrsharpeView Answer on Stackoverflow
Solution 3 - AngularyurzuiView Answer on Stackoverflow