exception handling for filter in spring
Filters happens before controllers are even resolved so exceptions thrown from filters can't be caught by a Controller Advice.
Filters are a part of the servlet and not really the MVC stack.
As the exception is not raised from a controller but a filter, @ControllerAdvice won't catch it.
So, the best solution i found after looking everywhere was to create a filter for this internal errors:
public class ExceptionHandlerFilter extends OncePerRequestFilter { @Override public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { filterChain.doFilter(request, response); } catch (JwtException e) { setErrorResponse(HttpStatus.BAD_REQUEST, response, e); e.printStackTrace(); } catch (RuntimeException e) { e.printStackTrace(); setErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, response, e); } } public void setErrorResponse(HttpStatus status, HttpServletResponse response, Throwable ex){ response.setStatus(status.value()); response.setContentType("application/json"); // A class used for errors ApiError apiError = new ApiError(status, ex); try { String json = apiError.convertToJson(); System.out.println(json); response.getWriter().write(json); } catch (IOException e) { e.printStackTrace(); } }}
Add it to your config, i'm using a WebSecurityConfigurerAdapter implementation:
// Custom JWT based security filterhttpSecurity .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);// Custom Exception Filter for filterhttpSecurity .addFilterBefore(exceptionHandlerFilterBean(), JwtAuthenticationTokenFilter.class);
The error class:
public class ApiError { private HttpStatus status; @JsonSerialize(using = LocalDateTimeSerializer.class) @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss") private LocalDateTime timestamp; private String message; private String debugMessage; private ApiError() { timestamp = LocalDateTime.now(); } public ApiError(HttpStatus status) { this(); this.status = status; } public ApiError(HttpStatus status, Throwable ex) { this(); this.status = status; this.message = "Unexpected error"; this.debugMessage = ex.getLocalizedMessage(); } public ApiError(HttpStatus status, String message, Throwable ex) { this(); this.status = status; this.message = message; this.debugMessage = ex.getLocalizedMessage(); } public String convertToJson() throws JsonProcessingException { if (this == null) { return null; } ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return mapper.writeValueAsString(this); } //Setters and getters}
Presumably, you want to set the HTTP Status code as a result of the exception being thrown in the Filter? If so, simply set the status as follows:
HttpServletResponse response = (HttpServletResponse) res;response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);