Passer au contenu

Gestion des Exceptions dans NestJS

Nest est livré avec une couche d’exception intégrée qui est responsable du traitement de toutes les exceptions non gérées à travers une application. Lorsque qu’une exception n’est pas gérée par votre code d’application, elle est capturée par cette couche qui envoie alors automatiquement une réponse conviviale appropriée.

Filter Image

Par défaut, cette action est effectuée par un filtre d’exception global intégré, qui gère les exceptions de type HttpException (et ses sous-classes). Lorsque qu’une exception est non reconnue (n’est ni HttpException ni une classe qui en hérite), le filtre d’exception intégré génère la réponse JSON par défaut suivante :

{
"statusCode": 500,
"message": "Internal server error"
}

Lancer des exceptions standard

Nest fournit une classe intégrée HttpException, exposée depuis le paquet @nestjs/common. Pour les applications API REST/GraphQL HTTP typiques, il est d’usage d’envoyer des objets de réponse HTTP standard lorsque certaines conditions d’erreur se produisent.

Par exemple, dans le CatsController, nous avons une méthode findAll() (un gestionnaire de route GET). Supposons que ce gestionnaire de route lance une exception pour une raison quelconque. Pour démontrer cela, nous allons le coder de la manière suivante :

cats.controller.ts
@Get()
async findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}

Lorsque le client appelle ce point de terminaison, la réponse ressemble à ceci :

{
"statusCode": 403,
"message": "Forbidden"
}

Le constructeur de HttpException prend deux arguments obligatoires qui déterminent la réponse :

  • L’argument response définit le corps de la réponse JSON. Il peut s’agir d’une string ou d’un object comme décrit ci-dessous.
  • L’argument status définit le code de statut HTTP.

Par défaut, le corps de la réponse JSON contient deux propriétés :

  • statusCode: par défaut le code de statut HTTP fourni dans l’argument status.
  • message: une courte description de l’erreur HTTP en fonction du status.

Exceptions personnalisées

Dans de nombreux cas, vous n’aurez pas besoin d’écrire des exceptions personnalisées et pouvez utiliser l’exception HTTP intégrée de Nest, comme décrit dans la section suivante. Si vous avez besoin de créer des exceptions personnalisées, il est de bonne pratique de créer votre propre hiérarchie d’exceptions, où vos exceptions personnalisées héritent de la classe de base HttpException. Avec cette approche, Nest reconnaîtra vos exceptions et s’occupera automatiquement des réponses d’erreur. Implémentons une telle exception personnalisée :

forbidden.exception.ts
export class ForbiddenException extends HttpException {
constructor() {
super('Forbidden', HttpStatus.FORBIDDEN);
}
}

Puisque ForbiddenException hérite de la base HttpException, elle fonctionnera sans problème avec le gestionnaire d’exceptions intégré, et nous pourrons donc l’utiliser dans la méthode findAll() :

cats.controller.ts
@Get()
async findAll() {
throw new ForbiddenException();
}

Exceptions HTTP intégrées

Nest fournit un ensemble d’exceptions standard qui héritent de la base HttpException. Celles-ci sont exposées depuis le paquet @nestjs/common, et représentent de nombreuses exceptions HTTP les plus courantes :

  • BadRequestException
  • UnauthorizedException
  • NotFoundException
  • ForbiddenException
  • NotAcceptableException
  • RequestTimeoutException
  • ConflictException
  • GoneException
  • HttpVersionNotSupportedException
  • PayloadTooLargeException
  • UnsupportedMediaTypeException
  • UnprocessableEntityException
  • InternalServerErrorException
  • NotImplementedException
  • ImATeapotException
  • MethodNotAllowedException
  • BadGatewayException
  • ServiceUnavailableException
  • GatewayTimeoutException
  • PreconditionFailedException

Toutes les exceptions intégrées peuvent également fournir à la fois une cause d’erreur et une description d’erreur en utilisant le paramètre options :

Exemple avec des exceptions intégrées
throw new BadRequestException('Something bad happened', {
cause: new Error(),
description: 'Some error description'
});

Filtres d’exception

Bien que le filtre d’exception de base (intégré) puisse automatiquement traiter de nombreux cas pour vous, vous souhaiterez peut-être un contrôle total sur la couche d’exceptions. Par exemple, vous pouvez vouloir ajouter la journalisation ou utiliser un schéma JSON différent en fonction de certains facteurs dynamiques. Les filtres d’exception sont conçus pour exactement ce but. Ils vous permettent de contrôler le flux exact de contrôle et le contenu de la réponse renvoyée au client.

Créons un filtre d’exception responsable de la capture des exceptions qui sont une instance de la classe HttpException, et d’implémenter une logique de réponse personnalisée pour elles. Pour cela, nous devrons accéder aux objets de plateforme sous-jacents Request et Response. Nous accéderons à l’objet Request pour pouvoir extraire l’URL d’origine et l’inclure dans les informations de journalisation. Nous utiliserons l’objet Response pour prendre le contrôle direct de la réponse envoyée, en utilisant la méthode response.json().

http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}

Le décorateur @Catch(HttpException) lie les métadonnées requises au filtre d’exception, indiquant à Nest que ce filtre particulier recherche des exceptions de type HttpException et rien d’autre. Le décorateur @Catch() peut prendre un seul paramètre, ou une liste séparée par des virgules. Cela vous permet de configurer le filtre pour plusieurs types d’exceptions à la fois.