Я хочу использовать 2 перехватчика глобально (httpInterceptorProviders,
jwtInterceptorProviders), но он не работает в моих ленивых модулях.
У меня есть CoreModule и X ленивые модули загрузки. Странная часть заключается в том, что у меня есть автоматически сгенерированный код генератором сваггера (службы http), эти вызовы перехватываются, но когда я использую пользовательский перехватчик служб http, не перехватывает эти запросы.
Index.ts, где я получаю провайдеров
/** Http interceptor providers in outside-in order */
export const httpInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true }
];
export const jwtInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true }
];
CoreModule, я импортирую свои перехватчики в провайдеры
@NgModule({
imports: [
// angular
CommonModule,
HttpClientModule,
// ngrx
StoreModule.forRoot(reducers, { metaReducers }),
StoreRouterConnectingModule.forRoot(),
EffectsModule.forRoot([AuthEffects, GoogleAnalyticsEffects]),
environment.production
? []
: StoreDevtoolsModule.instrument({
name: "Angular NgRx Material Starter"
}),
// 3rd party
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
ApiModule.forRoot(() => {
return new Configuration({
basePath: `${environment.HOST}:${environment.PORT}`,
apiKeys: { Authorization: "" }
});
})
],
declarations: [],
providers: [
LocalStorageService,
AuthGuardService,
AnimationsService,
TitleService,
// retryHttpInterceptorProviders,
{ provide: ErrorHandler, useClass: AppErrorHandler },
httpInterceptorProviders,
jwtInterceptorProviders,
{ provide: RouterStateSerializer, useClass: CustomSerializer },
{
provide: HAMMER_LOADER,
useValue: () => new Promise(() => {})
},
AnalyticsService,
LayoutService,
StateService,
PetsServiceWithUpload
],
exports: [TranslateModule]
})
export class CoreModule {
constructor(
@Optional()
@SkipSelf()
parentModule: CoreModule
) {
if (parentModule) {
throw new Error("CoreModule is already loaded. Import only in AppModule");
}
}
}
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(
http,
`${environment.i18nPrefix}/assets/i18n/`,
".json"
);
}
AppModule
@NgModule({
declarations: [AppComponent, NavigationComponent, ErrorsComponent],
imports: [
BrowserAnimationsModule,
BrowserModule,
SharedModule,
AppRoutingModule,
AuthModule,
ThemeModule.forRoot(),
CoreModule
],
bootstrap: [AppComponent]
})
export class AppModule {}
Перехватчик
@Injectable()
export class JwtInterceptor implements HttpInterceptor {
private isRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
constructor(private injector: Injector,
private localstorage: LocalStorageService,
private authService: AuthService,
private store: Store<AppState>, ) { }
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const tokenInfo = this.localstorage.getItem(AUTH_KEY);
if (tokenInfo) {
request = this.addToken(request, tokenInfo.token);
}
console.log('TCL: JwtInterceptor -> request', request);
return next.handle(request).pipe(catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
return this.handle401Error(request, next);
} else {
return throwError(error);
}
}));
}
private addToken(request: HttpRequest<any>, token: string): HttpRequest<any> {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
const tokenInfo = this.localstorage.getItem(AUTH_KEY);
return this.authService.authRefreshtokenPost({ refreshToken: tokenInfo.refreshToken }).pipe(
switchMap((tokenRes: any) => {
this.isRefreshing = false;
this.refreshTokenSubject.next(tokenRes.token);
this.localstorage.setItem(AUTH_KEY,
{ token: tokenRes.token, refreshToken: tokenRes.refreshToken, isAuthenticated: true });
return next.handle(this.addToken(request, tokenRes.token));
}), catchError((error) => {
console.log('TCL: JwtInterceptor -> privatehandle401Error -> error', error);
this.store.dispatch(new AuthActions.Logout({ refreshToken: tokenInfo.refreshToken }));
return next.handle(request);
}));
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
return next.handle(this.addToken(request, jwt));
}));
}
}
}
Модуль отложенной загрузки
@NgModule({
imports: [
ThemeModule,
PetsRoutingModule,
StoreModule.forFeature("pets", petsReducer),
TranslateModule.forChild({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
},
isolate: true
}),
EffectsModule.forFeature([PetsEffects]),
InfiniteScrollModule,
SharedModule
],
declarations: [
PetsComponent,
CreatePetComponent,
ViewPetComponent,
FormPetComponent,
EditPetComponent,
PetListVirtualComponent
],
providers: []
})
export class PetsModule {}
Если я экспортирую свои 2 перехватчика в pets.Module, он перехватывает запрос, но я хочу импортировать его один раз только в основной модуль. Я извлекаю этот стек https://stackblitz.com/edit/angular-http-interceptor-working-for-lazy-loaded-module?file=src/app/core/token-interceptor.service.ts и гарантирую, что я импортирую только HttpClientModule в свой основной модуль.