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

2022-08-31 06:54:23

我想使用通用方法来管理5xx错误代码,具体来说,当数据库在我的整个弹簧应用程序中关闭时。我想要一个非常错误的json而不是堆栈跟踪。

对于控制器,我有一个用于不同异常的类,这也捕获了db在请求中间停止的情况。但这还不是全部。我也碰巧有一个自定义扩展,当我调用时,我得到的,它不会被管理。我在网上读了几件事,这些东西只会让我更加困惑。@ControllerAdviceCorsFilterOncePerRequestFilterdoFilterCannotGetJdbcConnectionException@ControllerAdvice

所以我有很多问题:

  • 我是否需要实现自定义过滤器?我找到了,但这只处理或.ExceptionTranslationFilterAuthenticationExceptionAccessDeniedException
  • 我想过实现我自己的,但这让我怀疑,我没有任何自定义例外来管理,一定有比这更明显的方法。我还尝试添加一个 try/catch 并调用的实现(应该足够好,我的例外没什么特别的),但这在响应中没有返回任何内容,我得到一个状态 200 和一个空的正文。HandlerExceptionResolverHandlerExceptionResolver

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


答案 1

这就是我所做的:

我在这里阅读了有关筛选器的基础知识,我发现我需要创建一个自定义筛选器,该筛选器将成为筛选器链中的第一个,并将尝试捕获其中可能发生的所有运行时异常。然后我需要手动创建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);
    }
}

然后我把它添加到网络中.xml之前.它的工作原理!CorsFilter

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

答案 2

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

  1. 通过使用 重用控制器异常处理。HandlerExceptionResolver
  2. 使用 Java config over XML config

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

@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 安全筛选器链中重用此行为,需要定义一个筛选器并将其挂接到安全配置中。筛选器需要将异常重定向到上述定义的异常处理。下面是一个示例:

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

然后,需要将创建的筛选器添加到安全配置中。您需要尽早将其挂接到链中,因为不会捕获前面的所有筛选器的异常。就我而言,在 .请参阅官方文档中的默认筛选器链及其顺序。下面是一个示例: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)
            (...)
    }

}

推荐