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?
For this issue, make sure that the service which you are injecting in Http interceptor needs to be added in providers along with the HTTP_INTERCEPTORS in the module.
If in case, service class is declared in the same file of Injector class then service class should be declared and defined first. Followed by the Injector class where the service class injected as a dependency.
Following above structure does resolves the problem in my case.
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.This seems to work fine. Hope this helps someone.
I got a similar issue with the same design of an auth service coupled with an interceptor.
}
In my case, i found the cause is that I try to start a http request in the auth service's constructor. At that point the injector seems haven't completed the registration of the auth service's instance, while the http client captures the new request and tried to instantiaze the interceptor again, since the previous interceptor instance was stuck in its constructor on the call stack too!
That recursive invoke with two constructors, breaks the singleton pattern of the injector and leads to out of call stack.
you need to add
Injector
into constructor and injectAuthService
via injectordon't forget import
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
from Angular changelog