Угловая ошибка 5 Http-перехватчиков при инъекционном обслуживании
При использовании пользовательских HttpInterceptors в угловом 5+ я получаю следующее странное поведение инъекции зависимостей.
Следующий упрощенный код работает отлично:
export class AuthInterceptor implements HttpInterceptor {
constructor(private auth: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = this.auth.getToken();
return next.handle(req);
}
}
export class AuthService {
token: string;
constructor() {
console.log('AuthService.constructor');
}
}
ТЕМ НЕ МЕНИЕ....
Когда AuthService
имеет 1 или более зависимостей, например,
export class AuthService {
token: string;
constructor(private api: APIService) {
console.log('AuthService.constructor');
}
}
angular пытается многократно создавать новые экземпляры AuthService
пока я не получу следующие ошибки:
Журнал отображает сообщение AuthService.constructor
~ 400 раз
а также
Cannot instantiate cyclic dependency! HTTP_INTERCEPTORS ("[ERROR ->]"): in NgModule AppModule
а также
app.component.html: 44 ERROR RangeError: превышен максимальный размер стека вызовов
Затем я попытался использовать услугу с помощью класса Injector -
export class AuthService {
token: string;
api: APIService;
constructor(private injector: Injector) {
this.api = this.injector.get(APIService);
console.log('AuthService.constructor');
}
}
но получая ту же ошибку (максимальный размер стека вызовов).
APIService
- это простой сервис, который только вводит HttpClient
в свой конструктор.
@Injectable()
export class APIService {
constructor(private http: HttpClient) {}
}
Наконец, когда я AuthService
в Interceptor с помощью Injector
, ошибка исчезает, но AuthService создается 200+ раз:
export class AuthInterceptor implements HttpInterceptor {
auth: AuthService;
constructor(private injector: Injector) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.auth = this.auth || this.injector.get(AuthService);
const token = this.auth.getToken();
return next.handle(req);
}
}
Рассматривая официальную документацию и другой пример, кажется, что технически возможно внедрить услуги в Http Interceptors. Есть ли какие-либо ограничения или любые другие настройки, которые могут отсутствовать?
Ответы
Ответ 1
Таким образом, оказывается, что если служба, которую вы вводите в Http Interceptor, зависит от HttpClient
, это приводит к циклической зависимости.
Поскольку мой AuthService
представлял собой сочетание всех различных логик (вход/выход, маршрутизация пользователя, сохранение/загрузка токенов, создание вызовов api), я выделил часть, необходимую для перехватчиков, в свою собственную службу (только учетные данные пользователя и токены) и теперь успешно внедряя его в Interceptor.
export class AuthInterceptor implements HttpInterceptor {
constructor(private credentials: CredentialsService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = this.credentials.getToken();
const api_key = this.credentials.getApiKey();
}
}
export class CredentialsService {
token: string;
user: IUser;
constructor(private http: HttpClient) {
this.loadCredentialsFromStorage();
}
}
Кажется, это работает нормально. Надеюсь, это поможет кому-то.
Ответ 2
Обновление до конца января 2018 года
Угловая команда разрешила эту проблему в Angular 5.2.3, выпущенной 31 января 2018 года. После обновления угловой версии вы сможете внедрять службы, которые используют HTTPClient как обычно в конструкторе
Исправление ошибок
common: разрешить HttpInterceptors вводить HttpClient (# 19809) (ed2b717), закрывает # 18224
из углового журнала изменений
Ответ 3
вам нужно добавить Injector
в конструктор и ввести AuthService
через инжектор
export class AuthInterceptor implements HttpInterceptor {
constructor(private inj: Injector) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const auth = this.inj.get(AuthService);
const token = this.auth.getToken();
return next.handle(req);
}
}
не забывайте импортировать
import {Injector} from '@angular/core';
Ответ 4
Я получил аналогичную проблему с тем же дизайном службы auth в сочетании с перехватчиком.
@Injectable() AuthInterceptorService {
constructor (authApi: AuthApiService) {}
handle () {...do the job}
}
@Injectable() AuthApiService {
constructor () {
// ...do some setup with a request, such as to get current session
// that leads to an indirect circular between 2 constructors.
}
}
В моем случае я обнаружил, что я пытаюсь запустить HTTP-запрос в конструкторе службы auth. В этот момент инжектор, похоже, не завершил регистрацию экземпляра служебной службы, в то время как клиент-клиент захватил новый запрос и попытался снова перехватить перехватчик, поскольку предыдущий экземпляр перехватчика тоже застрял в его конструкторе в стеке вызовов!
Этот рекурсивный вызов с двумя конструкторами разбивает одноэлементный шаблон инжектора и приводит к выходу из стека вызовов.
Ответ 5
Для этой проблемы убедитесь, что служба, которую вы вводите в перехватчик Http, должна быть добавлена в провайдеры вместе с HTTP_INTERCEPTORS в модуле.
providers: [ AuthService,
{
provide: HTTP_INTERCEPTORS,
useClass: HttpConfigInterceptor,
multi: true
}
]