HttpInterceptor->Service->HttpClient Cyclic Depend

2020-02-10 07:21发布

问题:

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:

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));
  }
}

回答1:

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) 


回答2:

Try setting this.auth with a timeout:

constructor(private injector: Injector) {
  setTimeout(() => {
    this.auth = this.injector.get(AuthService);
  })
}

The bug report you have linked to has since been updated, with an alternative workaround (retrieving AuthService in the intercept function / and not setting it in the constructor): https://github.com/angular/angular/issues/18224#issuecomment-316957213



回答3:

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.



标签: angular