Interceptors only for specific Service

Tom picture Tom · Sep 9, 2017 · Viewed 11.2k times · Source

I have several services in my app that point to different API URLs. Now I need to set different headers to each of these services. My question is now regarding the new interceptors in Angular 4. Is there a possibility to set one interceptor for a specific service? So each service has its specific interceptor?

Hopefully, you guys get my question.

Answer

Nightking picture Nightking · May 31, 2018

TL:DR Answer:

No there is no way. An interceptor is designed to intercept all requests.

Long Answer:

A repository nor a request shouldn't be aware of it's interceptors it could pass. Therefore I don't agree with the solution to mark the request or to check for a specific class.

I rather like more the solution provided here: Example Angular HttpInterceptors

Basically your interceptor has to check a service (mediator pattern) if a specific header should be added.

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    console.log(JSON.stringify(req));

    const token: string = this.currentUserService.token;

    if (token) {
        req = req.clone({ headers: req.headers.set('Authorization', 'Bearer ' + token) });
    }

    if (!req.headers.has('Content-Type')) {
        req = req.clone({ headers: req.headers.set('Content-Type', 'application/json') });
    }

    req = req.clone({ headers: req.headers.set('Accept', 'application/json') });
    return next.handle(req);
}

It's a good example but be aware that it violates the single responsibility principle (setting multiple headers).

Beside that, my opinion is, that an interceptor is the wrong pattern for your problem. Also I don't see an interceptor as solution for adding a bearer token to a request. That was my use case which brought me here.

Basically I would challange your architecture and to rethink how you create the request. A solution to this problem could be following design:

Abstract-Repository

Has basic methods for get / post / put etc. which returns a HttpRequest.

Has a method called "send" which accepts a HttpRequest as a parameter.

Concret-Repository

Inherits from the abstract repository and extends the basic request function.

So for your use case you have one basic service and every specific / custom service inherits from this particular service which extends the behavior of your request.

Decorator

To take this architecture a step further (as I did) you can create a typescript decorator (not possible in all cases, e.g. when dependency injection is needed) which extends the behavior for all decorated functions. For example adding a specific header. This could look like this:

import { Observable } from 'rxjs/Observable';
import { HttpClient, HttpRequest, HttpEvent } from '@angular/common/http';
import { Injectable } from '@angular/core';

export abstract class BaseRepository<T> {
    constructor(protected client: HttpClient) {

    }

    public createGetRequest(url: string): HttpRequest<T> {
        return new HttpRequest("GET", url);
    }

    public send(request: HttpRequest<T>): Observable<HttpEvent<T>> {
        return this.client.request(request);
    }
}

@Injectable()
export class NormalServiceRepository extends BaseRepository<any> {

    constructor(protected client: HttpClient) {
        super(client);
    }

    public get(url: string): Observable<any> {
        const baseRequest = super.createGetRequest(url);

        baseRequest.headers.set('Content-Type', 'application/json');

        return this.send(baseRequest);
    }
}



@Injectable()
export class AuthServiceRepository extends BaseRepository<any> {

    constructor(protected client: HttpClient) {
        super(client);
    }

    @AcceptsJson()
    @SendsJson()
    @ForwardCredentials()
    public createGetRequest(url: string): HttpRequest<any> {
        return super.createGetRequest(url);
    }

    public get(url: string): Observable<any> {
        const baseRequest = super.createGetRequest(url);
        return this.send(baseRequest);
    }
}

That should give you a basic picture how the architecture would look like.

More about decorators

TypeScript Decorators

Example Implementations