Explain Codes LogoExplain Codes Logo

How to manage exceptions thrown in filters in Spring?

java
exception-handling
spring-framework
filter-chain
Anton ShumikhinbyAnton Shumikhin·Dec 31, 2024
TLDR

Utilize @ControllerAdvice along with @ExceptionHandler methods to handle exceptions thrown in filters.

@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity<String> handleException(Exception e) { // because exceptions love attention too return new ResponseEntity<>("Oops! Something went wrong", HttpStatus.INTERNAL_SERVER_ERROR); } }

This snippet effectively catches all exceptions, enabling a consistent response structure for filters.

Building a custom filter for exceptions

It's essential to create a custom filter that can handle exceptions. This filter serves as the first protective barrier in the filter chain, keeping errors at bay before reaching your controllers.

public class ExceptionHandlingFilter extends OncePerRequestFilter { // Our favorite mapper - helps convert objects to JSON private final ObjectMapper objectMapper = new ObjectMapper(); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { filterChain.doFilter(request, response); } catch (RuntimeException e) { // Hey there exception, gotcha! response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); ErrorResponse errorResponse = new ErrorResponse("Something broke!", e.getMessage()); response.getWriter().write(objectMapper.writeValueAsString(errorResponse)); } } public static class ErrorResponse { // All response details here. Nice and simple! private String message; private String detailedMessage; // getters and setters omitted for brevity } }

This funnel of justice ensures sensible handling of exceptions, converting them into a beautifully formatted JSON response.

Integrating filter and controller exception handling

Integration of filter exception handling with controller-level exception handling is where HandlerExceptionResolver comes into play!

public class FilterExceptionHandler extends OncePerRequestFilter { @Autowired private HandlerExceptionResolver resolver; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { filterChain.doFilter(request, response); } catch (Exception e) { // I'm going to let the big guys handle this one! resolver.resolveException(request, response, null, e); } } }

The filter now delegates the exception handling to the global @ControllerAdvice - because sharing is caring.

Don't forget about security filters

Yes, exceptions can even trip over in the midst of Security Filters. Hence, it is essential to map the error handling filters within the Spring Security configuration.

@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private ExceptionHandlingFilter exceptionHandlingFilter; @Override protected void configure(HttpSecurity http) throws Exception { http.addFilterBefore(exceptionHandlingFilter, CorsFilter.class); // Defensive enchantments in progress... } }

This assures that exceptions cropping up in the security filter phase are also captured and processed accordingly.

Uniformity is the key: Standard error response structure

Uniformity helps in maintaining calm amidst chaos. Therefore, implementing a standard error response structure across the application simplifies client-side error handling process. Here’s a basic implementation:

public class StandardErrorResponse { private int status; private String message; private long timestamp; // Constructor and getters/setters with their tiny feet shuffling around... }

Use an ObjectMapper to show your response objects some JSON love, ensuring consistency in client-server dialogue.