Interceptez vos requêtes

Dans vos applications, vous aurez très souvent besoin d'accomplir certaines tâches pour chaque requête HTTP envoyée par votre application. L'authentification, le logging, la gestion d'erreur – tout ça serait très lourd à implémenter manuellement pour chaque méthode de service.

Heureusement, l'équipe Angular a pensé à ce besoin, et a créé les intercepteurs. Comme leur nom l'indique, les intercepteurs interceptent toutes les requêtes envoyées (ainsi que les réponses reçues) par votre application, et vous permettent d'accomplir les tâches nécessaires.

Authentifiez les requêtes

Avec l'équipe Dwelio, vous avez décidé de permettre aux utilisateurs d'ajouter des biens à leurs favoris, et ils devront être authentifiés pour le faire. Il faudra donc envoyer un header d'autorisation avec la requête qui ajoute ou retire un bien des favoris. Le temps que votre collègue implémente la vraie authentification, ce header contiendra un token codé en dur dans l'application.

Tout le reste de cette nouvelle feature a été préparé : il ne reste plus qu'à implémenter l'intercepteur qui ajoutera ce header d'autorisation à toutes les requêtes.

Si vous essayez d'ajouter un bien à vos favoris pour l'instant, l'étoile ne se remplit pas et vous verrez une erreur 401 dans la console :

Une erreur 401 car la requête n'est pas autorisée
Unauthorized!

Implémentons un intercepteur pour autoriser cette requête !

