Posted on: January 19, 2025 Posted by: rahulgite Comments: 0

Spring Boot provides robust mechanisms for handling errors and exceptions in applications. It supports both global and local exception handling, making applications more user-friendly and maintainable.


1. What is Error and Exception Handling?

Error and exception handling involves managing application failures gracefully. Spring Boot provides a default error response mechanism and allows customization through controllers, @ControllerAdvice, and exception handlers.


2. Default Error Handling in Spring Boot

Spring Boot automatically provides a default error response for exceptions.

Default JSON Response:

{
    "timestamp": "2025-01-19T10:00:00.000+00:00",
    "status": 500,
    "error": "Internal Server Error",
    "message": "NullPointerException",
    "path": "/api/resource"
}

This response is generated by the BasicErrorController class.


3. Step-by-Step: Setting Up Global Exception Handling

Step 1: Create a Custom Exception Class

public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

Step 2: Implement @ControllerAdvice for Global Handling

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<String> handleResourceNotFound(ResourceNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGenericException(Exception ex) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occurred: " + ex.getMessage());
    }
}

Step 3: Customizing Error Response Attributes

Extend the DefaultErrorAttributes class to modify default error attributes.

@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
        Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
        errorAttributes.put("customMessage", "A custom error occurred");
        errorAttributes.put("developerMessage", "Contact support with this error ID");
        return errorAttributes;
    }
}

4. Setting Up Local Exception Handling

Example: Using @ExceptionHandler in a Controller

@RestController
@RequestMapping("/api")
public class ResourceController {

    @GetMapping("/resource/{id}")
    public String getResource(@PathVariable int id) {
        if (id <= 0) {
            throw new ResourceNotFoundException("Resource not found with id: " + id);
        }
        return "Resource data";
    }

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<String> handleResourceNotFound(ResourceNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
    }
}

5. Custom Error Pages

Spring Boot allows serving custom error pages for specific HTTP status codes.

Steps to Configure Custom Error Pages:

  1. Place an HTML file in the src/main/resources/templates/error folder.
  2. Name the file as error-<status-code>.html.

Example: Custom 404 Error Page

File: error-404.html

<!DOCTYPE html>
<html>
<head>
    <title>Page Not Found</title>
</head>
<body>
    <h1>404 - Page Not Found</h1>
    <p>The requested page does not exist.</p>
</body>
</html>

6. Using @ResponseStatus

Map exceptions to HTTP status codes directly using @ResponseStatus.

Example:

@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

7. Logging Exceptions

Log exceptions using SLF4J with Logback for debugging and monitoring.

Example:

@RestController
@RequestMapping("/api")
public class LoggingController {

    private static final Logger logger = LoggerFactory.getLogger(LoggingController.class);

    @GetMapping("/test")
    public String testLogging() {
        try {
            throw new RuntimeException("Simulated exception");
        } catch (Exception ex) {
            logger.error("An error occurred: ", ex);
            throw ex;
        }
    }
}

8. Customizing Exception Handling for Applications

Customize Exception Details:

You can use the @ExceptionHandler to add details like error codes, timestamps, or developer messages.

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<Map<String, Object>> handleResourceNotFound(ResourceNotFoundException ex) {
    Map<String, Object> errorDetails = new HashMap<>();
    errorDetails.put("timestamp", LocalDateTime.now());
    errorDetails.put("message", ex.getMessage());
    errorDetails.put("errorCode", "ERR404");

    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorDetails);
}

Custom Error Response Object:

Create a custom response object to standardize error responses.

public class ErrorResponse {
    private LocalDateTime timestamp;
    private String message;
    private String details;

    public ErrorResponse(LocalDateTime timestamp, String message, String details) {
        this.timestamp = timestamp;
        this.message = message;
        this.details = details;
    }

    // Getters and setters
}

Use it in exception handling:

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex, WebRequest request) {
    ErrorResponse errorResponse = new ErrorResponse(
        LocalDateTime.now(),
        ex.getMessage(),
        request.getDescription(false)
    );
    return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}

9. Best Practices for Exception Handling

  1. Use Custom Exceptions: Define meaningful custom exceptions for application-specific errors.
  2. Centralize Exception Handling: Use @ControllerAdvice to manage exceptions globally.
  3. Provide Consistent Responses: Standardize error response structures using DTOs or error classes.
  4. Avoid Leaking Internal Details: Do not expose sensitive implementation details in error messages.
  5. Log Exceptions: Always log exceptions for debugging and analysis.
  6. Test Error Scenarios: Write test cases to ensure proper handling of various exceptions.

Let me know if further examples or details are required!

Leave a Comment