我有一个控制器,其方法返回text/csv.这适用于正常的成功案例,但如果抛出异常,并且我有一个标题Accept: text/csv,我得到406响应.例如:
@RequestMapping(value = "/foo", method = RequestMethod.GET, produces = "text/csv")
public String getCsv() {
throw new IllegalArgumentException();
}
Run Code Online (Sandbox Code Playgroud)
这是一个完全普通的Spring Boot应用程序(Maven项目,导入spring-boot-starter-web-services),只包含一个带有上述方法的控制器.
我假设原因是框架将异常转换为JSON错误响应.如果我删除该produces属性并发送Accept: */*我得到异常的JSON表示.显然JSON不是text/csv,因此406(不可接受)响应.
以下是显示问题的卷曲请求/响应的示例:
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
Run Code Online (Sandbox Code Playgroud)
但是,有趣的是,如果我/trace在Spring应用程序中查看端点,我会看到一些不同的东西:
{
"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"
}
}
Run Code Online (Sandbox Code Playgroud)
因此,Spring认为它返回了500,但是当它变得卷曲时,它就是406.如果我从PostMan发送请求,我会看到完全相同的事情.
我不确定是什么导致了从500到406的变化.我认为它不是客户端,所以我最好的猜测是Tomcat正在这样做.有没有办法阻止这种情况发生?还是有其他可能性我错过了?
====原始答案(解释预期的行为)====
所述Accept标头指定哪个客户端期望服务器与响应的格式类型.任何与此相关的差异都会导致HTTP 406 - Not Acceptable错误.但是,此错误并不意味着操作失败,但它表示客户端对指定格式的期望失败.
在你的情况下,Accept头部携带,text/csv但服务器响应application/json,因此406错误,因为有明显的不匹配.
要纠正此行为,服务器/弹出端不需要更改.相反,客户端应该开始发送Accept带有值的标头application/json,text/csv.这将确保客户端期望两种格式并在有效/错误响应的情况下支持它们.
请参阅此处了解更多详情.
编辑 2017年12月22日
观察到的行为被确认为Spring团队中的错误在这里.还没有已知的解决方法.
编辑 2018年1月4日
正如Spring JIRA评论中提到的那样,我们需要删除HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE请求属性@RestControllerAdvice.代码可能类似于下面的内容(返回带有一些"info"的500 - 该对象的序列化版本也会返回).
休息控制器建议
@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);
}
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3312 次 |
| 最近记录: |