Aller au contenu principal

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éTypeDescription
CodestringCode unique de l'erreur
DescriptionstringDescription lisible
TypeErrorTypeCatégorie de l'erreur
MetadataDictionary<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

CodeDescription
Employe.NotFoundEmployé non trouvé
Employe.AlreadyExistsIdExterne déjà utilisé
Employe.InvalidEmailCourriel invalide

Employeurs

CodeDescription
Employeur.NotFoundEmployeur non trouvé
Employeur.AlreadyExistsIdExterne déjà utilisé
Employeur.HasEmployesImpossible de supprimer, a des employés

Validation

CodeDescription
Validation.RequiredChamp obligatoire manquant
Validation.InvalidFormatFormat invalide
Validation.MaxLengthLongueur 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é");
}