Gestion des erreurs
MCM.ApiProxy utilise le pattern ErrorOr<T> pour une gestion d'erreurs explicite et type-safe.
Le pattern ErrorOr
Au lieu de lancer des exceptions, les méthodes retournent un ErrorOr<T> qui contient soit une valeur, soit une liste d'erreurs.
var result = await _employesClient.GetEmployeById("EMP-123");
// Vérifier s'il y a une erreur
if (result.IsError)
{
// Gérer l'erreur
var error = result.FirstError;
Console.WriteLine($"Erreur: {error.Description}");
return;
}
// Utiliser la valeur
var employe = result.Value;
Structure d'une erreur
Chaque Error contient :
| Propriété | Type | Description |
|---|---|---|
Code | string | Code unique de l'erreur |
Description | string | Description lisible |
Type | ErrorType | Catégorie de l'erreur |
Metadata | Dictionary<string, object>? | Données supplémentaires |
Types d'erreurs
public enum ErrorType
{
Failure, // Erreur générique
Unexpected, // Erreur inattendue
Validation, // Erreur de validation
Conflict, // Conflit (doublon, etc.)
NotFound, // Ressource non trouvée
Unauthorized // Non autorisé
}
Patterns de gestion
Pattern simple
var result = await _employesClient.GetEmployeById(idExterne);
if (result.IsError)
{
_logger.LogError("Erreur: {Error}", result.FirstError.Description);
return null;
}
return result.Value;
Pattern avec switch sur le type
var result = await _employesClient.GetEmployeById(idExterne);
if (result.IsError)
{
return result.FirstError.Type switch
{
ErrorType.NotFound => NotFound($"Employé {idExterne} non trouvé"),
ErrorType.Unauthorized => Unauthorized("Accès refusé"),
ErrorType.Validation => BadRequest(result.FirstError.Description),
_ => StatusCode(500, "Erreur interne")
};
}
return Ok(result.Value);
Pattern avec Match
var result = await _employesClient.GetEmployeById(idExterne);
return result.Match(
employe => Ok(employe),
errors => errors.First().Type switch
{
ErrorType.NotFound => NotFound(),
_ => StatusCode(500)
}
);
Gestion de plusieurs erreurs
var result = await _employesClient.AddEmploye(employe);
if (result.IsError)
{
// Parcourir toutes les erreurs
foreach (var error in result.Errors)
{
_logger.LogWarning("Erreur {Code}: {Description}",
error.Code, error.Description);
}
// Ou les joindre en un message
var message = string.Join(", ", result.Errors.Select(e => e.Description));
return BadRequest(message);
}
Codes d'erreur courants
Employés
| Code | Description |
|---|---|
Employe.NotFound | Employé non trouvé |
Employe.AlreadyExists | IdExterne déjà utilisé |
Employe.InvalidEmail | Courriel invalide |
Employeurs
| Code | Description |
|---|---|
Employeur.NotFound | Employeur non trouvé |
Employeur.AlreadyExists | IdExterne déjà utilisé |
Employeur.HasEmployes | Impossible de supprimer, a des employés |
Validation
| Code | Description |
|---|---|
Validation.Required | Champ obligatoire manquant |
Validation.InvalidFormat | Format invalide |
Validation.MaxLength | Longueur maximale dépassée |
Bonnes pratiques
1. Toujours vérifier IsError
// BON
if (result.IsError)
{
// Gérer l'erreur
}
// MAUVAIS - lancera une exception si erreur
var value = result.Value;
2. Logger les erreurs
if (result.IsError)
{
_logger.LogError(
"Opération échouée - Code: {Code}, Description: {Description}",
result.FirstError.Code,
result.FirstError.Description);
}
3. Propager les erreurs
public async Task<ErrorOr<EmployeDto>> GetEmployeFormatteAsync(string id)
{
var result = await _employesClient.GetEmployeById(id);
if (result.IsError)
{
return result.Errors; // Propager les erreurs
}
// Transformer la valeur
return MapToDto(result.Value);
}
4. Créer des erreurs personnalisées
public static class MonApplicationErrors
{
public static Error EmployeInactif(string id) =>
Error.Validation(
"MonApp.EmployeInactif",
$"L'employé {id} est inactif et ne peut pas être modifié");
}