在所有生成JSON的端点上使用@Produces(" application / json")是一种好习惯吗? [英] Is it good practice to use @Produces("application/json") on all JSON producing endpoints?
问题描述
我们开始使用Jersey / JAX-RS作为我们的前端代码使用的内部REST端点。必须返回结果的端点总是发送JSON对象。
We started using Jersey/JAX-RS for internal REST endpoints that get used by our front-end code. Endpoints that have to return a result, always send JSON objects.
出于调试目的,我们使用的是firefox restclient 扩展名。直到最近,我只需输入URL并点击发送,然后返回显示为JSON的内容。
For debugging purposes, we are using the firefox restclient extension. Until recently, I would just enter the URL and hit send, and would get back content displayed as JSON.
但是当我今天早上这样做时,FF扩展回来了并告诉我,我必须将响应类型更改为二进制(BLOB)。这样做会导致显示编码的字符串而不是JSON。
But when I did that this morning, the FF extension comes back and tells me that I have to change the response type to binary (BLOB). Doing so results in displaying an encoded string instead of JSON.
我可以通过设置请求标头来解决这个问题(接受:
是 application / json
)。
I could resolve that by setting a request header (Accept:
to be application/json
).
做了一些研究,我发现了这个问题。我的结论是:我们可能应该将 @Produces(application / json)
添加到所有这些端点。
Doing some more research, I came across this question. My conclusion is: probably we should add @Produces("application/json")
to all these endpoints.
问题:它真的那么简单,还是有很好的技术理由不那样做?
Question: is it really that simple, or are there good technical reasons to not do that?
推荐答案
你应总是宣布 @Produces
和 @Consumes
注释(在类级别或方法级别),用于内容协商和HTTP协议正确性。如果没有这些注释,结果将取决于客户端请求和服务器的默认行为(实现可能会有所不同),这会导致不可预测和模糊的结果。
You should always declare the @Produces
and @Consumes
annotations (either at the class level or method level) for the purpose of Content Negotiation and HTTP protocol correctness. Without these annotations, the result will be dependent on the client request and the default behavior of the server (which may be different across implementations), which leads to unpredictable and ambiguous results.
通过这些注释,我们宣传我们可以生产和使用的媒体类型。在检索(GET)请求时,客户端应发送 Accept
标头,其中包含他们期望的资源的媒体类型。在创建请求(PUT,POST)时,客户端应发送 Content-Type
标头,告诉服务器他们发送的数据是什么媒体类型。如果这些标头与广告服务器要处理的内容不匹配,那么客户端将收到错误响应,告诉他们问题是什么;如果有一个Retrieve请求和一个不匹配的 Accept
标题,那么响应将是 406 Not Acceptable 。使用Create请求和不匹配的 Content-Type
标头,响应将是 415不支持的媒体类型。
With these annotations, we advertise what media types we can produce and consume. On Retrieve (GET) requests, the client should send an Accept
header with the media type of the resource they expect back. And on Create requests (PUT, POST), the client should send a Content-Type
header telling the server what media type the data is that they are sending. If these headers don't match what the server is advertised to handle, then the client will get error responses back telling them what the problem is; with a Retrieve request and a non-matching Accept
header, the response will be a 406 Not Acceptable. With a Create request and a non-matching Content-Type
header, the response will be a 415 Unsupported Media Type.
这就是内容协商的工作原理。为了确保我们的服务器像客户期望的那样运行,我们应该声明我们可以在服务器上处理什么。注释就是这样。
This is how content negotiation works. And to make sure our server behaves as the clients expect, we should declare what we can handle on the server. The annotations do just this.
如你所说,当你离开 @Produces
时,客户告诉你你需要改变响应类型。这是因为结果是 Content-Type
响应头设置为 application / octet-stream
,这是什么这里的答案总结。客户端使用 Content-Type
标头来确定如何处理响应。
As you mentioned, when you left off the @Produces
, the client told you you needed to change the response type. This is because the result was that the Content-Type
response header was set to application/octet-stream
, which is what the answers here conclude. Clients use the Content-Type
header to determine how to handle the response.
最后一个示例是针对检索的请求。如果我们在Create端点上停止了 @Consumes
,那么很多不同的东西都可能出错。举个例子我们有一个我们想要接受JSON的端点,所以我们创建一个POJO来将JSON映射到。
That last example was for a Retrieve request. If we left off the @Consumes
on a Create endpoint, a lot of different things can go wrong. Take for example we have an endpoint that we want to accept JSON, so we create a POJO to map the JSON to.
@POST
public Response create(Customer customer) {}
为此工作,它是依赖于客户端在 application / json
的请求中设置 Content-Type
标头。但是如果没有 @Consumes
注释,我们基本上宣传这个端点是为了能够接受任何媒体类型,这简直太荒谬了。 @Consumes
注释就像一个警卫说如果你没有发送正确类型的数据,你就无法通过。但由于我们没有守卫,所有数据都被允许通过,结果是不可预测的,因为根据客户端设置 Content-Type
的原因,我们不要不知道 MessageBodyReader
1 将处理从实体主体到 Customer
的转换。如果未选择正确的 MessageBodyReader
(将JSON转换为POPJO的那个),则很可能会导致异常,客户端将返回500内部服务器错误,与获取415不支持的媒体类型不同。
For this to work, it is dependent on the client setting the Content-Type
header on the request to application/json
. But without the @Consumes
annotation, we are basically advertising this endpoint to be able to accept any media type, which is just ridiculous. The @Consumes
annotation acts like a guard saying "If you don't send the right type of data, you cannot pass". But since we don't have the guard, all data is allowed through, and the result is unpredictable, because depending on what the client sets the Content-Type
to, we don't know what MessageBodyReader
1 will handle the conversion from the entity body to Customer
. If the correct MessageBodyReader
is not chosen (the one that converts JSON to POPJOs), then most likely it will lead to an exception, and the client will get back a 500 Internal Server Error, which is not as specific as getting a 415 Unsupported Media Type.
1。 请参阅泽西岛文档的第8章和第9章。它将分别使用 MessageBodyReader
和 MessageBodyWriter
来解释实体主体如何转换为Java对象(反之亦然)。 / sub>
1. See chapter 8 and 9 of the Jersey docs. It will explain how entity bodies are converted to Java objects (and vice versa) using MessageBodyReader
and MessageBodyWriter
, respectively.
这篇关于在所有生成JSON的端点上使用@Produces(" application / json")是一种好习惯吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!