我有一个带有返回文本/csv 方法的 Controller 。这适用于正常的成功案例,但如果抛出异常,并且我有 Accept: text/csv
header ,我会收到 406 响应。例如:
@RequestMapping(value = "/foo", method = RequestMethod.GET, produces = "text/csv")
public String getCsv() {
throw new IllegalArgumentException();
}
这是在一个完全普通的 Spring Boot 应用程序(Maven 项目,导入 spring-boot-starter-web-services
)中,只包含一个具有上述方法的 Controller 。
我假设原因是框架正在将异常转换为 JSON 错误响应。如果我删除 produces
属性并发送 Accept: */*
我会得到异常的 JSON 表示。显然 JSON 不是 text/csv
,因此是 406( Not Acceptable )响应。
这是显示问题的 curl 请求/响应示例:
curl -v http://localhost:8080/foo -H 'accept: text/csv'
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /foo HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.47.0
> accept: text/csv
>
< HTTP/1.1 406
< X-Application-Context: application
< Content-Length: 0
< Date: Sat, 16 Dec 2017 23:04:05 GMT
<
* Connection #0 to host localhost left intact
但是,有趣的是,如果我查看我的 Spring 应用程序中的 /trace
端点,我会看到一些不同的东西:
{
"timestamp": 1513465445542,
"info": {
"method": "GET",
"path": "/foo",
"headers": {
"request": {
"host": "localhost:8080",
"user-agent": "curl/7.47.0",
"accept": "text/csv"
},
"response": {
"X-Application-Context": "application",
"status": "500"
}
},
"timeTaken": "1"
}
}
因此,Spring 认为它会返回 500,但当它开始 curl 时,它会返回 406。如果我从 PostMan 发送请求,我会看到完全相同的结果。
我不确定是什么导致了从 500 到 406 的变化。我假设它不是客户端,所以我最好的猜测是 Tomcat 正在做这件事。有什么办法可以阻止这种情况发生吗?还是我遗漏了其他一些可能性?
最佳答案
==== 原始答案(解释预期行为)====
Accept
header 指定客户端希望服务器响应的格式类型。对此的任何差异都会导致 HTTP 406 - Not Acceptable
错误。然而,此错误并不意味着操作失败,而是表明客户端对指定格式的期望失败。
在您的情况下,Accept
header 带有 text/csv
但服务器响应 application/json
,因此 406
错误,因为存在明显的不匹配。
要纠正此行为,不需要在服务器/ Spring 端进行任何更改。相反,客户端应该开始发送 Accept
header ,该 header 将携带值作为 application/json,text/csv
。这将确保客户端期望两种格式并在有效/错误响应的情况下支持它们。
引用here了解更多详情。
编辑 2017 年 12 月 22 日
观察到的行为被 Spring 团队确认为错误 here .尚无已知的解决方法。
编辑 2018 年 1 月 4 日
如 Spring JIRA comments 中所述作为解决方法,我们需要删除 @RestControllerAdvice
中的 HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
请求属性。代码可能如下所示(返回带有一些“信息”的 500——还返回对象的序列化版本)。
休息 Controller 建议
@RestControllerAdvice
public class ExampleControllerAdvice {
@ExceptionHandler(value = Exception.class)
public ResponseEntity<ErrorResponse> handleException(HttpServletRequest request, Exception e) {
ErrorResponse response = new ErrorResponse();
response.setErrorMsg("Server error " + e); // or whatever you want
response.setErrorCode("ERROR007"); // or whatever you want
request.removeAttribute(
HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
return new ResponseEntity<ErrorResponse>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
ErrorResponse 对象
public class ErrorResponse {
private String errorCode;
private String errorMsg;
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
编辑 2019 年 6 月 27 日
这是 fixed现在在 Spring 框架中。请求属性 HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
现在在处理异常之前由 Spring 自动删除。
关于java - 406 当在 Spring Controller 中抛出异常并接受文本/csv header 时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47831530/