exception handling for filter in spring

后端 未结 7 867
既然无缘
既然无缘 2020-12-17 14:59

I am handling exceptions in spring using @ExceptionHandler. Any exception thrown by controller is caught using method annotated with @ExceptionHandler and action is taken ac

相关标签:
7条回答
  • 2020-12-17 15:41

    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
    }
    
    0 讨论(0)
  • 2020-12-17 15:53

    If, like me, you're stuck with spring 3.1 (just 0.1 vesrsions behind @ControllerAdvice) you can try this solution I just came up with.


    So, you've heard of exception resolvers, right? If not, read here:

    @Component
    public class RestExceptionResolver extends ExceptionHandlerExceptionResolver {
    
        @Autowired
        //If you have multiple handlers make this a list of handlers
        private RestExceptionHandler restExceptionHandler;
        /**
         * This resolver needs to be injected because it is the easiest (maybe only) way of getting the configured MessageConverters
         */
        @Resource
        private ExceptionHandlerExceptionResolver defaultResolver;
    
        @PostConstruct
        public void afterPropertiesSet() {
            setMessageConverters(defaultResolver.getMessageConverters());
            setOrder(2); // The annotation @Order(2) does not work for this type of component
            super.afterPropertiesSet();
        }
    
        @Override
        protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
            ExceptionHandlerMethodResolver methodResolver = new ExceptionHandlerMethodResolver(restExceptionHandler.getClass());
            Method method = methodResolver.resolveMethod(exception);
            if (method != null) {
                return new ServletInvocableHandlerMethod(restExceptionHandler, method);
            }
            return null;
        }
    
        public void setRestExceptionHandler(RestExceptionHandler restExceptionHandler) {
            this.restExceptionHandler = restExceptionHandler;
        }
    
        public void setDefaultResolver(ExceptionHandlerExceptionResolver defaultResolver) {
            this.defaultResolver = defaultResolver;
        }
    }
    

    Then an example handler will look like this

    @Component
    public class RestExceptionHandler {
    
        @ExceptionHandler(ResourceNotFoundException.class)
        @ResponseStatus(HttpStatus.NOT_FOUND)
        @ResponseBody
        public Map<String, Object> handleException(ResourceNotFoundException e, HttpServletResponse response) {
            Map<String, Object> error = new HashMap<>();
            error.put("error", e.getMessage());
            error.put("resource", e.getResource());
            return error;
        }
     }
    

    Of course you will not forget to register your beens


    Then create a filter that is called before your desiered filter (optionally all of 'em)

    Then in that filter

    try{
       chain.doFilter(request, response);
    catch(Exception e){
       exceptionResolver(request, response, exceptionHandler, e);
       //Make the processing stop here... 
       return; //just in case
    }
    
    0 讨论(0)
  • 2020-12-17 15:54

    Check the below code snippet, it works for me.

    final HttpServletResponseWrapper wrapper = new 
    HttpServletResponseWrapper((HttpServletResponse) res);    
    wrapper.sendError(HttpServletResponse.SC_UNAUTHORIZED, "<your error msg>");    
    res = wrapper.getResponse();
    

    If you are using this inside a filter then add a return statement else chain.doFilter(req,res) will override this.

    0 讨论(0)
  • 2020-12-17 15:57

    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);

    0 讨论(0)
  • 2020-12-17 16:03

    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.

    0 讨论(0)
  • 2020-12-17 16:04

    I built my application with rest api, so I resolved this problem by catching it in the filter that may throw an exception and then writing something back. Remember that filterChain.doFilter(request, response); must be included.

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            // something may throw an exception
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            // ResponseWrapper is a customized class
            ResponseWrapper responseWrapper = new ResponseWrapper().fail().msg(e.getMessage());
            response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
            response.getWriter().write(JSON.toJSONString(responseWrapper));
        }
    }
    
    0 讨论(0)
提交回复
热议问题