首页 > 解决方案 > 在所有生成 JSON 的端点上使用 @Produces("application/json") 是一种好习惯吗?

问题描述

我们开始将 Jersey/JAX-RS 用于我们的前端代码使用的内部 REST 端点。必须返回结果的端点总是发送 JSON 对象。

出于调试目的,我们使用了 firefox restclient扩展。直到最近,我只是输入 URL 并点击发送,然后将返回显示为 JSON 的内容。

但是当我今天早上这样做时,FF 扩展又回来了,告诉我必须将响应类型更改为二进制 (BLOB)。这样做会导致显示编码字符串而不是 JSON。

我可以通过设置请求标头(Accept:to be application/json)来解决这个问题。

做一些更多的研究,我遇到了这个问题。我的结论是:也许我们应该添加@Produces("application/json")到所有这些端点。

问题:真的那么简单,还是有充分的技术理由这样做?

标签: javajerseyjax-rs

解决方案


出于内容协商和 HTTP 协议正确性的目的,您应该始终声明@Produces@Consumes注释(在类级别或方法级别) 。如果没有这些注释,结果将取决于客户端请求和服务器的默认行为(在不同的实现中可能会有所不同),这会导致不可预测和模棱两可的结果。

通过这些注释,我们可以宣传我们可以生产和消费的媒体类型。在检索 (GET) 请求中,客户端应发送一个Accept标头,其中包含他们期望返回的资源的媒体类型。在创建请求(PUT、POST)上,客户端应该发送一个Content-Type标头,告诉服务器他们正在发送的数据是什么媒体类型。如果这些标头与服务器宣传要处理的标头不匹配,那么客户端将收到错误响应,告诉他们问题所在;使用 Retrieve 请求和不匹配的Accept标头,响应将是406 Not Acceptable。使用 Create 请求和不匹配的Content-Type标头,响应将是415 Unsupported Media Type

这就是内容协商的工作原理。并且为了确保我们的服务器按照客户期望的方式运行,我们应该声明我们可以在服务器上处理什么。注释就是这样做的。

正如您所提到的,当您离开时@Produces,客户告诉您需要更改响应类型。这是因为结果是Content-Type响应标头设置为application/octet-stream,这就是这里的答案得出的结论。客户端使用Content-Type标头来确定如何处理响应。

最后一个示例是针对检索请求的。如果我们@Consumes在 Create 端点上省略了,很多不同的事情都可能出错。举个例子,我们有一个我们想要接受 JSON 的端点,所以我们创建一个 POJO 来将 JSON 映射到。

@POST
public Response create(Customer customer) {}

为此,它取决于客户端将Content-Type请求的标头设置为application/json. 但是如果没有@Consumes注释,我们基本上是在宣传这个端点能够接受任何媒体类型,这太荒谬了。注释就像一个守卫说“@Consumes如果你不发送正确类型的数据,你就不能通过”。但是由于我们没有守卫,所以所有数据都被允许通过,并且结果是不可预测的,因为根据客户端设置的Content-Typeto,我们不知道MessageBodyReader1将处理从实体主体到的转换Customer。如果正确MessageBodyReader未选择(将 JSON 转换为 POPJO 的那个),那么它很可能会导致异常,并且客户端将返回 500 Internal Server Error,这不像获取 415 Unsupported Media Type 那样具体。


1.参见 Jersey 文档的第 8 章和第 9 章。它将解释实体主体如何分别使用MessageBodyReader和转换为 Java 对象(反之亦然) 。MessageBodyWriter


推荐阅读