Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 5 Http Interceptors error when injecting service

Tags:

I am receiving the following strange dependency injection behavior when using custom HttpInterceptors in angular 5+.

The following simplified code works fine:

    export class AuthInterceptor implements HttpInterceptor {         constructor(private auth: AuthService) {}          intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {             const token = this.auth.getToken();             return next.handle(req);         }     }     export class AuthService {         token: string;         constructor() {           console.log('AuthService.constructor');         }     } 

HOWEVER....

When the AuthService has 1 or more dependencies on its own e.g.

   export class AuthService {       token: string;       constructor(private api: APIService) {          console.log('AuthService.constructor');       }    } 

angular is trying to repeatedly create new instances of AuthService until I receive the following errors:

The log is displaying the AuthService.constructor message ~400 times

and

Cannot instantiate cyclic dependency! HTTP_INTERCEPTORS ("[ERROR ->]"): in NgModule AppModule

and

app.component.html:44 ERROR RangeError: Maximum call stack size exceeded

I then tried injecting the service using the Injector class -

 export class AuthService {       token: string;       api: APIService;       constructor(private injector: Injector) {          this.api = this.injector.get(APIService);          console.log('AuthService.constructor');       }    } 

but getting the same error (maximum call stack size).

The APIService is a simple service that only injects the HttpClient in its constructor.

@Injectable() export class APIService {     constructor(private http: HttpClient) {} } 

Lastly, when I inject the AuthService into the Interceptor using the Injector, the error disappears but the AuthService is being instantiated 200+ times:

export class AuthInterceptor implements HttpInterceptor {     auth: AuthService;     constructor(private injector: Injector) {}     intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {            this.auth = this.auth || this.injector.get(AuthService);            const token = this.auth.getToken();            return next.handle(req);         }     } 

Looking at the official documentation and other example it seems as it is technically possible to inject services into the Http Interceptors. Is there any limitation or any other setup that might be missing?

like image 669
dev7 Avatar asked Jan 17 '18 05:01

dev7


People also ask

What is error interceptor in Angular?

An error interceptor is a special kind of interceptor used for handling errors that happen when making HTTP requests. Errors come either from the client-side (browser) or the server-side when the request fails for some reason.

Can I have multiple HTTP interceptors in Angular?

So how can I add multiple interceptors? Http doesn't allow to have more than one custom implementation. But as @estus mentioned the Angular team has added a new HttpClient service recently (release 4.3) which supports multiple interceptors concept. You don't need to extend the HttpClient as you do with the old Http .


2 Answers

Update on end of January 2018

Angular Team resolved this issue in Angular 5.2.3 released 31 January 2018. After updating angular version you will be able to inject services that use HTTPClient as normal in constructor

Bug Fixes

common: allow HttpInterceptors to inject HttpClient (#19809) (ed2b717), closes #18224

from Angular changelog

like image 115
Gwidon Avatar answered Oct 14 '22 14:10

Gwidon


So it turns out that if the service you inject into the Http Interceptor has a dependency on HttpClient, this leads to a cyclic dependency.

Since my AuthService was a mix of all different logics (login/out, routing the user, saving/loading tokens, making api calls), I separated the part needed for the interceptors into its own service (just the user credentials & tokens) and now injecting it successfully into the Interceptor.

export class AuthInterceptor implements HttpInterceptor {     constructor(private credentials: CredentialsService) {}     intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {         const token = this.credentials.getToken();         const api_key = this.credentials.getApiKey();     } }  export class CredentialsService {     token: string;     user: IUser;     constructor(private http: HttpClient) {         this.loadCredentialsFromStorage();     } } 

This seems to work fine. Hope this helps someone.

like image 45
dev7 Avatar answered Oct 14 '22 14:10

dev7