Aller au contenu principal

28-2 Gestion d'erreurs et fichiers ressources

Gestion des erreurs

Normalement, il ne devrait pas être possible de créer d'événement sans nom puisqu'il s'agit d'une valeur requise associée à une colonne de BD ne pouvant être null.

Si on essaie, ça ne fonctionnera pas, mais l'erreur reçue n'est pas d'une grande aide pour quelqu'un qui utilise l'API. En effet, il serait préférable de retourner un message d'erreur comme quoi le champ nom est requis.

Imgur

Vous remarquez que la requête se rend jusqu'à la base de données et une erreur de Postgres est soulevée.

Pour prévenir cela, on peut utiliser les DataAnnotation, de la même façon que pour les ViewModel dans le projet MVC rendu serveur.

Par exemple, ajoutons les DataAnnotations (ex.: [Required]) de base au DTO CreerEvenementDto.

Snowfall.Application/Dtos/Evenements/CreerEvenementDto.cs
public class CreerEvenementDto
{
[Required]
public required string Nom { get; set; }
[MinLength(10)]
public string? Description { get; set; }
public string? ImagePath { get; set; }
[Required]
public DateTime Date { get; set; }
[Required]
public Decimal Prix { get; set; }
[Required]
public int Capacite { get; set; }
[Required]
public int VilleId { get; set; }
}

Maintenant, si vous recompilez le projet et essayez la même requête que précédemment (événement incomplet), le message affiché est différent, mais toujours plus ou moins utile...

img

Le code d'erreur est mieux qu'une erreur 500, soit 400 Bad Request, mais pourquoi The creerEvenementDto field is required et une erreur de désérialisation JSON!?

Types nullables, required et validations

En C#, le mot clé required sur les propriétés signifie que le la propriété est nécessaire pour instancier l'objet. Or, lorsque l'application essaie de convertir le JSON reçu en objet, l'opération échoue, le nom n'est pas présent!

Ainsi, on ne se rend même pas aux validations. Un peu comme pour les ViewModel, on pourrait utiliser String.Empty pour donner une valeur par défaut, sans toutefois compromettre la validation:

[Required]
public string Nom { get; set; } = String.Empty;

Une autre approche est d'assigner null et d'utiliser ! pour forcer le compilateur à accepter la valeur.

[Required]
public string Nom { get; set; } = null!;

Cette approche est typiquement l'approche privilégiée par C#. Ainsi, le DTO deviendrait:

public class CreerEvenementDto
{
[Required]
public string Nom { get; set; } = null!;
[MinLength(10)]
public string? Description { get; set; }
public string? ImagePath { get; set; }
[Required]
public DateTime Date { get; set; }
[Required]
public Decimal Prix { get; set; }
[Required]
public int Capacite { get; set; }
[Required]
public int VilleId { get; set; }
}

Tester la requête

Maintenant, is vous recompilez et testez la requête, vous devriez obtenir un message d'erreur beaucoup plus pertinent!

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Nom": [
"The Nom field is required."
]
},
"traceId": "00-ff11706c6d21a3999050f01ea1e182fd-b752232ac843dc14-00"
}

Fichiers ressources

Ne serait-il pas merveilleux si en plus on pouvait mettre des messages d'erreur personnalisés et les récupérer à partir de fichiers ressources!?

Nous allons créer des fichiers ressources propres à la couche Application et les DTO.

  1. Sous le projet Application, créez un dossier Resources.
  2. Sous le dossier Resources -> Add -> Resources (.resx)
  3. Nommez le fichier ressource Evenements

À l'intérieur, on peut ensuite y mettre les différents messages de validation associés aux propriétés du DTO ou tout autre message.

Imgur

Pour finir, on fait référence au fichier et aux propriétés directement à l'aide de ErrorMessageResourceType et ErrorMessageResourceName.

info

Dans le projet MVC, pour les ViewModel, il y avait un lien automatique entre une classe (ex.: un ViewModel) et un fichier ressource si ce dernier était dans le bon dossier et portait le bon nom.

Ce lien n'est pas automatisé dans le projet Application puisque ce dernier n'utilise au fond aucun framework particulier, autre que .NET Core.

Une méthode manuelle est donc utilisée dans ce contexte.

Snowfall.Application/Dtos/Evenements/CreerEvenementDto.cs
public class CreerEvenementDto
{
[Required(ErrorMessageResourceType = typeof(Resources.Evenements), ErrorMessageResourceName = "Nom_Required")]
public string Nom { get; set; } = null!;
[MinLength(10, ErrorMessageResourceType = typeof(Resources.Evenements), ErrorMessageResourceName = "Description_MinLength")]
public string? Description { get; set; }
public string? ImagePath { get; set; }
[Required(ErrorMessageResourceType = typeof(Resources.Evenements), ErrorMessageResourceName = "Date_Required")]
public DateTime Date { get; set; }
[Required(ErrorMessageResourceType = typeof(Resources.Evenements), ErrorMessageResourceName = "Prix_Required")]
public Decimal Prix { get; set; }
[Required(ErrorMessageResourceType = typeof(Resources.Evenements), ErrorMessageResourceName = "Capacite_Required")]
public int Capacite { get; set; }
[Required(ErrorMessageResourceType = typeof(Resources.Evenements), ErrorMessageResourceName = "VilleId_Required")]
public int VilleId { get; set; }
}

Les messages d'erreur en provenance de l'API seront traduits et utiliseront les fichiers ressources!

Imgur

À propos de la validation

Peut-être avez-vous remarqué, mais à aucune étape on ne fait de validation sur les DTO reçus. Pourtant, ils sont validés et les actions de contrôleur ne vont pas plus loin si le DTO reçu en paramètre n'est pas valide.

Dans le projet MVC, il fallait utiliser ModelState.IsValid pour déclencher le processus de validation.

Au niveau de l'API, la magie opère grâce à [ApiController]:

[Route("api/[controller]")]
[ApiController]
public class EvenementsController : ControllerBase

Cet attribut a comme effet, entre autres, de déclencher le processus de validation automatiquement pour les paramètres d'entrée des actions du contrôleur.

Comme dans l'action de création, on reçoit en paramètre un EvenementDto:

public async Task<IActionResult> Create(EvenementDto evenementDto)

La validation est déclenchée automatiquement sans intervention de notre part. Dans le cas où les validations échouent, le code d'erreur 400 Bad Request est retourné avec les erreurs associées.

C'est extrêmement pratique puisque cela fait un cas de figure de moins à gérer. On peut assumer qu'une fois dans la fonction, le DTO reçu est valide. Tant et aussi longtemps que les validations sont bien appliquées à ce dernier évidemment!