23. Global Exception Handling & Error Contracts

@RestControllerAdvice & Exception Mappings

Master centralized exception handling with @RestControllerAdvice, @ExceptionHandler, ErrorResponseDto envelopes, and HTTP 400 vs 409 semantics.

@RestControllerAdvice & Exception Mappings

Global Exception Handling centralizes failure translation across all Controllers in a Spring Boot application, transforming uncaught Java exceptions into predictable HTTP error responses.


1. @ControllerAdvice vs @RestControllerAdvice

@RestControllerAdvice  ≡  @ControllerAdvice  +  @ResponseBody
  • @ControllerAdvice: Intercepts exceptions thrown across controllers; handler methods return view templates unless annotated with @ResponseBody.
  • @RestControllerAdvice: Specialized advice for REST APIs; handler method return values are automatically serialized into JSON response bodies.

2. Standardized Error Contract (ErrorResponseDto)

To prevent returning inconsistent JSON structures across different endpoints, define a standardized error envelope:

public class ErrorResponseDto {
    private final LocalDateTime timestamp;
    private final int statusCode;
    private final String error;
    private final String message;
    private final String path;

    public ErrorResponseDto(int statusCode, String error, String message, String path) {
        this.timestamp = LocalDateTime.now();
        this.statusCode = statusCode;
        this.error = error;
        this.message = message;
        this.path = path;
    }

    public LocalDateTime getTimestamp() { return timestamp; }
    public int getStatusCode() { return statusCode; }
    public String getError() { return error; }
    public String getMessage() { return message; }
    public String getPath() { return path; }
}
{
  "timestamp": "2026-08-13T23:51:00",
  "statusCode": 404,
  "error": "Not Found",
  "message": "Student with id 100 not found",
  "path": "/api/students/100"
}

3. Global Exception Handler Implementation

Inject HttpServletRequest into @ExceptionHandler methods to extract request.getRequestURI() dynamically:

@RestControllerAdvice
public class GlobalExceptionHandler {

    // Handle 404 Not Found
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponseDto> handleResourceNotFound(
            ResourceNotFoundException ex, HttpServletRequest request) {

        ErrorResponseDto error = new ErrorResponseDto(
                HttpStatus.NOT_FOUND.value(),
                HttpStatus.NOT_FOUND.getReasonPhrase(),
                ex.getMessage(),
                request.getRequestURI()
        );
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }

    // Handle 409 Conflict
    @ExceptionHandler(DuplicateResourceException.class)
    public ResponseEntity<ErrorResponseDto> handleDuplicateResource(
            DuplicateResourceException ex, HttpServletRequest request) {

        ErrorResponseDto error = new ErrorResponseDto(
                HttpStatus.CONFLICT.value(),
                HttpStatus.CONFLICT.getReasonPhrase(),
                ex.getMessage(),
                request.getRequestURI()
        );
        return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
    }
}

4. 400 Bad Request vs 409 Conflict: The Critical Distinction

Error CategoryHTTP StatusMeaningTypical Trigger Scenario
Client Input Error400 Bad RequestThe request payload/syntax itself is malformed or invalidInvalid email string, missing field, malformed JSON
Server State Conflict409 ConflictRequest payload is valid, but conflicts with current DB stateSubmitting an email/username that already exists
HTTP ERROR DECISION TREE:
Can the server parse and understand the payload?
 ├── NO  ──► 400 Bad Request (Invalid syntax / DTO validation failure)
 └── YES ──► Does it conflict with existing database state?
              ├── YES ──► 409 Conflict (Duplicate record / Unique key violation)
              └── NO  ──► Continue normal processing

❓ Knowledge Check

Knowledge Check

What is the difference between @ControllerAdvice and @RestControllerAdvice in Spring Boot?

Knowledge Check

Why does duplicate email submission produce HTTP 409 Conflict rather than HTTP 400 Bad Request?

On this page