Un intercepteur est une fonction qui reçoit deux arguments — un objet qui représente la requête etnext, un handler qui passe la main au prochain intercepteur de la chaîne (ou qui envoie la requête finale s'il n'y a plus d'intercepteurs).

Dans  core/auth, créeztoken.interceptor.tset créez-y cette fonction :

export const tokenInterceptor: HttpInterceptorFn = (
  req: HttpRequest<unknown>,
  next: HttpHandlerFn
): Observable<HttpEvent<unknown>> => {
  return next(req);
}

Ici, on ne touche pas du tout à la requête — on la passe ànextet on retourne le tout, passant ainsi la main au prochain handler.

Du coup, comment on fait pour modifier la requête avant de l'envoyer ?

Eh bien on ne la modifie pas ! Les objetsHttpRequestsont immuables : on doit donc clonerreqen y ajoutant notre header.

Déjà, pour récupérer le token à y insérer, vous aurez besoin de TokenService. Les fonctions intercepteurs sont des contextes d'injection, vous pouvez donc utiliser la fonctioninject:

export const tokenInterceptor: HttpInterceptorFn = (
  req: HttpRequest<unknown>,
  next: HttpHandlerFn
): Observable<HttpEvent<unknown>> => {
  const tokenService = inject(TokenService);
  return next(req);
}

L'objetreqexpose une fonction clone à laquelle on passe un objet de configuration :

export const tokenInterceptor: HttpInterceptorFn = (
  req: HttpRequest<unknown>,
  next: HttpHandlerFn
): Observable<HttpEvent<unknown>> => {
  const tokenService = inject(TokenService);
  const authReq = req.clone({
    setHeaders: {
      Authorization: `Bearer ${tokenService.userToken()}`
    }
  });
  return next(authReq);
}

Ici, on configuresetHeadersavec notre headerAuthorizationqui contient notre token précédé du mot-cléBearer. On stocke notre clone dans la constanteauthReq, qu'on passe ensuite à  next.

Il ne reste plus qu'à enregistrer cet intercepteur pour qu'il commence à fonctionner. Cela se fait dans  app.config.tsen ajoutantwithInterceptorsà l'appel àprovideHttpClient:

provideHttpClient(
    withInterceptors([tokenInterceptor])
)

Avec ça, si vous essayez de nouveau d'ajouter un bien à vos favoris :

L'étoile qui représente le statut de favori du bien est rempli, montrant que le bien à bien été ajouté aux favoris
Mon bien favori

Et voilà ! En plus, toutes les requêtes de l'application comportent maintenant ce header, donc vous et vos collègues pouvez ajouter autant de routes autorisées que vous voulez sans souci !

Enregistrez les réponses

En plus de pouvoir modifier les requêtes, les intercepteurs peuvent également agir sur les réponses HTTP reçues dans l'application. Il y a plusieurs cas d'usage possibles, et vous allez en implémenter deux : le logging et la gestion centralisée d'erreur.

Commençons par le logging. Dans le dossiercore/logger, créezlogger.interceptor.ts. La fonction d'intercepteur a exactement la même signature quetokenInterceptor:

export const loggerInterceptor: HttpInterceptorFn = (
    req: HttpRequest<unknown>, 
    next: HttpHandlerFn
): Observable<HttpEvent<unknown>> => {
    
}

Les plus perspicaces d'entre vous ont certainement remarqué que nosHttpInterceptorFn  retournent un Observable, ce qui veut dire quenext(req)est un Observable ! Spoiler alert : il émet tous les événements liés à la requête en question, entre autres la réponse ! Du coup, on pourra réagir à cette réponse en ajoutant unpipe():

return next(req).pipe(
    // ?
);

Tout d'abord, il faut savoir qu'il y a plusieurs types d'événement possibles, mais ceux qui nous intéressent sont de typeHttpResponse, donc commençons par filtrer les émissions :

return next(req).pipe(
    filter(event => event instanceof HttpResponse),
);

L'une de vos collègues chez Dwelio a déjà crééLoggerService, donc vous pouvez l'injecter et envoyer les événements via la méthodelog():

const logger = inject(LoggerService);
  return next(req).pipe(
    filter(event => event instanceof HttpResponse),
    tap(event => logger.log(event))
);

Il ne reste plus qu'à enregistrer votre nouvel intercepteur au niveau deapp.config.ts:

provideHttpClient(
    withInterceptors([
        tokenInterceptor,
        loggerInterceptor,
    ])
)

En ouvrant la console, si vous vous baladez un peu dans l'application, vous devriez voir apparaître les logs :

La console montre des tables de réponses HTTP
Toutes les réponses

A vous de jouer

Contexte

Vous et votre équipe décidez de centraliser une première couche de gestion d'erreur : vous souhaitez les log avec LoggerService et afficher un modal d'erreur à l'utilisateur. Vous souhaitez aussi rethrow les erreurs afin que chaque component/service puisse les gérer de manière spécifique.

Consigne

  1. Vous allez implémenter et enregistrer un nouvel intercepteur qui doit réagir à toutes les erreurs HTTP de l'application Dwelio.

  2. L'intercepteur doit log l'erreur avec la méthodelogErrordeLoggerService, et afficher un modal avec la méthodetoggleErrorModaldeModalService  .

  3. Il doit rethrow l'erreur reçue.

Pour tester votre intercepteur, il vous suffit de retirer temporairement votretokenInterceptoret d'essayer d'ajouter un bien aux favoris !

En résumé

  • Un intercepteur HTTP intercepte toutes les requêtes HTTP envoyées par votre application pour effectuer des tâches requises, comme l'ajout d'un header d'autorisation.

  • Un intercepteur Angular est une fonction qui respecte le typeHttpInterceptorFn.

  • La fonction clone la requête reçue en ajoutant les modifications requises au clone.

  • La fonction passe ensuite la nouvelle requête ànext()pour lui permettre de continuer son chemin.

Vous avez maintenant toutes les connaissances nécessaires pour implémenter le HTTP dans vos applications Angular. Allons les valider dès maintenant avec un quiz !

Et si vous obteniez un diplôme OpenClassrooms ?
  • Formations jusqu’à 100 % financées
  • Date de début flexible
  • Projets professionnalisants
  • Mentorat individuel
Trouvez la formation et le financement faits pour vous