Gestion des exceptions et erreurs : une approche robuste et centralisée avec Spring Boot et Angular
Une application moderne doit gérer les erreurs de façon prévisible, sécurisée et homogène. Cette approche réduit la complexité, améliore l’observabilité et garantit une expérience utilisateur cohérente. Avec Spring Boot côté serveur et Angular côté client, il est possible d’implémenter une stratégie de gestion des exceptions à la fois robuste et centralisée.
Objectifs d’une gestion centralisée
La centralisation vise principalement :
- Uniformiser les formats de réponse d’erreur (code, message, détail, corrélation).
- Sécuriser les informations sensibles (ne pas exposer de stacktrace au client).
- Améliorer l’observabilité via des identifiants de corrélation et un logging structuré.
- Faciliter le diagnostic et la maintenance.
Spring Boot : centraliser les exceptions avec @ControllerAdvice
Spring Boot permet d’intercepter les erreurs à un niveau global grâce à @ControllerAdvice et @ExceptionHandler. Cette couche transforme les exceptions en réponses HTTP cohérentes (souvent via Problem Details ou un contrat JSON maison).
Modèle de réponse d’erreur
Un contrat d’erreur standard aide le client à interpréter et afficher correctement les messages.
Gestion globale des exceptions
Le conseil ci-dessous illustre une approche structurée : mappage des exceptions métiers, validation, et erreurs génériques.
handleValidation(
MethodArgumentNotValidException ex,
HttpServletRequest request) {
String correlationId = CorrelationIdUtil.fromRequest(request);
ApiError error = new ApiError(
"VALIDATION_ERROR",
"Requête invalide : vérification des champs échouée.",
request.getRequestURI(),
correlationId
);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity handleNotFound(
ResourceNotFoundException ex,
HttpServletRequest request) {
String correlationId = CorrelationIdUtil.fromRequest(request);
ApiError error = new ApiError(
"NOT_FOUND",
ex.getMessage(),
request.getRequestURI(),
correlationId
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity handleGeneric(
Exception ex,
HttpServletRequest request) {
String correlationId = CorrelationIdUtil.fromRequest(request);
// Log structuré côté serveur
// logger.error("Unhandled exception. correlationId={}", correlationId, ex);
ApiError error = new ApiError(
"INTERNAL_SERVER_ERROR",
"Une erreur inattendue est survenue. Référence : " + correlationId,
request.getRequestURI(),
correlationId
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
]]>
Corrélation et traçabilité
Pour relier les requêtes et les logs, un correlationId est généralement propagé via un en-tête HTTP (ex. X-Correlation-Id). Un filtre peut être utilisé pour l’initialiser.
Spring Boot : distinguer erreurs techniques et erreurs métier
Une erreur métier (ex. ressource inexistante, état invalide, droits insuffisants) doit être mappée sur un code applicatif et un HTTP status pertinent. À l’inverse, les erreurs techniques (null pointer, échec d’intégration, etc.) doivent rester génériques côté client tout en étant détaillées dans les logs.
Exemple de hiérarchie d’exceptions
Angular : une stratégie centralisée de gestion d’erreurs
Côté client, il est recommandé d’industrialiser le traitement des erreurs via un HttpInterceptor. Cela évite la duplication dans chaque composant et garantit un comportement homogène.
Modèle d’erreur côté Angular
HttpInterceptor pour mapper les réponses en actions UI
L’intercepteur peut gérer : redirections (401/403), notifications (400), et message générique (500).
, next: HttpHandler): Observable> {
return next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
const apiError: any = error.error;
// Exemples de décisions UI
switch (error.status) {
case 400:
// notification toast : apiError.message
break;
case 401:
// redirection vers login
break;
case 403:
// notification "accès refusé"
break;
case 404:
// notification "ressource introuvable"
break;
default:
// message générique + correlationId si disponible
break;
}
return throwError(() => error);
})
);
}
}
]]>
Enregistrement de l’intercepteur
Contrat API : cohérence des codes et messages
Une réponse d’erreur devrait être stable et suffisamment informative pour le client, sans révéler d’implémentation interne. Un schéma minimal utile inclut :
- code : identifiant applicatif (ex. VALIDATION_ERROR, NOT_FOUND).
- message : texte orienté utilisateur.
- path : utile au diagnostic.
- correlationId : référence pour les logs.
Exemple de réponse JSON
Bonnes pratiques de sécurité et d’ergonomie
- Ne pas exposer les détails techniques au client (stacktrace, requêtes internes).
- Log structuré côté serveur avec correlationId.
- Messages stables : éviter les variations inutiles qui compliquent la traduction côté UI.
- Statuts HTTP corrects : BAD_REQUEST pour validation, NOT_FOUND pour absence, FORBIDDEN/UNAUTHORIZED pour sécurité.
Résumé
En combinant @ControllerAdvice et un HttpInterceptor, une application Spring Boot + Angular obtient une gestion des erreurs centralisée, cohérente et traçable. La séparation entre erreurs métier et erreurs techniques permet de préserver la sécurité tout en améliorant l’expérience utilisateur et la capacité de diagnostic.
Checklist rapide
- Spring Boot : mapping global via @ControllerAdvice.
- Contrat d’erreur : code + message + path + correlationId.
- Logs : enrichis avec correlationId, sans fuite de stacktrace au client.
- Angular : traitement centralisé via HttpInterceptor.
- UI : notifications et redirections basées sur le statut HTTP.
À propos de l'auteur
Laty Gueye Samba est développeur Full Stack basé à Dakar, Sénégal. Spécialiste des écosystèmes Java / Spring Boot et Angular.
Contact : latygueyesamba@gmail.com | Dakar, Sénégal