Katkat

Exception Handling in a RESTful Service with @ControllerAdvice

J.P. MorganSoftware Engineer · Onsite

Last updated

This problem tests your ability to design a centralized exception handling mechanism in a Java Spring environment, ensuring consistent API error responses and proper abstraction of internal implementation details.

The Problem

You are building a RESTful service for a banking application. You need to ensure that regardless of where an error occurs in your service layer, the client receives a consistent, standardized JSON response that follows a predefined contract. Furthermore, you must ensure that sensitive stack traces or internal server details are never exposed to the client.

Requirements

  1. Define a standard error response structure (e.g., ErrorResponse) that includes a timestamp, an HTTP status code, a human-readable error message, and a unique internal trace ID.
  2. Implement a centralized exception handler using @ControllerAdvice to intercept specific exceptions (e.g., UserNotFoundException, InsufficientFundsException, and generic Exception).
  3. Map these custom exceptions to appropriate HTTP status codes (e.g., 404 Not Found for missing resources, 422 Unprocessable Entity for business logic violations).

Task

Write a Java implementation consisting of:

  • A POJO class ErrorResponse.
  • A global exception handler class GlobalExceptionHandler using @ControllerAdvice.
  • A brief explanation of how you would generate and log the traceId so that it correlates with server-side logs.

Example

If a UserNotFoundException is thrown, the client should receive a 404 status with the body:

{
  "timestamp": "2023-10-27T10:00:00Z",
  "status": 404,
  "message": "User with ID 123 not found.",
  "traceId": "a1-b2-c3-d4"
}

Hints

Hint 1

Consider using the @ExceptionHandler annotation on methods within your @ControllerAdvice class to map specific exception types to response entities.

Hint 2

Use a request-scoped bean or a thread-local variable to generate a unique correlation ID at the start of each request for the traceId field.

Hint 3

Ensure the generic Exception handler is marked with @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) to catch unhandled runtime errors and prevent leakage.

Follow-up questions

  • How would you handle validation errors, such as those thrown by @Valid annotations, within this centralized framework?
  • If your application grows to have dozens of business exceptions, how would you prevent the GlobalExceptionHandler class from becoming too large and unmaintainable?
  • How does your implementation change if you need to internationalize the error messages based on the 'Accept-Language' header of the incoming request?