108

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 { }
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Alexandru Olaru
  • 6,842
  • 6
  • 27
  • 53
  • 1
    I couldn't figure this out either. I think the correct way will be to create middle tier "intercepted" and "not-intercepted" modules between the app and the feature modules, and to provide the interceptor in the "not-intercepted" module instead of the "core" module. If anyone has any other suggestions, or concurs with this approach, please chime in. – hayhorse Sep 30 '17 at 21:26

5 Answers5

283

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.

Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
deg
  • 2,846
  • 2
  • 11
  • 5
  • 1
    From the docs, this seems like a good solution - how future-proof is it? – Rodney May 21 '18 at 23:12
  • 7
    @deg you are my hero right now! I was using APP_INITIALIZER and HTTP_INTERCEPTOR tokens in my code. my Interceptor was using a value that was initialized in the App initializer. And my app initializer was making an http.get, which was in turn hitting the interceptor. you have no idea how happy i am to have come across this answer!... – Nandun Jul 26 '18 at 04:47
  • 2
    Was using an APP_INITIALIZER to fetch my appsettings via an express endpoint, but my adal inteceptor wasn't initialized yet, so it was throwing an error. This fixed my issues after searching for about an hour. Could easily see creating a blog post on this. – Brad Aug 28 '18 at 17:19
  • 11
    @rodney This solution has been upvoted significantly on github with no objection by the core team https://github.com/angular/angular/issues/20203#issuecomment-368680437 – seangwright Aug 30 '18 at 17:35
  • 18
    Using HttpBackend bypasses all interceptors, correct? Is it possible to exclude some interceptors but not others? – whatsTheDiff Sep 24 '18 at 16:39
  • I know this is an old post, but this really helped me out. Thanks @deg. – christok Jan 14 '20 at 17:51
  • 3
    I'm using Angular 9 and still's a valid solution – E-Bat Jun 13 '20 at 02:59
  • Works well to bypass mindless amount of unnecessary interceptor logic that no ones in the company is allowed to touch. – SoEzPz Jun 24 '20 at 19:10
  • this answer saved my back!! Thanks a lot!! – youshea Mar 17 '21 at 07:12
  • On Angular 11 it doesn't fully stop my interceptors... – Ap0st0l Apr 16 '21 at 09:05
  • @deg another dev here you saved, thanks a lot mate – Jervie Vitriolo Apr 20 '21 at 14:36
  • Thank you a ton! – William Jul 15 '21 at 20:54
  • Works with Angular 12 to bypass MSAL Azure. I wish I could buy you a cold one! – Neil J Sep 10 '21 at 22:41
  • This answer made an end to my 2 days long tour of frustration. Thank you VERY much! – MysticEarth Apr 07 '22 at 10:36
112

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.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • 1
    Nice Idea, so using headers actually we can create different kind of interceptors! – Alexandru Olaru Mar 01 '18 at 13:16
  • @AlexandruOlaru yes, I suppose you could have a couple that you toggle on and off as needed. That might get quite complex though, and you have to be careful to make good decisions about whether the *service* or the *interceptor* should be choosing when to apply the transformation. We just have one interceptor for authentication, and turn it off from the one service that needs to get the initial token. – jonrsharpe Mar 01 '18 at 13:36
8

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)
  );
}
sam
  • 125
  • 1
  • 11
yurzui
  • 205,937
  • 32
  • 433
  • 399
3

As of Angular 12, this can be accomplished with the HttpContext property for an HttpRequest!

This approach is nice because it doesn't require any logic for stripping headers, and can be done on a per-request basis.

export const DISABLE_GLOBAL_EXCEPTION_HANDLING = new HttpContextToken<boolean>(() => false);

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if (this.ignoreErrorHandling(request)) {
            return next.handle(request);
        }

        return next.handle(request).pipe(
            catchError((error) => {
                // normal intercepting logic
                return throwError(() => error);
            }),
        );

    private ignoreErrorHandling(request: HttpRequest<any>) {
        return request.context.get(DISABLE_GLOBAL_EXCEPTION_HANDLING);
    }

An individual request can be disabled by adding the token:

this.httpClient.get<ResponseType>(`/api`, {
    context: new HttpContext().set(DISABLE_GLOBAL_EXCEPTION_HANDLING, true),
});
trebor
  • 903
  • 1
  • 12
  • 23
0

If the HttpClient is being used within a dependency outside of your control (such as a 3rd party lib) you can still use the HttpContextTokens that were added in Angular v12 by using a HttpClient factory provider. You can scope this to individual modules or components using the providers array:

disable-interceptor.handler.ts

import { HttpContextToken, HttpHandler, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';

export const DISABLE_INTERCEPTORS = new HttpContextToken<boolean>(() => false);

@Injectable()
export class DisableInterceptorHandler extends HttpHandler {
  constructor(private httpHandler: HttpHandler) {
    super();
  }

  handle(req: HttpRequest<unknown>) {
    return this.httpHandler.handle(
      req.clone({
        context: req.context.set(DISABLE_INTERCEPTORS, true),
      }),
    );
  }
}

Provide the handler in either your component/module/service/whatever providers array

providers: [
  DisableInterceptorHandler,
  {
    provide: HttpClient,
    useFactory: (handler: DisableInterceptorHandler) => new HttpClient(handler),
    deps: [DisableInterceptorHandler],
  },
],

Respect the HttpContextToken in your interceptor!

import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { DISABLE_INTERCEPTORS } from './disable-interceptor.handler';

@Injectable()
export class AuthenticationInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    if (request.context.get(DISABLE_INTERCEPTORS) === true) {
      return next.handle(request);
    }

    return next.handle(request).pipe(
      // add your typical interceptor logic
    );
  }
}
Brad C
  • 2,868
  • 22
  • 33