当前位置:  开发笔记 > 编程语言 > 正文

如何管理Spring中过滤器抛出的异常?

如何解决《如何管理Spring中过滤器抛出的异常?》经验,为你挑选了6个好方法。

我想使用通用方法来管理5xx错误代码,让我们具体说明db在整个spring应用程序中出现故障的情况.我想要一个漂亮的错误json而不是堆栈跟踪.

对于控制器,我有一个@ControllerAdvice用于不同异常的类,这也是捕获db在请求中间停止的情况.但这并不是全部.我也碰巧有一个自定义CorsFilter扩展OncePerRequestFilter,当我打电话给doFilter我得到CannotGetJdbcConnectionException它,它将不会被管理@ControllerAdvice.我在线阅读了几件让我更加困惑的事情.

所以我有很多问题:

我需要实现自定义过滤器吗?我找到了,ExceptionTranslationFilter但这只是处理AuthenticationExceptionAccessDeniedException.

我想实现自己的HandlerExceptionResolver,但这让我怀疑,我没有任何自定义异常来管理,必须有一个比这更明显的方法.我还尝试添加一个try/catch并调用一个实现HandlerExceptionResolver(应该足够好,我的异常没什么特别的)但是这不会在响应中返回任何内容,我得到一个状态200和一个空体.

有什么好方法可以解决这个问题吗?谢谢



1> kopelitsa..:

所以这就是我做的:

我在这里阅读了有关过滤器的基础知识,并且我发现我需要创建一个自定义过滤器,该过滤器将首先位于过滤器链中,并且将有一个try catch来捕获可能在那里发生的所有运行时异常.然后我需要手动创建json并将其放入响应中.

所以这是我的自定义过滤器:

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

然后我在之前的web.xml中添加了它CorsFilter.它的工作原理!

 
    exceptionHandlerFilter 
    xx.xxxxxx.xxxxx.api.controllers.filters.ExceptionHandlerFilter 
 


 
    exceptionHandlerFilter 
    /* 
 

 
    CorsFilter 
    org.springframework.web.filter.DelegatingFilterProxy 
 


    CorsFilter
    /*



2> Raf..:

我遇到这个问题我自己和我执行的步骤如下重用我ExceptionController被注释与@ControllerAdviseExceptions扔在注册的过滤器.

显然有很多方法可以处理异常,但在我的情况下,我希望异常由我处理,ExceptionController因为我很顽固,也因为我不想复制/粘贴相同的代码(即我有一些处理/记录代码ExceptionController).我想返回漂亮的JSON响应,就像其他不是从Filter中抛出的异常一样.

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

无论如何,我设法利用了我ExceptionHandler,我必须做一些额外的工作,如下所示:

脚步


    您有一个自定义过滤器,可能会或可能不会抛出异常

    你有一个Spring控制器,使用@ControllerAdviseieExceptionController 来处理异常

示例代码

//sample Filter, to be added in web.xml
public 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); 
     }
}

现在是Exception在正常情况下处理的示例Spring Controller (即通常不会在Filter级别引发的异常,我们要用于Filter中抛出的异常的异常)

//sample SpringController 
@ControllerAdvice
public 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
}

与希望ExceptionController用于Exceptions过滤器的人共享解决方案.


那么,欢迎您分享您自己的解决方案,听起来像这样做:)

3> holmis83..:

如果需要通用方法,可以在web.xml中定义错误页面:


  java.lang.Throwable
  /500

并在Spring MVC中添加映射:

@Controller
public class ErrorController {

    @RequestMapping(value="/500")
    public @ResponseBody String handleException(HttpServletRequest req) {
        // you can get the exception thrown
        Throwable t = (Throwable)req.getAttribute("javax.servlet.error.exception");

        // customize response to what you want
        return "Internal server error.";
    }
}



4> 小智..:

所以,这就是我基于上述答案的合并而做的...我们已经有了GlobalExceptionHandler注释,@ControllerAdvice我也想找到一种方法来重用这些代码来处理来自过滤器的异常.

我能找到的最简单的解决方案是单独留下异常处理程序,并按如下方式实现错误控制器:

@Controller
public class ErrorControllerImpl implements ErrorController {
  @RequestMapping("/error")
  public void handleError(HttpServletRequest request) throws Throwable {
    if (request.getAttribute("javax.servlet.error.exception") != null) {
      throw (Throwable) request.getAttribute("javax.servlet.error.exception");
    }
  }
}

因此,由异常引起的任何错误首先通过ErrorController并通过从@Controller上下文中重新抛出它们而被重定向到异常处理程序,而任何其他错误(不是由异常直接引起)都会通过ErrorController而不进行修改.

为什么这实际上是一个坏主意的任何原因?



5> ssc-hrep3..:

我想根据@kopelitsa的答案提供解决方案。主要区别在于:

    通过使用重用控制器异常处理HandlerExceptionResolver

    在XML配置上使用Java配置


首先,您需要确保您有一个类来处理在常规RestController / Controller中发生的异常(用@RestControllerAdvice或标记的类@ControllerAdvice和和用标记的方法@ExceptionHandler)。这可以处理您在控制器中发生的异常。这是使用RestControllerAdvice的示例:

@RestControllerAdvice
public 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) {
        (...)
    }
}

要在Spring Security过滤器链中重用此行为,您需要定义一个Filter并将其挂钩到您的安全配置中。筛选器需要将异常重定向到上面定义的异常处理。这是一个例子:

@Component
public 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);
        }
    }
}

然后需要将创建的过滤器添加到SecurityConfiguration。您需要非常早地将其挂接到链中,因为不会捕获所有先前过滤器的异常。就我而言,在之前添加它是合理的LogoutFilter。请参阅官方文档中的默认过滤器链及其顺序。这是一个例子:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private FilterChainExceptionHandler filterChainExceptionHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .addFilterBefore(filterChainExceptionHandler, LogoutFilter.class)
            (...)
    }

}



6> walv..:

这是我的解决方案,通过覆盖默认的Spring Boot /错误处理程序

package com.mypackage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * This controller is vital in order to handle exceptions thrown in Filters.
 */
@RestController
@RequestMapping("/error")
public class ErrorController implements org.springframework.boot.autoconfigure.web.ErrorController {

    private final static Logger LOGGER = LoggerFactory.getLogger(ErrorController.class);

    private final ErrorAttributes errorAttributes;

    @Autowired
    public ErrorController(ErrorAttributes errorAttributes) {
        Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
        this.errorAttributes = errorAttributes;
    }

    @Override
    public String getErrorPath() {
        return "/error";
    }

    @RequestMapping
    public ResponseEntity> error(HttpServletRequest aRequest, HttpServletResponse response) {
        RequestAttributes requestAttributes = new ServletRequestAttributes(aRequest);
        Map result =     this.errorAttributes.getErrorAttributes(requestAttributes, false);

        Throwable error = this.errorAttributes.getError(requestAttributes);

        ResponseStatus annotation =     AnnotationUtils.getAnnotation(error.getClass(), ResponseStatus.class);
        HttpStatus statusCode = annotation != null ? annotation.value() : HttpStatus.INTERNAL_SERVER_ERROR;

        result.put("status", statusCode.value());
        result.put("error", statusCode.getReasonPhrase());

        LOGGER.error(result.toString());
        return new ResponseEntity<>(result, statusCode) ;
    }

}

推荐阅读
拾味湖
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有