java - 406 当在 Spring Controller 中抛出异常并接受文本/csv header 时

标签 java spring rest

我有一个带有返回文本/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/

相关文章:

java - 哪些部分功能应该放在DAO层,哪些部分放在服务层

java - REST资源异常拦截器

java - 如何将READ_PHONE_STATE权限(特权权限)列入白名单?

java - 警告 : [unchecked] unchecked cast while using Generic Array

java - .split ("\")不起作用,并且还出现错误 arrayIndexOitOfBoundsException : 1

android - android平台上的数据库关系设计

xml - 谁能推荐 VIN 号码的 REST/SOAP Web 服务?

java - RESTful Web 服务中的@POST

Spring DAO vs Spring ORM vs Spring JDBC

Java Spring MVC 无状态到有状态的切换