Skip to main content
spring boot the mechanics of magic

Exception Handling and Response Marshalling

5 min read Chapter 16 of 24
Summary

This section establishes a comprehensive global exception handling...

This section establishes a comprehensive global exception handling strategy for Spring applications. It introduces core mechanisms: declarative @ExceptionHandler within @ControllerAdvice for type-based exception handling, contrasted with the programmatic HandlerExceptionResolver interface. The strategy integrates with RFC 7807 ProblemDetails for standardized error responses using Spring Framework 6's ProblemDetail class, ensuring consistent error structures. A critical security focus is preventing information leakage through secure fallback handlers that avoid exposing internal exception details. Jackson ObjectMapper customization is covered for secure serialization, including @JsonIgnore annotations and Mixins to prevent accidental serialization of sensitive fields like passwords and API keys. The LogisticsCore application serves as the example context throughout, with Java 21+ features including Records for error context. Key terms defined: @ExceptionHandler, HandlerExceptionResolver, ProblemDetails (RFC 7807), Jackson ObjectMapper, Jackson Module, Jackson Mixin, and Information Leakage. The section emphasizes practical implementation patterns for production-ready error handling while maintaining security.

Exception Handling and Response Marshalling

In the context of building robust web applications with Spring, effective exception handling is non-negotiable. Failure to implement a centralized, secure, and standardized strategy results in information leakage, inconsistent error contracts, and increased attack surface. This section mandates the implementation of global exception handling using @ControllerAdvice and @ExceptionHandler, enforces integration with ProblemDetails (RFC 7807) for standardized error payloads, and requires secure configuration of the Jackson ObjectMapper. All examples are grounded in the LogisticsCore warehouse management system.

Introduction to Exception Handling in Spring

Spring Framework provides two primary mechanisms for exception handling: @ExceptionHandler and HandlerExceptionResolver. Understanding the distinction is critical.

  • @ExceptionHandler operates at the controller method level and is processed by ExceptionHandlerExceptionResolver, a concrete implementation of HandlerExceptionResolver. It uses reflection to locate annotated methods and binds them to specific exception types.
  • HandlerExceptionResolver is the lower-level interface that allows programmatic resolution of exceptions. Implementations such as ResponseEntityExceptionHandler provide default handling for Spring MVC exceptions.

While @ExceptionHandler offers declarative convenience, it is ultimately a thin abstraction over the HandlerExceptionResolver chain. Prefer @ControllerAdvice with @ExceptionHandler for application-wide consistency, but recognize that this relies on proxying via CGLIB when applied to non-interface methods.

Using @ExceptionHandler

The @ExceptionHandler annotation must be used within a @ControllerAdvice class to ensure global coverage. Local usage within individual controllers is prohibited in LogisticsCore—decentralized error handling leads to contract fragmentation.

// Example: Prohibited—local exception handling in controller
@RestController
public class ShipmentController {
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
        return ResponseEntity.badRequest().body(ex.getMessage());
    }
}

This approach violates the principle of centralized error contract management. All exception handling must be delegated to a single, auditable entry point.

Global Exception Handling with @ControllerAdvice

@ControllerAdvice enables global exception handling by registering advice classes across all @RequestMapping methods. It is implemented using CGLIB proxies to intercept method invocations and apply cross-cutting concerns.

All LogisticsCore services must implement a global exception handler that extends ResponseEntityExceptionHandler, the base class provided by Spring Framework for handling standard Spring MVC exceptions (e.g., MethodArgumentNotValidException, HttpMessageNotReadableException).

// Example: Required global exception handler in LogisticsCore
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity<Object> handleExceptionInternal(
            Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return super.handleExceptionInternal(ex, body, headers, status, request);
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<ProblemDetail> handleIllegalArgumentException(
            IllegalArgumentException ex, HttpServletRequest request) {
        ProblemDetail problem = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, ex.getMessage());
        problem.setType(URI.create("https://api.logistics.core/errors/invalid-argument"));
        problem.setTitle("Invalid Request Argument");
        problem.setInstance(URI.create(request.getRequestURI()));
        return ResponseEntity.status(problem.getStatus()).body(problem);
    }
}

