【问题标题】:Why am I getting a 406 error when trying to get JSON in Java Spring?为什么在 Java Spring 中尝试获取 JSON 时出现 406 错误?
【发布时间】:2018-07-23 16:32:41
【问题描述】:

当我通常使用返回 JSON 数据的 Java Spring 制作控制器端点时,我的返回类型通常是 String;我将 toString() 我的 JSON 对象并在我的 Javascript 中解析它们。最近,我在某处读到这不是“正确”的做法(试图遵循最佳实践和指导方针)。我正在开始一个新项目,其中我的返回类型是 JSON 对象(依赖项是 org.json)。

当我尝试在浏览器中访问端点时,我收到一个 406 错误,描述为“此请求标识的资源只能生成具有根据请求“接受”标头不可接受的特征的响应。”。

这是我的端点现在的样子:

@RequestMapping(value = "/api/get-items", method = {RequestMethod.GET, RequestMethod.POST}, headers = "Accept=application/json")
@ResponseBody
public JSONObject doGetItems(HttpServletRequest request, HttpServletResponse response) {
    JSONObject data = new JSONObject();
    JSONArray items = new JSONArray();
    JSONObject item1 = new JSONObject().put("id", 123);
    JSONObject item2 = new JSONObject().put("id", 456);
    JSONObject item3 = new JSONObject().put("id", 789);
    items.put(item1).put(item2).put(item3);
    data.put("data", items);
    return data;
}

我怎样才能让它工作并返回 JSON?还是我应该返回我的 JSON 对象的字符串化版本并且我一直都在正确地执行此操作?

更新

首先,我尝试去掉@RequestMapping注解中的headers参数。我仍然收到 406 错误。

然后我尝试更改/添加 @RequestMapping 注释中的参数,但也没有运气。这是我尝试将其更改为:

@RequestMapping(value = "/api/get-items",
                method = {RequestMethod.GET, RequestMethod.POST},
                headers = "Accept=*/*",
                produces = MediaType.APPLICATION_JSON_VALUE)

我仍然收到 406 错误。

【问题讨论】:

  • Un 没有正确理解。 Spring 不处理 org.json.JSONObject 的实例。它使用 Jackson(或 GSON,取决于您在类路径中的内容)自动解析/编组从/到实际 Java 类。因此,例如,如果您想返回一个项目数组,其中每个项目都有一个 ID、一个名称和一个 startInstant,例如,那么只需返回一个 List。官方示例见spring.io/guides/gs/rest-service
  • @JBNizet 我尝试使用 gson 将我的一个端点中的所有 JSONObjects 和 JSONArrays 更改为 JsonObjectJsonArray,但随后出现 500 错误说“无法写入 JSON:这不是 JSON Primitive。”
  • 再次阅读我的评论。您不应该返回 JsonArrays 或 JsonObjects。您应该返回 POJO,它将映射到 JSON。如我喜欢的示例所示。
  • 简单。我确实读过你的评论。其中一部分说“或 GSON”,所以我尝试了 JsonObjectJsonArray。无论如何,我现在了解使用 Java 对象。如果我 toString() 使用 org.JSON 构建的 JSON 对象,Spring 实际上会处理其余部分并将它们作为实际 JSON 返回。我对此很好;只是我的一个误解。如果您将回复作为答案提交,我会将其标记为正确。

标签: java json spring controller response


【解决方案1】:

超文本传输​​协议 (HTTP) 406 Not Acceptable 客户端错误响应代码表示服务器无法生成与请求的主动内容协商标头中定义的可接受值列表匹配的响应,并且服务器不愿意提供默认表示。

主动式内容协商标头包括:

客户端(例如您的 Web 浏览器或我们的 CheckUpDown 机器人)可以向 Web 服务器(运行网站)指示它将从 Web 服务器接收回的数据的特征。这是使用以下类型的“接受标头”完成的:

接受:客户端接受的 MIME 类型。例如,浏览器可能只接受它知道如何处理的返回类型的数据(HTML 文件、GIF 文件等)。

Accept-Charset:客户端接受的字符集。

Accept-Encoding:客户端接受的数据编码,例如它理解的文件格式。

Accept-Language:客户接受的自然语言(英语、德语等)。

Accept-Ranges:客户端是否接受来自资源的字节范围,即资源的一部分。

通过在客户端更改或删除标题内容,它应该可以工作。

【讨论】:

  • 更新了原帖。我试过去掉@RequestMapping注解中的headers参数,但是没有用。
【解决方案2】:

当您指定headers = "Accept=application/json" 时,您的意思是Accept 标头必须存在并且具有该值。

客户端没有准确发送该值,也没有理由要求它这样做,因为Accept 标头值是一个逗号分隔的可接受值列表,所以它只需要列出@ 987654325@,它不必指定该值。

要修复,请将headers = "Accept=application/json" 替换为produces = "application/json"

当然,客户端还是需要指定application/jsonapplication/*或者*/*是可以接受的。

【讨论】:

  • 我更新了我原来的帖子。我添加了produces = "application/json",也添加了headers = Accept=*/*,但效果不佳。
  • @aCarella 删除 headers =。这就是我回答中的 replace 的意思。
  • 我试过了,也没用。我弄清楚了问题所在。在 Spring 中使用 @ResponseBody 时,不一定要构造实际的 JSON 对象和数组。 Java 数据结构可以与控制器方法一起使用和返回,它们将被自动转换。就我而言,如果您确实使用 org.json 构造 JSON 对象和数组,则必须将它们转换为字符串,因为 Spring 不处理它们。这有点像双重工作,因为 Spring 已经使用 Jackson 来 JSON 化对象。
  • @aCarella 所以第一个comment by JB Nizet 是完全正确的,嗯?
猜你喜欢
  • 1970-01-01
  • 2014-06-16
  • 1970-01-01
  • 2019-12-02
  • 2018-01-10
  • 1970-01-01
  • 1970-01-01
  • 2021-05-01
  • 2012-05-02
相关资源
最近更新 更多