@ControllerAdvice + @ExceptionHandler xử lý exception tập trung — không cần try/catch rải khắp controller.
java
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
ProblemDetail handleNotFound(EntityNotFoundException ex) {
return ProblemDetail.forStatusAndDetail(NOT_FOUND, ex.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
Map<String, String> errors = new LinkedHashMap<>();
ex.getBindingResult().getFieldErrors()
.forEach(e -> errors.put(e.getField(), e.getDefaultMessage()));
ProblemDetail pd = ProblemDetail.forStatus(BAD_REQUEST);
pd.setProperty("errors", errors);
return pd;
}
@ExceptionHandler(Exception.class)
@ResponseStatus(INTERNAL_SERVER_ERROR)
ProblemDetail handleGeneric(Exception ex) {
log.error("Unhandled exception", ex);
return ProblemDetail.forStatusAndDetail(INTERNAL_SERVER_ERROR, "Internal error");
}
}ProblemDetail (Spring 6, RFC 9457) = chuẩn error response: type, title, status, detail.
Scope: @ControllerAdvice áp cho tất cả controller. Thu hẹp: @ControllerAdvice(basePackages = "com.example.api").
@ControllerAdvice + @ExceptionHandler handle exceptions centrally — no try/catch scattered across controllers.
java
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
ProblemDetail handleNotFound(EntityNotFoundException ex) {
return ProblemDetail.forStatusAndDetail(NOT_FOUND, ex.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
Map<String, String> errors = new LinkedHashMap<>();
ex.getBindingResult().getFieldErrors()
.forEach(e -> errors.put(e.getField(), e.getDefaultMessage()));
ProblemDetail pd = ProblemDetail.forStatus(BAD_REQUEST);
pd.setProperty("errors", errors);
return pd;
}
@ExceptionHandler(Exception.class)
@ResponseStatus(INTERNAL_SERVER_ERROR)
ProblemDetail handleGeneric(Exception ex) {
log.error("Unhandled exception", ex);
return ProblemDetail.forStatusAndDetail(INTERNAL_SERVER_ERROR, "Internal error");
}
}ProblemDetail (Spring 6, RFC 9457) = standard error response: type, title, status, detail.
Scope: @ControllerAdvice applies to all controllers. Narrow with @ControllerAdvice(basePackages = "com.example.api").