HttpInterceptor->Service->HttpClient Cyclic Dependency

Viewed 6199

So I have my authentication service, AuthService, that basically has two methods, one that gets a new token from the server, given a username and a password, and one that retrieves the current stored token and refreshes the tokens during the process when necessary. Both obviously rely on HttpClient. That is, AuthService has a dependency on HttpClient. Let's keep that in mind.

Another "service" is an HttpInterceptor that I want to intercept all outgoing requests other than those made by AuthService to add the Authorization header (it's getting dirty now). And to make up that header, we need a token, which we get from AuthService. That is, AuthInterceptor (the name of my interceptor) has a dependency on AuthService. And as far as I know, HttpClient has a dependency on all HTTP_INTERCEPTORS.

So the scenario is as follows: Cyclic Dependency

Any ideas or suggestions on how to break that circle? Is there any way to make AuthService's HttpClient independent of AuthInterceptor? Or is it a bad idea? (Another third function will be added to AuthService for logging the user out, whose requests shall be intercepted and have the Authorization header added to them as well)

So far I found a similar issue but the workaround suggested there doesn't solve my problem, now I get infinite recursion during the bootstrapping process before any requests are sent. I've handled the case of login and token refresh requests being intercepted to avoid this so this is not the issue here as far as I know. Here's a plunk with an overview of my code.

Excerpt from the plunk:

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  private auth: AuthService;

  constructor(inj: Injector) {
    this.auth = inj.get(AuthService);
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Ignore if login or refresh request
    if (req.url.includes('login')) {
      return next.handle(req);
    }

    console.log('Intercepting request...');
    return this.auth.getToken().map(token => {
      const authHeader = 'Bearer ' + token;
      const authReq = req.clone({setHeaders: {Authorization: authHeader}});
      return authReq;
    }).concatMap(newReq => next.handle(newReq));
  }
}
4 Answers

Update 08/02/2018 - angular 5.2.3

Just an update on this: this was fixed in angular 5.2.3

https://github.com/angular/angular/blob/master/CHANGELOG.md#bug-fixes-2

So you can directly inject services that depend on HttpClient in HttpInterceptors

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    constructor(private auth: AuthService) 

I was running into the same or a similar issue using Angular 6.1.10. I simply instantiated HttpClient myself inside the service that need to be injected into an HttpInterceptor:

@Injectable()
export class AuthService {

  private http: HttpClient;

  constructor(httpBackend: HttpBackend) {
    this.http = new HttpClient(httpBackend);    
  }
}

That broke the infinite loop issue in my case.

My problem was using the HttpClient in the service constructor that was injected into the HttpInterceptor.

export class SettingsService {
    constructor(
        private http: HttpClient,
    ) {
        this.http.get(...).subscribe() => {
            ...
        });
    }
}

export class AppInterceptor implements HttpInterceptor {
    constructor(
        private settingsS: SettingsService,
    ) { }
    ...
    ...
}
Related