Angular 中每个模块的单独 HttpClient 实例
Separate HttpClient instances per module in Angular
在 Angulars HttpInterceptor 参考中,我 found the following section:
To use the same instance of HttpInterceptors
for the entire app, import the HttpClientModule
only in your AppModule
, and add the interceptors to the root application injector . If you import HttpClientModule
multiple times across different modules (for example, in lazy loading modules), each import creates a new copy of the HttpClientModule
, which overwrites the interceptors provided in the root module.
太棒了,这正是我需要的。所以我可以在每个模块中导入 HttpClientModule
,这样我的 ApiInterceptor
就会注入基本路径和 JWT 令牌,不会干扰我在 AppTranslationModule
中使用的 HttpClient
,获取一些翻译文件。
所以我有我的 AppModule
:
@NgModule({
declarations: [AppComponent],
imports: [AppRoutingModule, AppTranslationModule, CoreModule],
bootstrap: [AppComponent]
})
export class AppModule {}
... 导入 AppTranslationModule
和 CoreModule
。他们每个人都导入 HttpClientModule
.
AppTranslationModule
:
@Injectable()
export class TranslationLoader implements TranslocoLoader {
constructor(private http: HttpClient) {}
getTranslation(lang: string) {
return this.http.get<Translation>(`/assets/i18n/${lang}.json`);
}
}
@NgModule({
imports: [TranslocoModule, HttpClientModule],
providers: [
{
provide: TRANSLOCO_CONFIG,
useValue: translocoConfig({
availableLangs: ['en-US', 'de-CH', 'fr-CH', 'it-CH'],
defaultLang: 'en-US',
// Remove this option if your application doesn't support changing language in runtime.
reRenderOnLangChange: true,
prodMode: environment.production
})
},
{ provide: TRANSLOCO_LOADER, useClass: TranslationLoader }
],
exports: []
})
export class AppTranslationModule {}
CoreModule
:
@NgModule({
imports: [BrowserModule, BrowserAnimationsModule, RouterModule, HttpClientModule],
exports: [DefaultLayoutComponent],
declarations: [DefaultLayoutComponent],
providers: [
{ provide: BASE_API_URL, useValue: environment.api },
{ provide: HTTP_INTERCEPTORS, useClass: BaseUrlInterceptor, multi: true }
]
})
export class CoreModule {}
不幸的是,来自 CoreModule
的拦截器 (BaseUrlInterceptor
) 仍然应用于 AppTranslationModule
中的 HttpClient
。如果我正确理解上面提到的文档,这不应该发生吗?知道这里发生了什么吗?
我在 Angular 11.
我在 this blog post 找到了解决方案。正如那里提到的 HttpHandler
需要更改,以创建独立于其他拦截器的 HttpClient
的单独实例:
import { HttpBackend, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { InjectionToken, Injector } from '@angular/core';
import { Observable } from 'rxjs';
export class CustomInterceptHandler implements HttpHandler {
constructor(private next: HttpHandler, private interceptor: HttpInterceptor) {}
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
return this.interceptor.intercept(req, this.next);
}
}
export class CustomInterceptorHandler implements HttpHandler {
private chain: HttpHandler | null = null;
constructor(private backend: HttpBackend, private injector: Injector, private interceptors: InjectionToken<HttpInterceptor[]>) {}
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
if (this.chain === null) {
const interceptors = this.injector.get(this.interceptors, []);
this.chain = interceptors.reduceRight((next, interceptor) => new CustomInterceptHandler(next, interceptor), this.backend);
}
return this.chain.handle(req);
}
}
有了这个 HttpClient
可以扩展:
import { HttpBackend, HttpClient, HttpInterceptor } from '@angular/common/http';
import { Injectable, InjectionToken, Injector } from '@angular/core';
import { CoreModule } from '../core.module';
import { CustomInterceptorHandler } from './custom-http.handler';
export const API_HTTP_INTERCEPTORS = new InjectionToken<HttpInterceptor[]>('API_HTTP_INTERCEPTORS');
@Injectable({ providedIn: CoreModule })
export class ApiHttpService extends HttpClient {
constructor(backend: HttpBackend, injector: Injector) {
super(new CustomInterceptorHandler(backend, injector, API_HTTP_INTERCEPTORS));
}
}
最后,新的 HttpClient
连同拦截器可以注入到依赖树中:
@NgModule({
imports: [BrowserModule, BrowserAnimationsModule, RouterModule, HttpClientModule],
exports: [DefaultLayoutComponent],
declarations: [DefaultLayoutComponent],
providers: [
ApiHttpService,
{ provide: BASE_API_URL, useValue: environment.api },
{ provide: API_HTTP_INTERCEPTORS, useClass: BaseUrlInterceptor, multi: true },
{ provide: API_HTTP_INTERCEPTORS, useClass: ResponseTransformerInterceptor, multi: true }
]
})
export class CoreModule {}
在 Angulars HttpInterceptor 参考中,我 found the following section:
To use the same instance of
HttpInterceptors
for the entire app, import theHttpClientModule
only in yourAppModule
, and add the interceptors to the root application injector . If you importHttpClientModule
multiple times across different modules (for example, in lazy loading modules), each import creates a new copy of theHttpClientModule
, which overwrites the interceptors provided in the root module.
太棒了,这正是我需要的。所以我可以在每个模块中导入 HttpClientModule
,这样我的 ApiInterceptor
就会注入基本路径和 JWT 令牌,不会干扰我在 AppTranslationModule
中使用的 HttpClient
,获取一些翻译文件。
所以我有我的 AppModule
:
@NgModule({
declarations: [AppComponent],
imports: [AppRoutingModule, AppTranslationModule, CoreModule],
bootstrap: [AppComponent]
})
export class AppModule {}
... 导入 AppTranslationModule
和 CoreModule
。他们每个人都导入 HttpClientModule
.
AppTranslationModule
:
@Injectable()
export class TranslationLoader implements TranslocoLoader {
constructor(private http: HttpClient) {}
getTranslation(lang: string) {
return this.http.get<Translation>(`/assets/i18n/${lang}.json`);
}
}
@NgModule({
imports: [TranslocoModule, HttpClientModule],
providers: [
{
provide: TRANSLOCO_CONFIG,
useValue: translocoConfig({
availableLangs: ['en-US', 'de-CH', 'fr-CH', 'it-CH'],
defaultLang: 'en-US',
// Remove this option if your application doesn't support changing language in runtime.
reRenderOnLangChange: true,
prodMode: environment.production
})
},
{ provide: TRANSLOCO_LOADER, useClass: TranslationLoader }
],
exports: []
})
export class AppTranslationModule {}
CoreModule
:
@NgModule({
imports: [BrowserModule, BrowserAnimationsModule, RouterModule, HttpClientModule],
exports: [DefaultLayoutComponent],
declarations: [DefaultLayoutComponent],
providers: [
{ provide: BASE_API_URL, useValue: environment.api },
{ provide: HTTP_INTERCEPTORS, useClass: BaseUrlInterceptor, multi: true }
]
})
export class CoreModule {}
不幸的是,来自 CoreModule
的拦截器 (BaseUrlInterceptor
) 仍然应用于 AppTranslationModule
中的 HttpClient
。如果我正确理解上面提到的文档,这不应该发生吗?知道这里发生了什么吗?
我在 Angular 11.
我在 this blog post 找到了解决方案。正如那里提到的 HttpHandler
需要更改,以创建独立于其他拦截器的 HttpClient
的单独实例:
import { HttpBackend, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { InjectionToken, Injector } from '@angular/core';
import { Observable } from 'rxjs';
export class CustomInterceptHandler implements HttpHandler {
constructor(private next: HttpHandler, private interceptor: HttpInterceptor) {}
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
return this.interceptor.intercept(req, this.next);
}
}
export class CustomInterceptorHandler implements HttpHandler {
private chain: HttpHandler | null = null;
constructor(private backend: HttpBackend, private injector: Injector, private interceptors: InjectionToken<HttpInterceptor[]>) {}
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
if (this.chain === null) {
const interceptors = this.injector.get(this.interceptors, []);
this.chain = interceptors.reduceRight((next, interceptor) => new CustomInterceptHandler(next, interceptor), this.backend);
}
return this.chain.handle(req);
}
}
有了这个 HttpClient
可以扩展:
import { HttpBackend, HttpClient, HttpInterceptor } from '@angular/common/http';
import { Injectable, InjectionToken, Injector } from '@angular/core';
import { CoreModule } from '../core.module';
import { CustomInterceptorHandler } from './custom-http.handler';
export const API_HTTP_INTERCEPTORS = new InjectionToken<HttpInterceptor[]>('API_HTTP_INTERCEPTORS');
@Injectable({ providedIn: CoreModule })
export class ApiHttpService extends HttpClient {
constructor(backend: HttpBackend, injector: Injector) {
super(new CustomInterceptorHandler(backend, injector, API_HTTP_INTERCEPTORS));
}
}
最后,新的 HttpClient
连同拦截器可以注入到依赖树中:
@NgModule({
imports: [BrowserModule, BrowserAnimationsModule, RouterModule, HttpClientModule],
exports: [DefaultLayoutComponent],
declarations: [DefaultLayoutComponent],
providers: [
ApiHttpService,
{ provide: BASE_API_URL, useValue: environment.api },
{ provide: API_HTTP_INTERCEPTORS, useClass: BaseUrlInterceptor, multi: true },
{ provide: API_HTTP_INTERCEPTORS, useClass: ResponseTransformerInterceptor, multi: true }
]
})
export class CoreModule {}