我们开始将Jersey / JAX-RS用于内部REST端点,这些端点被我们的前端代码使用。必须返回结果的端点始终发送JSON对象。
出于调试目的,我们使用firefox restclient扩展名。直到最近,我才输入URL并单击send,然后返回显示为JSON的内容。
但是当我今天早上这样做时,FF扩展名又回来了,并告诉我必须将响应类型更改为二进制(BLOB)。这样做会导致显示编码的字符串而不是JSON。
我可以通过将请求标头(Accept:
设置为application/json
)来解决此问题。
做更多的研究,我发现了这个question。我的结论是:也许我们应该在所有这些端点中添加@Produces("application/json")
。
问题:真的那么简单吗?还是有很好的技术理由不这样做?
答案 0 :(得分:3)
出于Content Negotiation和HTTP协议正确性的考虑,您应该始终声明@Produces
和@Consumes
注释(在类级别或方法级别) 。如果没有这些注释,结果将取决于客户端请求和服务器的默认行为(在不同的实现中可能有所不同),这将导致无法预测和模棱两可的结果。
使用这些注释,我们宣传我们可以产生和消费的媒体类型。在获取(GET)请求时,客户端应发送一个Accept
标头,其中包含他们期望返回的资源的媒体类型。并且在创建请求(PUT,POST)时,客户端应发送一个Content-Type
头,告诉服务器它们正在发送的数据是哪种媒体类型。如果这些标头与广告服务器要处理的标头不匹配,则客户端将收到错误响应,告诉他们问题出在哪里;带有检索请求和不匹配的Accept
标头,响应将是406 Not Acceptable。使用创建请求和不匹配的Content-Type
标头,响应将是415 Unsupported Media Type。
这是内容协商的工作方式。为了确保我们的服务器行为符合客户的期望,我们应该声明我们可以在服务器上处理的内容。注释就是这样做的。
正如您提到的,当您离开@Produces
时,客户端告诉您您需要更改响应类型。这是因为结果是Content-Type
响应标头设置为the answers here得出的application/octet-stream
。客户端使用Content-Type
标头来确定如何处理响应。
最后一个示例是针对“检索”请求的。如果我们在创建端点上放弃了@Consumes
,那么很多不同的事情都会出错。例如,我们有一个要接受JSON的端点,因此我们创建一个POJO来将JSON映射到。
@POST
public Response create(Customer customer) {}
要使其正常工作,取决于客户端将请求上的Content-Type
标头设置为application/json
。但是,如果没有@Consumes
批注,我们基本上是在宣传此端点,使其能够接受 any 媒体类型,这简直是荒谬的。 @Consumes
注释的作用就像是一个警卫,说“如果没有发送正确的数据类型,就无法通过”。但是,由于我们没有保护措施,因此所有数据都可以通过,因此结果是不可预测的,因为根据客户端将Content-Type
设置为的内容,我们不知道MessageBodyReader
1 将处理从实体主体到Customer
的转换。如果未选择正确的MessageBodyReader
(将JSON转换为POPJO的那个),则很可能会导致异常,并且客户端将返回500 Internal Server Error,该错误不如415不支持的媒体类型。
1。 See chapter 8 and 9 of the Jersey docs。它将说明如何分别使用MessageBodyReader
和MessageBodyWriter
将实体主体转换为Java对象(反之亦然)。
答案 1 :(得分:1)
在所有产生JSON的端点上使用
@Produces("application/json")
是一种好习惯吗?
如果您的资源方法生成JSON作为资源的表示形式,则应 用@Produces(MediaType.APPLICATION_JSON)
进行注释。结果,响应将具有一个Content-Type
头,指示有效负载的媒体类型。
@Produces
批注还用于请求匹配:JAX-RS运行时将Accept
标头中发送的媒体类型与{ {3}}注释。
如果您不想注释应用程序中的每个资源方法,则可以注释资源类。这将指示此类中定义的所有方法都必须产生JSON作为资源的表示形式。
在@Produces
批注中定义的媒体类型指示将由在应用程序中注册的@Produces
实例产生的媒体类型。考虑以下示例:
@GET
@Produces(MediaType.APPLICATION_JSON)
public Foo getFoo() {
Foo foo = new Foo();
return Response.ok(foo).build();
}
一旦getFoo()
方法用@Produces(MediaType.APPLICATION_JSON)
注释,JAX-RS将把Foo
实例作为JSON文档写入。这是在MessageBodyWriter
实现中完成的。例如,如果您的应用程序使用MessageBodyWriter
,则Jackson将用于将Java对象转换为JSON文档。