如何强制 Spring Boot 设置正确的 HTTP 状态代码来响应错误?
How to force Spring Boot to set correct HTTP status code to error response?
我制作了这个简单的 Spring 启动应用程序示例来展示我的问题 Spring 默认情况下启动处理错误的方式:
package com.pany.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
这是 Spring 安全配置:
package com.pany.app;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").anonymous();
}
}
这是 REST 控制器:
package com.pany.app;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
@RestController
public class MyController {
@GetMapping(path = "/", produces = { "application/json" })
public void home() {
throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "This is my custom 400 Bad Request message");
}
}
现在,使用 Spring Boot 2.1.8.RELEASE
,当调用 URL http://localhost:8080/
时,我收到一个包含 500 代码的 HTTP 响应和以下 JSON 响应:
{
"timestamp": "2020-09-14T07:24:22.061+0000",
"status": 500,
"error": "Internal Server Error",
"message": "400 This is my custom 400 Bad Request message",
"path": "/"
}
但是 HTTP 状态代码并不像预期的那样(500 而不是 400),以及 JSON 中的各个字段(“状态”、“错误”、“消息”具有意外的“代码” “在它前面)。
我也用 Spring Boot 2.3.3.RELEASE
测试过这个...现在它甚至不再像以前那样工作了!!
{
"timestamp": "2020-09-14T07:34:23.154+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "",
"path": "/"
}
这一切都是通过ErrorController
、AbstractErrorController
和BasicErrorController
等classes/interfaces处理的(都在ErrorMvcAutoConfiguration
中设置)。显然有一个重定向到 URL /error
,原始请求/and/or 响应被包装到其他东西中,例如我可以像这样调试我的请求:
SecurityContextHolderAwareRequestWrapper[FirewalledRequest[org.apache.catalina.core.ApplicationHttpRequest@3751bea4]]
我发现我得到的 JSON 是通过一个名为 DefaultErrorAttributes
的 bean 生成的,它从键 "javax.servlet.error.status_code"
下的请求中提取 HTTP 状态代码。
我在那里读到:https://github.com/spring-projects/spring-boot/issues/4694#issuecomment-163597864(2015 年 12 月):
The problem's actually in BasicErrorController and is specific to HTML responses. BasicErrorController.error sets the response status using the javax.servlet.error.status_code attribute that's set by ErrorPageFilter, but BasicErrorController.errorHtml does not. This results in text/html error responses have a status of 200 OK.
事实上,在我的例子中,ErrorPageFilter
从未被调用过。
还在 Spring 引导项目上发现了这个问题:https://github.com/spring-projects/spring-boot/issues/20412 这与我的问题很相关,但仍然没有提供解决方案。
回到我的控制器是如何设计的(记住这只是一个极其简化的例子):如何“定制”Spring 引导以获得代码为 400 的 HTTP 响应和这个 JSON 作为回应:
{
"timestamp": "2020-09-14T07:24:22.061+0000",
"status": 400,
"error": "Bad Request",
"message": "This is my custom 400 Bad Request message",
"path": "/"
}
谢谢。
进行自定义错误响应的最简单方法是添加控制器通知 class,它将捕获并处理异常。
@ControllerAdvice
public class ControllerAdvice {
@ResponseBody
@ExceptionHandler(value = HttpClientErrorException.class) {
public ResponseEntity<ExceptionResponseBody> genericException(HttpClientErrorException exception) {
// Build and return your response
您可以从异常中获取代码和消息以构建响应。您还可以添加更多处理程序来捕获和处理您想要的所有错误。
@Controlleradvice
的一个简单替代方法是自定义异常如下
@Slf4j
@ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException{
public NotFoundException(String message) {
super(message);
log.warn(message);
}
}
使用带有 @ResponseStatus
注释的所需 Http 代码。
并在需要时抛出此异常
throw new NotFoundException("Your message for not found goes here");
我制作了这个简单的 Spring 启动应用程序示例来展示我的问题 Spring 默认情况下启动处理错误的方式:
package com.pany.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
这是 Spring 安全配置:
package com.pany.app;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").anonymous();
}
}
这是 REST 控制器:
package com.pany.app;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
@RestController
public class MyController {
@GetMapping(path = "/", produces = { "application/json" })
public void home() {
throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "This is my custom 400 Bad Request message");
}
}
现在,使用 Spring Boot 2.1.8.RELEASE
,当调用 URL http://localhost:8080/
时,我收到一个包含 500 代码的 HTTP 响应和以下 JSON 响应:
{
"timestamp": "2020-09-14T07:24:22.061+0000",
"status": 500,
"error": "Internal Server Error",
"message": "400 This is my custom 400 Bad Request message",
"path": "/"
}
但是 HTTP 状态代码并不像预期的那样(500 而不是 400),以及 JSON 中的各个字段(“状态”、“错误”、“消息”具有意外的“代码” “在它前面)。
我也用 Spring Boot 2.3.3.RELEASE
测试过这个...现在它甚至不再像以前那样工作了!!
{
"timestamp": "2020-09-14T07:34:23.154+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "",
"path": "/"
}
这一切都是通过ErrorController
、AbstractErrorController
和BasicErrorController
等classes/interfaces处理的(都在ErrorMvcAutoConfiguration
中设置)。显然有一个重定向到 URL /error
,原始请求/and/or 响应被包装到其他东西中,例如我可以像这样调试我的请求:
SecurityContextHolderAwareRequestWrapper[FirewalledRequest[org.apache.catalina.core.ApplicationHttpRequest@3751bea4]]
我发现我得到的 JSON 是通过一个名为 DefaultErrorAttributes
的 bean 生成的,它从键 "javax.servlet.error.status_code"
下的请求中提取 HTTP 状态代码。
我在那里读到:https://github.com/spring-projects/spring-boot/issues/4694#issuecomment-163597864(2015 年 12 月):
The problem's actually in BasicErrorController and is specific to HTML responses. BasicErrorController.error sets the response status using the javax.servlet.error.status_code attribute that's set by ErrorPageFilter, but BasicErrorController.errorHtml does not. This results in text/html error responses have a status of 200 OK.
事实上,在我的例子中,ErrorPageFilter
从未被调用过。
还在 Spring 引导项目上发现了这个问题:https://github.com/spring-projects/spring-boot/issues/20412 这与我的问题很相关,但仍然没有提供解决方案。
回到我的控制器是如何设计的(记住这只是一个极其简化的例子):如何“定制”Spring 引导以获得代码为 400 的 HTTP 响应和这个 JSON 作为回应:
{
"timestamp": "2020-09-14T07:24:22.061+0000",
"status": 400,
"error": "Bad Request",
"message": "This is my custom 400 Bad Request message",
"path": "/"
}
谢谢。
进行自定义错误响应的最简单方法是添加控制器通知 class,它将捕获并处理异常。
@ControllerAdvice
public class ControllerAdvice {
@ResponseBody
@ExceptionHandler(value = HttpClientErrorException.class) {
public ResponseEntity<ExceptionResponseBody> genericException(HttpClientErrorException exception) {
// Build and return your response
您可以从异常中获取代码和消息以构建响应。您还可以添加更多处理程序来捕获和处理您想要的所有错误。
@Controlleradvice
的一个简单替代方法是自定义异常如下
@Slf4j
@ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException{
public NotFoundException(String message) {
super(message);
log.warn(message);
}
}
使用带有 @ResponseStatus
注释的所需 Http 代码。
并在需要时抛出此异常
throw new NotFoundException("Your message for not found goes here");