返回

HttpInterceptor 循环引用错误:记一场胜利

前端

使用 HttpInterceptor 的常见场景是实现基于 token 的验证机制。因为,在基于 token 的验证机制中,证明用户身份的 token 需要被附带在每一…

在 Angular 应用中,你会发现 HttpInterceptor 可以从一个请求的发起,一路追溯到它的终结。如果对 HttpInterceptor 的使用稍有不慎,我们就会遇到循环引用。

以这个犯错的代码为例:

// userService.ts
@Injectable({
  providedIn: 'root',
})
export class UserService {
  constructor(private http: HttpClient) {}

  getUserInfo() {
    return this.http.get('/user/info');
  }
}


// http.interceptor.ts
@Injectable()
export class HttpInterceptor implements HttpInterceptor {
  constructor(private userService: UserService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = this.userService.getUserInfo();
    const newReq = req.clone({
      headers: req.headers.set('Authorization', `Bearer ${token}`),
    });
    return next.handle(newReq);
  }
}

我们看看这个流程:

  1. HttpInterceptor 被调用时,它尝试获取 userService
  2. UserService 尝试使用 HttpClient 发送一个请求来获取用户信息。
  3. HttpClient 拦截了这个请求,并调用了 HttpInterceptor

在这个过程中,HttpInterceptorUserService 相互依赖,导致了循环引用。

如何避免循环引用?

解决这一错误的关键在于,分离 HttpInterceptorUserService 的依赖关系。方法很简单:

  1. 把 HttpInterceptor 从 UserService 中移出,改成在根模块中直接提供。
  2. HttpInterceptor 使用 Injector 来获取 UserService,这样就可以避免循环引用。

HttpInterceptor 改写如下:

@Injectable()
export class HttpInterceptor implements HttpInterceptor {
  constructor(private injector: Injector) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const userService = this.injector.get(UserService);
    const token = userService.getUserInfo();
    const newReq = req.clone({
      headers: req.headers.set('Authorization', `Bearer ${token}`),
    });
    return next.handle(newReq);
  }
}

在对 HttpInterceptor 进行改写后,我们就可以避免出现循环引用的错误了。

从长远来看,我们可以通过遵循一些最佳实践来避免循环引用。例如:

  1. 在 Angular 应用中,不要在组件、服务和其他注入对象之间创建循环依赖。
  2. 尽量减少组件、服务和其他注入对象之间的依赖关系。
  3. 使用依赖注入来管理依赖关系,并使用类型提示来确保依赖关系是显式的。

总的来说,通过理解循环引用的原因和解决方法,我们可以避免在 Angular 应用中遇到这种错误。希望本文对您有所帮助。