Angular @Inject注入HttpClient时报this.http.get不是函数错误
@Inject 无法注入完整HttpClient模块问题
问题复现代码
基础服务模块(ServicesModule)
@Injectable({ providedIn: 'root', }) export class ApiService { constructor(@Inject(MY_INJECTION_TOKEN) private http: HttpClient) {} test() { return this.http.get('some_api') } } @NgModule({ imports: [CommonModule], providers: [ApiService] }) export class ServicesModule { static forChild( config: { http: Provider }, ): ModuleWithProviders<ServicesModule> { return { ngModule: ServicesModule, providers: { provide: MY_INJECTION_TOKEN, useValue: config.http } } }
上层父模块(ParentModule)
@NgModule({ imports: [ServicesModule], }) export class ParentModule { static forRoot( config: { http: Provider, someOtherConfigs?: T } ): ModuleWithProviders<ParentModule> { return { ngModule: ParentModule, providers: [ { provide: MY_INJECTION_TOKEN, useValue: config.http }, { provide: DIFFERENT_INJECTION_TOKEN, useValue: config.someOtherConfigs }, ] } } }
主应用模块(AppModule)
@NgModule({ declarations: [AppComponent], imports: [ HttpClientModule, ParentModule.forRoot({ http: HttpClientModule }), ] }) export class AppModule {}
报错现象
运行时调用ApiService.test()方法抛出错误:
TypeError: this.http.get is not a function
打印this.http得到输出:class HttpClientModule {}。即使跳过ParentModule直接引入ServicesModule,只要传参写ServicesModule.forChild({ http: HttpClientModule }),问题依然存在。
问题根因
核心错误有两点:
- 混淆了Angular模块类和服务实例的概念:
HttpClientModule是用于注册HttpClient相关依赖的模块类,本身不是可注入的HttpClient服务实例,也不是HttpClient的注入令牌。你在调用静态方法时直接把HttpClientModule作为参数传入,又用useValue配置提供者,Angular会原封不动把这个类本身作为值注入到ApiService里,这个类自然没有get之类的实例方法。 - 提供者配置逻辑错误:你定义参数类型为
Provider,但实际写配置时没有按Angular提供者规则解析传入的Provider对象,直接把传入值硬塞给了useValue,完全没走DI系统的实例解析流程。
之所以没报“缺少HttpClient依赖”的常见错误,是因为你确实在imports数组中引入了HttpClientModule,Angular只会校验模块层面的依赖是否存在,不会校验你自定义注入令牌里塞的值是什么类型。
修复方案
根据业务需求选对应方案即可:
方案1:不需要自定义Http客户端实现,直接用官方HttpClient
这种场景完全没必要自定义注入令牌,删掉冗余的传参逻辑即可:
- 直接在
ApiService中注入官方HttpClient,移除自定义@Inject(MY_INJECTION_TOKEN)装饰器 - 删掉
ServicesModule、ParentModule静态方法中和http传参相关的逻辑 - 在
ServicesModule的imports数组中引入HttpClientModule,保证依赖可用
修改后的核心代码示例:
@Injectable({ providedIn: 'root', }) export class ApiService { // 直接注入官方HttpClient constructor(private http: HttpClient) {} test() { return this.http.get('some_api') } } @NgModule({ imports: [CommonModule, HttpClientModule], providers: [ApiService] }) export class ServicesModule {}
方案2:确实需要支持调用方传入自定义Http客户端实现
这种场景需要修正提供者配置逻辑,不要用useValue硬传值:
- 给自定义注入令牌配置默认值,默认指向官方HttpClient实例
- 修正静态方法中的提供者配置,正确合并传入的Provider对象,不要直接赋值给
useValue - 调用方传参时传入合法的Provider配置,不要直接传模块类
核心修改代码示例:
// 定义注入令牌,默认使用官方HttpClient export const MY_INJECTION_TOKEN = new InjectionToken<HttpClient>('HTTP_CLIENT', { providedIn: 'root', factory: () => inject(HttpClient) }); @NgModule({ imports: [CommonModule], providers: [ApiService] }) export class ServicesModule { static forChild( config: { http: Provider }, ): ModuleWithProviders<ServicesModule> { return { ngModule: ServicesModule, providers: [ // 正确合并传入的提供者配置,不要硬塞给useValue { provide: MY_INJECTION_TOKEN, ...config.http } ] } } // 主模块调用时传合法Provider,不要传HttpClientModule类 @NgModule({ declarations: [AppComponent], imports: [ HttpClientModule, ServicesModule.forChild({ http: { useClass: HttpClient } // 传标准Provider配置 }) ] }) export class AppModule {}
内容的提问来源于stack exchange,提问作者s.Lawrenz
相关产品推荐
相关产品推荐

