Угловая ошибка 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
    }
]