Angular 5 注入服务时出现 Http 拦截器错误

Angular 5 Http Interceptors error when injecting service

在 angular 5+ 中使用自定义 HttpInterceptors 时,我收到以下奇怪的依赖项注入行为。

以下简化代码工作正常:

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

然而....

AuthService 自身有 1 个或多个依赖项时,例如

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

angular 正在尝试重复创建 AuthService 的新实例,直到我收到以下错误:

日志显示 AuthService.constructor 消息 ~400 次

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

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

然后我尝试使用注入器注入服务 class -

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

但出现相同的错误(最大调用堆栈大小)。

APIService 是一个简单的服务,只在其构造函数中注入 HttpClient

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

最后,当我使用 InjectorAuthService 注入拦截器时,错误消失了,但 AuthService 被实例化了 200 多次:

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

查看官方文档和其他示例,似乎在技术上可以将服务注入 Http 拦截器。是否有任何限制或可能缺少的任何其他设置?

您需要将 Injector 添加到构造函数中并通过注入器

注入 AuthService
export class AuthInterceptor implements HttpInterceptor {
            constructor(private inj: Injector) {}

            intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
                const auth = this.inj.get(AuthService);
                const token = this.auth.getToken();
                return next.handle(req);
            }
        }

别忘了导入

import {Injector} from '@angular/core';

所以事实证明,如果你注入Http Interceptor的服务依赖于HttpClient,这会导致循环依赖。

由于我的 AuthService 混合了所有不同的逻辑(login/out、路由用户、saving/loading 令牌、进行 api 调用),我将这部分分开拦截器需要进入其自己的服务(仅用户凭据和令牌),现在将其成功注入拦截器。

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

这似乎工作正常。希望这对某人有所帮助。

2018 年 1 月末更新

Angular 团队在 2018 年 1 月 31 日发布的 Angular 5.2.3 中解决了这个问题。更新 angular 版本后,您将能够在构造函数中正常注入使用 HTTPClient 的服务

Bug Fixes

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

from Angular changelog

我在与拦截器相结合的身份验证服务的相同设计中遇到了类似的问题。

@Injectable() AuthInterceptorService {
    constructor (authApi: AuthApiService) {}
    handle () {...do the job}
}

@Injectable() AuthApiService {
   constructor () {
       // ...do some setup with a request, such as to get current session
       // that leads to an indirect circular between 2 constructors. 
   }

}

就我而言,我发现原因是我尝试在 auth 服务的构造函数中启动 http 请求。此时注入器似乎还没有完成 auth 服务实例的注册,而 http 客户端捕获新请求并尝试再次实例化拦截器,因为之前的拦截器实例也卡在调用堆栈的构造函数中!

使用两个构造函数进行递归调用,打破了注入器的单例模式并导致调用堆栈溢出。

对于这个问题,请确保您在 Http 拦截器中注入的服务需要与模块中的 HTTP_INTERCEPTORS 一起添加到提供程序中。

providers: [ AuthService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: HttpConfigInterceptor,
      multi: true
    }
]

如果以防万一,服务class在注入器class的同一个文件中被声明,那么服务class应该首先被声明和定义。随后是注入器 class,其中服务 class 作为依赖注入。

以上结构确实解决了我的问题。

您需要添加auth服务作为拦截器的依赖。

providers: [ 
{
  provide: HTTP_INTERCEPTORS,
  useClass: HttpConfigInterceptor,
  multi: true,
  deps: [AuthService]
}

对我来说,@Benjineer answar 帮助我在 app.module.ts 中提供了所需的服务。

虽然很有趣,但按照您在 app.module.ts 中添加为依赖项的顺序,您可以在服务的构造函数中获得它。

例如:在您提供的应用模块中AuthService、ErroMsgService的顺序

deps: [AuthService, ErrorMsgService]

在 HttpInterceptor 构造函数中,您必须以相同的顺序创建它们

 constructor(
    private authService: AuthService,
    private errorService: ErrorMsgService
    ) {}