Passer au contenu

Cycle de vie des requêtes

Les applications Nest gèrent les requêtes et produisent des réponses dans une séquence que nous appelons le cycle de vie des requêtes. Avec l’utilisation de middleware, de pipes, de guards et d’intercepteurs, il peut être difficile de suivre où un morceau de code particulier s’exécute pendant le cycle de vie des requêtes, surtout lorsque des composants globaux, à niveau de contrôleur et à niveau de route entrent en jeu. En général, une requête passe par des middleware aux guards, puis aux intercepteurs, puis aux pipes et enfin retourne aux intercepteurs lors du chemin de retour (au fur et à mesure que la réponse est générée).

Middleware

Le middleware est exécuté dans un ordre particulier. Tout d’abord, Nest exécute le middleware lié globalement (tel que le middleware lié avec app.use) puis il exécute le middleware lié au module, qui sont déterminés en fonction des chemins. Le middleware est exécuté séquentiellement dans l’ordre où il est lié, de la même manière que le middleware dans Express fonctionne. Dans le cas de middlewares liés à différents modules, le middleware lié au module racine sera exécuté en premier, puis le middleware sera exécuté dans l’ordre dans lequel les modules sont ajoutés au tableau d’importations.

Guards

L’exécution des guards commence par des guards globaux, puis passe aux guards de contrôleur, et enfin aux guards de route. Comme avec le middleware, les guards s’exécutent dans l’ordre dans lequel ils sont liés. Par exemple :

Exemple d'utilisation des Guards
@UseGuards(Guard1, Guard2)
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@UseGuards(Guard3)
@Get()
getCats(): Cats[] {
return this.catsService.getCats();
}
}

Le Guard1 s’exécutera avant Guard2 et les deux s’exécuteront avant Guard3.

Intercepteurs

Les intercepteurs, pour la plupart, suivent le même modèle que les guards, avec une particularité : comme les intercepteurs retournent des Observables RxJS, les observables seront résolus dans un ordre de premier arrivé, dernier sorti. Ainsi, les requêtes entrantes passeront par la résolution standard globale, de contrôleur et de niveau de route, mais le côté réponse de la requête (c’est-à-dire, après retour de la méthode de gestion du contrôleur) sera résolu de la route au contrôleur, puis au global. De plus, les erreurs lancées par les pipes, contrôleurs ou services peuvent être lues dans l’opérateur catchError d’un intercepteur.

Pipes

Les pipes suivent la séquence standard de global à contrôleur à route, avec le même ordre premier arrivé, premier sorti en ce qui concerne les paramètres de @UsePipes(). Cependant, à un niveau de paramètre de route, si vous avez plusieurs pipes s’exécutant, ils s’exécuteront dans l’ordre du dernier paramètre avec un pipe au premier. Cela s’applique également aux pipes de niveau de route et de niveau de contrôleur. Par exemple, si nous avons le contrôleur suivant :

Exemple d'utilisation des Pipes
@UsePipes(GeneralValidationPipe)
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@UsePipes(RouteSpecificPipe)
@Patch(':id')
updateCat(
@Body() body: UpdateCatDTO,
@Param() params: UpdateCatParams,
@Query() query: UpdateCatQuery,
) {
return this.catsService.updateCat(body, params, query);
}
}

Alors le GeneralValidationPipe sera exécuté pour les objets query, puis params, et enfin body avant de passer au RouteSpecificPipe, qui suit le même ordre.

Filtres

Les filtres sont le seul composant qui ne se résout pas d’abord au niveau global. Au lieu de cela, les filtres se résolvent du niveau le plus bas possible, ce qui signifie que l’exécution commence par n’importe quel filtre lié à une route et se déplace ensuite vers le niveau du contrôleur, et enfin vers les filtres globaux. Notez que les exceptions ne peuvent pas être transférées d’un filtre à l’autre ; si un filtre de niveau route attrape l’exception, un filtre de niveau contrôleur ou global ne peut pas attraper la même exception. La seule façon d’obtenir un effet similaire est d’utiliser l’héritage entre les filtres.

Résumé

En général, le cycle de vie des requêtes ressemble à ce qui suit :

  1. Requête entrante
  2. Middleware
    • 2.1. Middleware lié globalement
    • 2.2. Middleware lié au module
  3. Guards
    • 3.1. Guards globaux
    • 3.2. Guards de contrôleur
    • 3.3. Guards de route
  4. Intercepteurs (pré-contrôleur)
    • 4.1. Intercepteurs globaux
    • 4.2. Intercepteurs de contrôleur
    • 4.3. Intercepteurs de route
  5. Pipes
    • 5.1. Pipes globaux
    • 5.2. Pipes de contrôleur
    • 5.3. Pipes de route
    • 5.4. Pipes de paramètres de route
  6. Contrôleur (gestionnaire de méthode)
  7. Service (s’il existe)
  8. Intercepteurs (post-requête)
    • 8.1. Intercepteur de route
    • 8.2. Intercepteur de contrôleur
    • 8.3. Intercepteur global
  9. Filtres d’exception
    • 9.1. route
    • 9.2. contrôleur
    • 9.3. global
  10. Réponse du serveur