Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optionally apply http interceptors in angular 4

The following url explained how to use http interceptor in Angular 4: https://angular.io/guide/http#intercepting-all-requests-or-responses

However, I'm wondering whether there is any way to choose whether to use the interceptors or not? Or to choose which set of interceptors to use? I want to implement a set of authentication interceptors, however, I don't want these interceptors to be applied when users access things like login, signup and forget-password which do not need user authentication.

All I need is to inject a clean instance of HttpClient into my service, if I need. I don't like the way that I can only use a single global instance of HttpClient that is being polluted by all interceptors.

like image 395
Qian Chen Avatar asked Oct 30 '22 00:10

Qian Chen


1 Answers

I had this very same requirement and came up with the following solution.

In the Module, I 'provide' an HttpClient using a Token as follows.


    export const HTTP_NOAUTH = new InjectionToken("http_noauth");
    ...
    providers: [...,
    {
        provide: HTTP_NOAUTH,
        deps: [HttpBackend],
        useFactory: (handler: HttpBackend) => {
            return new HttpClient(handler);
        }
    },
    {
        provide: HTTP_INTERCEPTORS,
        useClass: AuthHttpInterceptor,
        multi: true
    }],
    ...

Then, when I am injecting the HttpClient and don't want the AuthHttpInterceptor to be utilized, I specify '@Inject(HTTP_NOAUTH)'.


    @Injectable({
        providedIn: 'root',
    })
    export class SomeService {
        constructor(@Inject(HTTP_NOAUTH) private http: HttpClient) {
        ...

The one major hole in this that I've found so far (and there may be more) is that it's an all or nothing solution. It either has all the Interceptors or it has none of them. It may be possible to inject individual Interceptors in the Token provided entry but I haven't dug far enough yet.

UPDATE:

I can now select which interceptors to exclude for each configuration of an HttpClient as follows.

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

export const provideTokenizedHttpClient = (token: InjectionToken<string>, options: { excludes: Function[] } = { excludes: [] }) => {
    return {
        provide: token,
        deps: [HttpBackend, Injector],
        useFactory: (backend: HttpBackend, injector: Injector) => {
            return new HttpClient(
                new HttpDynamicInterceptingHandler(backend, injector, options)
            );
        }
    }
}

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 HttpDynamicInterceptingHandler implements HttpHandler {
    private chain: any = null;

    constructor(private backend: HttpBackend, private injector: Injector, private options: { excludes: Function[] } = { excludes: [] }) { }

    public handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
        if (this.chain === null) {
            const interceptors = this.injector.get(HTTP_INTERCEPTORS, [])
                .filter(entry => !this.options.excludes.includes(entry.constructor));

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

And now in my providers I simply use the following:

providers: [...
    provideTokenizedHttpClient(HTTP_NOAUTH, { excludes: [AuthHttpInterceptor] }),
    {
        provide: HTTP_INTERCEPTORS,
        useClass: AppBusyHttpInterceptor,
        multi: true
    },
    {
        provide: HTTP_INTERCEPTORS,
        useClass: AuthHttpInterceptor,
        multi: true
    }],

The creation of the InjectionToken and its usage on the @Inject decorator is the same.

like image 135
Baffled Monkey Avatar answered Nov 15 '22 13:11

Baffled Monkey