无法从Spring Cloud自定义过滤器中调用@ControllerAdvice处理程序

时间:2019-04-01 08:14:20

标签: spring-boot exception filter spring-cloud

我创建了以下自定义过滤器,用于Spring Cloud应用程序中的授权。这是该过滤器中的apply()方法,如果授权检查失败,则会引发异常:

@Override
public GatewayFilter apply(Config config) {
    return (exchange, chain) -> {
        HttpStatus status = HttpStatus.FORBIDDEN;

        try {
            String authRequest = exchange.getRequest().getHeaders().getFirst(
                 Constants.SESSION_HEADER_NAME);
            HttpHeaders headers = new HttpHeaders();
            HttpEntity<String> entity = new HttpEntity<String>(authRequest, headers);
            // make REST call to authorization module
            status = restTemplate.postForEntity(authURL, entity, String.class).getStatusCode();
        }
        catch (Exception e) {
            LOGGER.error("Something went wrong during authorization", e);
        }

        // throw an exception if anything went
        // wrong with authorization
        if (!HttpStatus.OK.equals(status)) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN);
        }

        return chain.filter(exchange);
    };
}

我定义了以下@ControllerAdvice类来捕获上述网关云过滤器引发的所有异常:

@ControllerAdvice
public class RestExceptionHandler {
    @ExceptionHandler(value = ResponseStatusException.class)
    public final ResponseEntity<Object> handleException(ResponseStatusException ex) {
        return new ResponseEntity<>("UNAUTHORIZED", HttpStatus.FORBIDDEN);
    }
}

我目前观察到的是以下情况:

  • 上述自定义过滤器的ResponseStatusException不会被@ControllerAdvice映射方法捕获。
  • 但是,从Spring Boot应用程序的其他地方抛出此异常,例如我们用作身份验证端点的常规控制器 @ControllerAdvice方法捕获。

目前,这比阻止程序更令人讨厌,因为实际上从Cloud过滤器中抛出了ResponseStatusException,并带有自定义错误代码,实际上确实将错误代码返回给了调用者。但是,最好在一个地方处理所有异常。

有人可以阐明这个问题吗?

2 个答案:

答案 0 :(得分:0)

来自ControllerAdvice javadocs

  

带有@ControllerAdvice的类可以显式声明为Spring Bean或通过类路径扫描自动检测。

您没有为过滤器显示完整的类,但是我敢打赌它不是在类路径上扫描的Spring Bean。通常,servlet过滤器显式地插入到Spring Security配置中。因此,ControllerAdvice处理将忽略它。

答案 1 :(得分:0)

我认为“过滤器”是指javax.servlet.Filter

在这种情况下,@ControllerAdvice无法正常工作。它用于处理来自控制器的异常。但是您在Exception甚至无法传播到Controller之前就抛出了异常(通过不调用chain.filter(exchange)方法。

尝试在控制器而不是过滤器中引发异常。

编辑::如果您不想处理@Controller上的异常,则必须直接在javax.servlet.Filter中实现终端处理程序。这意味着可以像这样直接更改传入请求的响应:

HttpServletResponse httpResponse = (HttpServletResponse) exchange.getResponse();

// either format the response by yourself
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.setHeader(...)
...

// or let populate error directly
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);

... @ControllerAdvice内部执行的操作。