How to manage exceptions thrown in filters in Spring? How to manage exceptions thrown in filters in Spring? spring spring

How to manage exceptions thrown in filters in Spring?


So this is what I did:

I read the basics about filters here and I figured out that I need to create a custom filter that will be first in the filter chain and will have a try catch to catch all runtime exceptions that might occur there. Then i need to create the json manually and put it in the response.

So here is my custom filter:

public class ExceptionHandlerFilter extends OncePerRequestFilter {    @Override    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {        try {            filterChain.doFilter(request, response);        } catch (RuntimeException e) {            // custom error response class used across my project            ErrorResponse errorResponse = new ErrorResponse(e);            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());            response.getWriter().write(convertObjectToJson(errorResponse));    }}    public String convertObjectToJson(Object object) throws JsonProcessingException {        if (object == null) {            return null;        }        ObjectMapper mapper = new ObjectMapper();        return mapper.writeValueAsString(object);    }}

And then i added it in the web.xml before the CorsFilter. And it works!

<filter>     <filter-name>exceptionHandlerFilter</filter-name>     <filter-class>xx.xxxxxx.xxxxx.api.controllers.filters.ExceptionHandlerFilter</filter-class> </filter> <filter-mapping>     <filter-name>exceptionHandlerFilter</filter-name>     <url-pattern>/*</url-pattern> </filter-mapping> <filter>     <filter-name>CorsFilter</filter-name>     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping>    <filter-name>CorsFilter</filter-name>    <url-pattern>/*</url-pattern></filter-mapping>


I wanted to provide a solution based on the answer of @kopelitsa. The main differences being:

  1. Reusing the controller exception handling by using the HandlerExceptionResolver.
  2. Using Java config over XML config

First, you need to make sure, that you have a class that handles exceptions occurring in a regular RestController/Controller (a class annotated with @RestControllerAdvice or @ControllerAdvice and method(s) annotated with @ExceptionHandler). This handles your exceptions occurring in a controller. Here is an example using the RestControllerAdvice:

@RestControllerAdvicepublic class ExceptionTranslator {    @ExceptionHandler(RuntimeException.class)    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)    public ErrorDTO processRuntimeException(RuntimeException e) {        return createErrorDTO(HttpStatus.INTERNAL_SERVER_ERROR, "An internal server error occurred.", e);    }    private ErrorDTO createErrorDTO(HttpStatus status, String message, Exception e) {        (...)    }}

To reuse this behavior in the Spring Security filter chain, you need to define a Filter and hook it into your security configuration. The filter needs to redirect the exception to the above defined exception handling. Here is an example:

@Componentpublic class FilterChainExceptionHandler extends OncePerRequestFilter {    private final Logger log = LoggerFactory.getLogger(getClass());    @Autowired    @Qualifier("handlerExceptionResolver")    private HandlerExceptionResolver resolver;    @Override    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)            throws ServletException, IOException {        try {            filterChain.doFilter(request, response);        } catch (Exception e) {            log.error("Spring Security Filter Chain Exception:", e);            resolver.resolveException(request, response, null, e);        }    }}

The created filter then needs to be added to the SecurityConfiguration. You need to hook it into the chain very early, because all preceding filter's exceptions won't be caught. In my case, it was reasonable to add it before the LogoutFilter. See the default filter chain and its order in the official docs. Here is an example:

@Configuration@EnableWebSecuritypublic class SecurityConfiguration extends WebSecurityConfigurerAdapter {    @Autowired    private FilterChainExceptionHandler filterChainExceptionHandler;    @Override    protected void configure(HttpSecurity http) throws Exception {        http            .addFilterBefore(filterChainExceptionHandler, LogoutFilter.class)            (...)    }}


I come across this issue myself and I performed the steps below to reuse my ExceptionController that is annotated with @ControllerAdvise for Exceptions thrown in a registered Filter.

There are obviously many ways to handle exception but, in my case, I wanted the exception to be handled by my ExceptionController because I am stubborn and also because I don't want to copy/paste the same code (i.e. I have some processing/logging code in ExceptionController). I would like to return the beautiful JSON response just like the rest of the exceptions thrown not from a Filter.

{  "status": 400,  "message": "some exception thrown when executing the request"}

Anyway, I managed to make use of my ExceptionHandler and I had to do a little bit of extra as shown below in steps:

Steps


  1. You have a custom filter that may or may not throw an exception
  2. You have a Spring controller that handles exceptions using @ControllerAdvise i.e. MyExceptionController

Sample code

//sample Filter, to be added in web.xmlpublic MyFilterThatThrowException implements Filter {   //Spring Controller annotated with @ControllerAdvise which has handlers   //for exceptions   private MyExceptionController myExceptionController;    @Override   public void destroy() {        // TODO Auto-generated method stub   }   @Override   public void init(FilterConfig arg0) throws ServletException {       //Manually get an instance of MyExceptionController       ApplicationContext ctx = WebApplicationContextUtils                  .getRequiredWebApplicationContext(arg0.getServletContext());       //MyExceptionHanlder is now accessible because I loaded it manually       this.myExceptionController = ctx.getBean(MyExceptionController.class);    }   @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)            throws IOException, ServletException {        HttpServletRequest req = (HttpServletRequest) request;        HttpServletResponse res = (HttpServletResponse) response;        try {           //code that throws exception        } catch(Exception ex) {          //MyObject is whatever the output of the below method          MyObject errorDTO = myExceptionController.handleMyException(req, ex);           //set the response object          res.setStatus(errorDTO .getStatus());          res.setContentType("application/json");          //pass down the actual obj that exception handler normally send          ObjectMapper mapper = new ObjectMapper();          PrintWriter out = res.getWriter();           out.print(mapper.writeValueAsString(errorDTO ));          out.flush();          return;         }        //proceed normally otherwise        chain.doFilter(request, response);      }}

And now the sample Spring Controller that handles Exception in normal cases (i.e. exceptions that are not usually thrown in Filter level, the one we want to use for exceptions thrown in a Filter)

//sample SpringController @ControllerAdvicepublic class ExceptionController extends ResponseEntityExceptionHandler {    //sample handler    @ResponseStatus(value = HttpStatus.BAD_REQUEST)    @ExceptionHandler(SQLException.class)    public @ResponseBody MyObject handleSQLException(HttpServletRequest request,            Exception ex){        ErrorDTO response = new ErrorDTO (400, "some exception thrown when "                + "executing the request.");         return response;    }    //other handlers}

Sharing the solution with those who wish to use ExceptionController for Exceptions thrown in a Filter.