Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

exception handling for filter in spring

I am handling exceptions in spring using @ExceptionHandler. Any exception thrown by controller is caught using method annotated with @ExceptionHandler and action is taken accordingly. To avoid writing @exceptionHandler for every controller i am using @ControllerAdvice annotation.

Everything works fine as expected.

Now i have a filter(Yes, not interceptor, to handle certain requirement) which is implemented using DelegatingFilterProxy and ContextLoaderListener.

When i am throwing the same exception from above filter, its not caught the way it was done in controller case. It is directly thrown to user.

What's wrong in here?

like image 220
Rohit Jain Avatar asked Jul 18 '13 06:07

Rohit Jain


2 Answers

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.

like image 103
Andreas Wederbrand Avatar answered Oct 27 '22 08:10

Andreas Wederbrand


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 filter
httpSecurity
        .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);

// Custom Exception Filter for filter
httpSecurity
        .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
}
like image 28
Richard Avatar answered Oct 27 '22 09:10

Richard