java – 406当Spring控制器中抛出异常时接受带有text / csv的接受头

我有一个控制器,其方法返回text / csv.这适用于正常的成功案例,但是如果抛出异常,并且我有一个Accept:text / csv的标题,我得到406响应.例如:

@RequestMapping(value = "/foo", method = RequestMethod.GET, produces = "text/csv")
public String getCsv() {
    throw new IllegalArgumentException();
}

这是一个完全普通的Spring Boot应用程序(Maven项目,导入spring-boot-starter-web-services),只包含一个带有上述方法的控制器.

我假设原因是框架将异常转换为JSON错误响应.如果我删除了produce属性并发送了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

但是,有趣的是,如果我在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,但是当它变得卷曲时,它就是406.如果我从PostMan发送请求,我会看到完全相同的事情.

我不确定是什么导致了从500到406的变化.我认为它不是客户端,所以我最好的猜测是Tomcat正在这样做.有没有办法阻止这种情况发生?还是有其他可能性我错过了?

最佳答案
====原始答案(解释预期的行为)====

Accept标头指定客户端期望服务器响应的格式类型.任何与此差异都会导致HTTP 406 – 不可接受的错误.但是,此错误并不意味着操作失败,但它表示客户端对指定格式的期望失败.

在你的情况下,Accept标头带有text / csv但服务器用application / json响应,因此406错误,因为存在明显的不匹配.

要纠正此行为,服务器/弹出端不需要更改.相反,客户端应该开始发送Accept标头,它将携带值作为application / json,text / csv.这将确保客户端期望两种格式并在有效/错误响应的情况下支持它们.

有关详细信息,请参阅here.

编辑2017年12月22日

观察到的行为被Spring团队here确认为一个错误.尚无已知的解决方法.

编辑2018年1月4日

正如Spring JIRA comments中提到的那样,我们需要删除@RestControllerAdvice中的HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE请求属性.代码可能类似于下面的内容(返回带有一些“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);
    }
}

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;
    }

}

转载注明原文:java – 406当Spring控制器中抛出异常时接受带有text / csv的接受头 - 代码日志