This ensures uniform error responses and prevents unhandled exceptions from propagating raw details to clients.

Integrating with ProblemDetails (RFC 7807)

LogisticsCore enforces RFC 7807 compliance for all error responses. Raw string or ad-hoc JSON errors are prohibited. The ProblemDetail class is part of Spring Framework 6, not Spring Boot. Spring Boot auto-configures support when spring-web 6+ is present, but the class originates in the framework.

Using ProblemDetail ensures interoperability, machine-readability, and traceability. Each error must include:

  • type: A URI reference identifying the error category
  • title: A human-readable summary
  • status: The HTTP status code
  • detail: Specific error message
  • instance: The request URI
// Example: RFC 7807-compliant error for invalid inventory operation
@ExceptionHandler(InsufficientStockException.class)
public ResponseEntity<ProblemDetail> handleInsufficientStock(
        InsufficientStockException ex, HttpServletRequest request) {
    ProblemDetail problem = ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT, ex.getMessage());
    problem.setType(URI.create("https://api.logistics.core/errors/insufficient-stock"));
    problem.setTitle("Insufficient Stock for Shipment");
    problem.setInstance(URI.create(request.getRequestURI()));
    return ResponseEntity.status(HttpStatus.CONFLICT).body(problem);
}

This structure enables client-side error routing and automated remediation workflows.

Customizing Jackson ObjectMapper for Secure Serialization

Serialization must not expose internal state. Jackson’s ObjectMapper must be configured to prevent leakage of sensitive fields, null values, or implementation details.

Using @JsonIgnore

Apply @JsonIgnore to fields that must never be serialized, such as credentials or internal identifiers.

// Example: Securing UserRecord in LogisticsCore
public record UserRecord(
        String username,
        @JsonIgnore String password,
        Role role
) {}

This uses Java 21 record syntax with targeted exclusion. Avoid @JsonIgnoreProperties on types—prefer field-level control.

Configuring a Global ObjectMapper

Define a single ObjectMapper bean to enforce serialization policies across the application.

// Example: Secure ObjectMapper configuration in LogisticsCore
@Configuration
public class JacksonConfig {
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        mapper.registerModule(new JavaTimeModule());
        // Prevent exposure of internal fields
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
        return mapper;
    }
}

This configuration ensures consistent, secure serialization across all endpoints.

Preventing Information Leakage

Information leakage is a critical vulnerability. The following must never appear in error responses:

  • Stack traces
  • Database error codes or messages (e.g., ORA-00942, Duplicate entry 'X' for key 'Y')
  • Internal class names or method signatures
  • File system paths (e.g., /var/lib/tomcat/webapps/ROOT/...)
  • Configuration property names

These details aid attackers in fingerprinting the stack and crafting targeted exploits. The GlobalExceptionHandler must catch all exceptions and map them to sanitized ProblemDetail responses. Raw exceptions must not reach the response writer.

Conclusion

Exception handling in LogisticsCore is not optional—it is a security and contract enforcement mechanism. All services must:

  1. Use @ControllerAdvice with ResponseEntityExceptionHandler as the base class
  2. Return only RFC 7807-compliant ProblemDetail responses
  3. Configure a global ObjectMapper to prevent serialization leakage
  4. Never expose raw exceptions, stack traces, or internal errors

Failure to comply results in rejected deployments and security findings. The combination of Spring Framework 6’s native ProblemDetail, strict ObjectMapper configuration, and centralized @ControllerAdvice forms the foundation of a secure, observable, and maintainable error handling strategy.

Sources

[1] IETF, “RFC 7807: Problem Details for HTTP APIs,” 2016. [Online]. Available: https://datatracker.ietf.org/doc/html/rfc7807

[2] Spring Framework, “Exception Handling in Spring MVC,” Spring Documentation. [Online]. Available: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#web-handlerexceptionresolver

[3] FasterXML, “Jackson Documentation: Custom Serialization,” GitHub. [Online]. Available: https://github.com/FasterXML/jackson-docs/wiki/JacksonFeaturesCustomSerialization