java - 如何使用 Boot 仅更改 Spring MVC 错误上的状态代码?

标签 java spring spring-boot spring-mvc controller-advice

我正在编写一个使用 RestTemplate 进行下游调用的 Web 应用程序。如果底层服务返回401 Unauthorized,我想也返回一个401给调用应用程序;默认行为是返回 500。我想保留由 BasicErrorController 提供的默认 Spring Boot 错误响应。 ;我想要的唯一更改是设置状态代码。
在自定义异常中,我只是用 @ResponseStatus 注释异常类。 ,但我不能在这里这样做,因为 HttpClientErrorException.Unauthorized由 Spring 提供。我用 @ControllerAdvice 尝试了两种方法:

@ExceptionHandler(HttpClientErrorException.Unauthorized.class)
@ResponseStatus(UNAUTHORIZED)
public void returnsEmptyBody(HttpClientErrorException.Unauthorized ex) {
}

@ExceptionHandler(HttpClientErrorException.Unauthorized.class)
@ResponseStatus(UNAUTHORIZED)
public void doesNotUseBasicErrorController(HttpClientErrorException.Unauthorized ex) {
    throw new RuntimeException(ex);
}
除了显式覆盖状态代码之外,如何配置 MVC 以继续使用所有内置引导错误处理?

最佳答案

下面的代码对我有用——在一个由 @RestController 组成的应用程序中其一种方法包括 throw new HttpClientException(HttpStatus.UNAUTHORIZED) ,在嵌入式 Tomcat 上运行。如果您在非嵌入式 Tomcat(或者,我怀疑,在嵌入式非 Tomcat 上)上运行,您可能必须做一些至少有些不同的事情,但我希望这个答案至少有一定的帮助。

@ControllerAdvice
public class Advisor {
  @ExceptionHandler(HttpClientException.class)
  public String handleUnauthorizedFromApi(HttpClientException ex, HttpServletRequest req) {
    if (/* ex instanceof HttpClientException.Unauthorized or whatever */) {
      req.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, 401);
    }
    return "forward:/error";
  }
}
解释:当我们在处理请求 X(在嵌入式 servlet 中)时抛出 HttpClientException 时,通常发生的是它一直冒泡到某个 org.apache 类。 (我可能会再次启动调试器并计算出哪个,但这是一个非常高级的解释,所以它并不重要。)然后该类将请求 X 发送回应用程序,除了这次请求转到“/error”,而不是它最初要去的地方。在 Spring Boot 应用程序中(只要您不关闭某些自动配置),这意味着请求 X 最终会由 BasicErrorController 中的某个方法处理。 .
好的,那么为什么除非我们做点什么,否则整个系统会向客户端发送 500?因为上面提到的 org.apache 类在请求 X 上设置了一些内容,上面写着“处理这件事出错了”。这样做是正确的:毕竟,处理请求 X 确实导致了 servlet 容器必须捕获的异常。就容器而言,应用程序一团糟。
所以我们想做几件事。首先,我们希望 servlet 容器不会认为我们搞砸了。我们通过告诉 Spring 在它到达容器之前捕获异常来实现这一点,即通过编写 @ExceptionHandler方法。其次,即使我们捕获了异常,我们也希望请求转到“/error”。我们通过一个简单的方法将它自己发送到那里,通过转发来实现这一点。第三,我们想要BasicErrorController在它发送的响应上设置正确的状态和消息。原来BasicErrorController (与其直接父类(super class)协同工作)查看请求上的属性以确定要发送给客户端的状态代码。 (弄清楚这一点需要阅读类的源代码,但该源代码在 github 上并且完全可读。)因此我们设置了该属性。
编辑:写这篇文章时我有点忘乎所以,忘了提到我认为使用这段代码不是很好的做法。它将您与 BasicErrorController 的一些实现细节联系起来。 ,而且这不是 Boot 类的预期使用方式。 Spring Boot 通常假设您希望它完全或根本不处理您的错误;这也是一个合理的假设,因为零碎的错误处理通常不是一个好主意。我给你的建议——即使上面的代码(或类似的东西)最终能工作——是写一个 @ExceptionHandler完全处理错误,这意味着它设置状态和响应主体并且不转发任何内容。

关于java - 如何使用 Boot 仅更改 Spring MVC 错误上的状态代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63892438/

相关文章:

c# - 公共(public)内部类

java - 适配器类实现并创建 MouseAdapter 实例

java - Spring安全单点登录

spring - 在 Spock 规范中注入(inject)时,WebApplicationContext 不会 Autowiring

java - Spring - 验证字段的字段

java - 我如何使用 spring 使用带参数的消息

c# - 使用 C#(或 Java)实现日志查看器 GUI

java - 使用 AbstractRoutingDataSource 动态更改数据库架构/目录

java - Spring Boot 升级错误 - 名称为 org.springframework.transaction.config.internalTransactionalEventListenerFactory 的无效 bean 定义

java - 如何将外部jar文件传递到spring boot项目?