web-dev-qa-db-fra.com

Erreur intercepteurs angulaires 5 Http lors de l’injection de service

Je reçois le comportement suivant d'injection de dépendance étrange lorsque j'utilise des HttpInterceptors personnalisés dans Angular 5+.

Le code simplifié suivant fonctionne bien:

    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');
        }
    }

TOUTEFOIS....

Lorsque la AuthService a une ou plusieurs dépendances à elle seule, par ex.

   export class AuthService {
      token: string;
      constructor(private api: APIService) {
         console.log('AuthService.constructor');
      }
   }

angular tente de créer à plusieurs reprises de nouvelles instances de AuthService jusqu'à ce que je reçoive les erreurs suivantes:

Le journal affiche le message AuthService.constructor environ 400 fois.

et

Cannot instantiate cyclic dependency! HTTP_INTERCEPTORS ("[ERROR ->]"): in NgModule AppModule

et 

app.component.html: 44 ERREUR RangeError: Taille maximale de la pile d'appels dépassé

J'ai ensuite essayé d'injecter le service en utilisant la classe Injector - 

 export class AuthService {
      token: string;
      api: APIService;
      constructor(private injector: Injector) {
         this.api = this.injector.get(APIService);
         console.log('AuthService.constructor');
      }
   }

mais obtenant la même erreur (taille maximale de la pile d'appels).

APIService est un service simple qui n'injecte que HttpClient dans son constructeur.

@Injectable()
export class APIService {
    constructor(private http: HttpClient) {}
}

Enfin, lorsque j'injecte AuthService dans l'Interceptor à l'aide de Injector, l'erreur disparaît mais le service AuthService est instancié plus de 200 fois:

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);
        }
    }

En regardant la documentation officielle et d'autres exemples, il semble qu'il est techniquement possible d'injecter des services dans les intercepteurs Http. Existe-t-il une limitation ou une autre configuration manquante?

6
Yani

Donc, il s'avère que si le service que vous injectez dans Http Interceptor a une dépendance sur HttpClient, cela conduit à une dépendance cyclique.

Puisque ma AuthService était un mélange de différentes logiques (connexion/déconnexion, routage de l'utilisateur, sauvegarde/chargement de jetons, appels api), j'ai séparé la partie nécessaire aux intercepteurs dans son propre service (informations d'identification et jetons uniquement) et maintenant l'injecter avec succès dans l'intercepteur.

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();
    }
}

Cela semble bien fonctionner. J'espère que ça aide quelqu'un.

5
Yani

Mise à jour fin janvier 2018

Angular Team a résolu ce problème dans Angular 5.2.3 publié le 31 janvier 2018. Après la mise à jour de la version angulaire, vous pourrez injecter des services utilisant HTTPClient comme d'habitude dans le constructeur.

Corrections de bugs

common: permet à HttpInterceptors d'injecter HttpClient (# 19809) (ed2b717), ferme # 18224

de Angular changelog

6
Gwidon

vous devez ajouter Injector dans le constructeur et injecter AuthService via un injecteur

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);
            }
        }

n'oubliez pas l'importation

import {Injector} from '@angular/core';
3
Shailesh Ladumor

J'ai eu un problème similaire avec la même conception d'un service d'authentification couplé à un intercepteur.

@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. 
   }

}

Dans mon cas, j'ai trouvé la cause, c'est que j'essaie de démarrer une requête http dans le constructeur du service d'authentification. À ce stade, l'injecteur semble ne pas avoir terminé l'enregistrement de l'instance du service d'autorisation, tandis que le client http capture la nouvelle demande et tente à nouveau d'instantanéiser l'intercepteur, car l'instance précédente de l'intercepteur était également bloquée dans son constructeur sur la pile d'appels! 

Cette invocation récursive avec deux constructeurs, casse le motif singleton de l'injecteur et conduit à une pile d'absence d'appel.

1
Edward Liang