在角度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:最大调用堆栈大小 超过
然后我尝试使用Injector类注入服务 -
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) {}
}
最后,当我使用AuthService
将Injector
注入Interceptor时,错误消失但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拦截器。是否有任何限制或任何其他设置可能会丢失?
答案 0 :(得分:9)
Angular Team在2018年1月31日发布的Angular 5.2.3中解决了这个问题。更新角度版本后,您将能够在构造函数中正常注入使用HTTPClient的服务
错误修复
常见:允许HttpInterceptors注入HttpClient(#19809)(ed2b717),关闭#18224
答案 1 :(得分:5)
事实证明,如果注入Http Interceptor的服务依赖于HttpClient
,则会导致循环依赖。
由于我的AuthService
混合了所有不同的逻辑(登录/退出,路由用户,保存/加载令牌,进行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();
}
}
这似乎工作正常。希望这有助于某人。
答案 2 :(得分:3)
您需要将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';
答案 3 :(得分:2)
对我来说,@ Benjineer answar帮助在app.module.ts中提供了所需的服务。
尽管是相互干扰的,但在app.module.ts中添加为依赖项的顺序是在服务的构造函数中获取的。
例如:在应用模块中,您按照AuthService,ErroMsgService的顺序提供
deps: [AuthService, ErrorMsgService]
在HttpInterceptor构造函数中,您必须以相同的顺序创建它们
constructor(
private authService: AuthService,
private errorService: ErrorMsgService
) {}
答案 4 :(得分:1)
我遇到了与拦截器相同的auth服务设计的类似问题。
@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客户端捕获新请求并尝试再次实例化拦截器,因为前一个拦截器实例被卡在其构造函数中在电话堆栈上也是!
使用两个构造函数进行递归调用,会破坏注入器的单例模式并导致调用堆栈外。
答案 5 :(得分:1)
您需要将auth服务添加为拦截器的依赖项。
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: HttpConfigInterceptor,
multi: true,
deps: [AuthService]
}
答案 6 :(得分:0)
对于此问题,请确保需要将在Http拦截器中注入的服务与模块中的HTTP_INTERCEPTORS一起添加到提供程序中。
Data$unique_vals <- lapply(Data$Y, unique)
Data$count <- lengths(Data$Y)
答案 7 :(得分:0)
如果以防万一,服务类在Injector类的同一文件中声明,则应首先声明和定义服务类。接下来是Injector类,其中服务类作为依赖项注入。
按照上述结构确实可以解决我的问题。