Служба Angular 6 после введения в Interceptor не определена
Я не могу найти способ внедрить мой AuthService внутри ErrorHandlerInterceptor. Он возвращает мне либо "неопределенный" объект после внедрения, либо выдает ошибку.
Это мой ErrorHandlerInterceptor:
import { Injectable } from '@angular/core';
import { AuthService } from '@app/auth.service';
import { StorageService } from '@app/storage.service';
@Injectable({
providedIn: 'root'
})
export class ErrorHandlerInterceptor implements HttpInterceptor {
constructor(private authService: AuthService, private storageService: StorageService) {
console.log(this.authService); // undefined
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(catchError(error => this.errorHandler(error)));
}
// Customize the default error handler here if needed
private errorHandler(response: HttpErrorResponse): Observable<HttpEvent<any>>
{
// ... Various code
}
}
И это мой AuthService:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { StorageService } from './storage.service';
import { Router } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private _http: HttpClient,
private storageService: StorageService,
private router: Router) { }
}
Я пытался перечислить услугу в core.module.ts
провайдеры, но ошибки выбрасываются:
ERROR RangeError: Maximum call stack size exceeded
at setCurrentInjector (core.js:1382)
at resolveNgModuleDep (core.js:8333)
at _createClass (core.js:8425)
at _createProviderInstance (core.js:8393)
at resolveNgModuleDep (core.js:8356)
at _createClass (core.js:8425)
at _createProviderInstance (core.js:8393)
at resolveNgModuleDep (core.js:8356)
at _createClass (core.js:8425)
at _createProviderInstance (core.js:8393)
Обратите внимание, что я использую фреймворк ngx-rocket
, создано ngx-rocket-generator
,
Как я могу сделать, чтобы исправить эту проблему? Любые советы?
ОБНОВЛЕНИЕ 1 - CORE.MODULE.TS
Вот файл core.module.ts.
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http';
import { RouteReuseStrategy, RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { ShellComponent } from './shell/shell.component';
import { HeaderComponent } from './shell/header/header.component';
import { RouteReusableStrategy } from './route-reusable-strategy';
import { AuthenticationService } from './authentication/authentication.service';
import { AuthenticationGuard } from './authentication/authentication.guard';
import { I18nService } from './i18n.service';
import { HttpService } from './http/http.service';
import { HttpCacheService } from './http/http-cache.service';
import { ApiPrefixInterceptor } from './http/api-prefix.interceptor';
import { ErrorHandlerInterceptor } from './http/error-handler.interceptor';
import { CacheInterceptor } from './http/cache.interceptor';
import { TokenInterceptor } from './http/token.interceptor';
import { StorageService } from '@app/storage.service';
import { AuthService } from '@app/auth.service';
@NgModule({
imports: [
CommonModule,
HttpClientModule,
TranslateModule,
NgbModule,
RouterModule
],
declarations: [
HeaderComponent,
ShellComponent
],
providers: [
AuthenticationService,
AuthenticationGuard,
I18nService,
HttpCacheService,
ApiPrefixInterceptor,
ErrorHandlerInterceptor,
CacheInterceptor,
TokenInterceptor,
{
provide: HttpClient,
useClass: HttpService
},
{
provide: RouteReuseStrategy,
useClass: RouteReusableStrategy
}
]
})
export class CoreModule {
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
// Import guard
if (parentModule) {
throw new Error(`${parentModule} has already been loaded. Import Core module in the AppModule only.`);
}
}
}
1 ответ
Похоже, вам нужно добавить зависимости для вашего перехватчика. Вapp.module.ts
вместо того, чтобы просто редактировать provide
раздел ErrorHandlerInterceptor
объявить своего провайдера таким образом:
{
provide: HTTP_INTERCEPTORS,
useClass: ErrorHandlerInterceptor,
multi: true ,
deps: [AuthService, StorageService]
},
Будьте внимательны, порядок услуг в deps
должно быть так же, как в конструкторе.
PS. Не уверен в Angular 6, но для Angular 8,9 он отлично работает.
Введенная переменная конструктора недоступна из функции, которую вы передаете в catchError
функция. Вам нужно получить доступ кrouter
прямо в вашем "методе перехвата" вот так:
constructor(private router: Router) {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((errorResponse: HttpErrorResponse) => {
// this.router is defined here
})
);
}
Проблема, кажется, кроется в catchError
. Если вы распечатаете текущий объемthis
в обоих intercept
а также catchError
функции вы получаете MyInterceptor и CatchSubscriber соответственно. this.router
не доступен у CatchSubscriber. Вы по-прежнему можете использовать отдельные функции, добавив частный метод в свой класс перехватчика:
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((errorResponse: HttpErrorResponse) => {
this.handleError(errorResponse);
})
);
}
private handleError(errorResponse: HttpErrorResponse) {
// this.router is defined here
}
Обобщить:
catchError(this.handleError) // does not work because of different scope
catchError(err => this.handleError(err)) // works because of same scope
Наконец-то я решил проблему. В обработчике ошибок зависимости не могут быть введены через конструктор. Чтобы решить это, вам нужно сделать это:
Сначала импортируйте Инжектор из @angular/core
и ваш сервис:
import { Injector } from '@angular/core';
import { AuthService } from '@app/auth.service';
Затем вы должны добавить его в конструктор:
constructor(private modalService: NgbModal, private injector: Injector) { }
И тогда вы должны создать экземпляр своего сервиса и использовать его следующим образом:
const authService = this.injector.get(AuthService);
authService.logout();
Надеюсь, вам помог этот ответ!
У меня возникла именно эта проблема, и я попытался решить проблему с использованием Injector в обработчике ошибок, но у меня это тоже не сработало. Ни мой сервис, ни инжектор не были определены в моем обработчике ошибок. Я много чего перепробовал, но для меня работало использование анонимной функции вместо написания новой. Я понял, что в моей функции перехвата мой сервис был доступен, но что-то в переходе на новую функцию стало причиной его неопределенности. По какой-то причине использование анонимной функции позволило сохранить мой сервис в поле зрения.
Ниже я помещаю свой код, рассматриваемый сервис - GlobalMessagesService. Надеюсь, это поможет кому-то еще через эту головную боль.
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
import { Router } from "@angular/router";
import { environment } from '../../environments/environment';
import { GlobalMessagesService } from '../global-messages/global-messages.service';
@Injectable()
export class CustomHTTPSettings implements HttpInterceptor {
constructor(
private router: Router,
private MessagesService: GlobalMessagesService
) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
request = request.clone({
setHeaders: headers
});
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => { // this has to be inline or for some reason services aren't available
if (environment.production != true) {
console.error(error);
}
switch (error.status) {
case 401:
this.router.navigate(['/login']);
return of(error);
case 500:
this.MessagesService.addGlobalMessage({
message: 'Oops something went wrong. Please try again',
color: 'red'
});
throw error;
}
}) as any // then handle the error
);
}
}
Я нашел решение в github - служба с http-вызовом в конструкторе вводится как undefined в HttpInterceptor
static translateService;
constructor(translateService: TranslateService) {
ErrorInterceptor.translateService = translateService;
}