使用 JAX-RS Jersey 2.2 获取带有 Content-Type 和 Accept 标头的请求

Posted

技术标签:

【中文标题】使用 JAX-RS Jersey 2.2 获取带有 Content-Type 和 Accept 标头的请求【英文标题】:GET Request with Content-Type and Accept header with JAX-RS Jersey 2.2 【发布时间】:2013-09-06 21:07:27 【问题描述】:

我尝试访问提供交通信息的开放数据网络服务。文档说请求必须是GET 并且需要包含Accept: application/jsonContent-Type: application/json。我不明白他们为什么需要Content-Type 但没关系:

我尝试仅使用 Accept: 标头检索数据,但我总是得到 415 Unsupported Media Type。现在我正在尝试这种方式(但我不确定我是否真的正确设置了两个标题):

String entity = ClientBuilder.newClient().target(liveDataURI)
    .path(liveDataPath)
    .request(MediaType.APPLICATION_JSON)
    .accept(MediaType.APPLICATION_JSON)
    .get(String.class);

如您所见,我使用的是 Jersey 2.2,但我仍然收到 415 Unsupported Media Type

编辑

所以我让它工作了,但我不明白为什么。 accept(MediaType.APPLICATION_JSON)header("Content-type","application/json")不一样吗?

String responseEntity = ClientBuilder.newClient()
    .target(liveDataURI)
    .path(liveDataPath)
    .request(MediaType.APPLICATION_JSON)
    .header("Content-type", "application/json")
    .get(String.class);

【问题讨论】:

【参考方案1】:

Accept 标头告诉服务器您的客户端在响应中想要什么Content-Type 标头告诉服务器客户端在请求中发送的内容。所以两者是一样的。

如果服务器只接受application/json,则必须发送指定请求内容的请求:

Content-Type: application/json

这就是您编辑的代码有效的原因。

编辑

在您的第一个代码中,您使用WebTarget.request(MediaType... acceptedResponseTypes)。该方法的参数

定义接受的响应媒体类型。

您在此方法调用的结果上使用Innvocation.Builder.accept(MediaType... mediaTypes)。但是accept() 没有添加新标头,在您的第一个代码中是不必要的。

您从不指定请求的内容类型。由于服务器需要 Content-Type 标头,因此它以 415 响应。

【讨论】:

Ooookay 明白了!但在旁注中:我没有向服务器发送任何东西,我只是在做请求,那么为什么 Content-type 无论如何都是必需的?把我发送的参数算作“内容”? 什么参数?我不知道为什么这个服务器需要Content-Type 来代替GET。您必须询问运行此服务器的人员 :) 我可以在 URI 中使用一些参数来指定请求。但无论如何,非常感谢! 请求中的 Accept 标头告诉服务器您的客户端在响应中想要什么。响应中的 Content-Type 标头告诉客户端服务器在对客户端的响应中发送了什么。所以两者是不一样的。 在请求头中,Accept 适用于响应正文,而Content-Type 适用于请求正文。查看HTTP request headers列表【参考方案2】:

accept(MediaType.APPLICATION_JSON)header("Content-type","application/json") 不一样吗?

不,它们是不同的。 这就是它们的关系:

Client                     Server
(header)                   (class/method annotation)
====================================================
Accept          <--->      @Produces
Content-Type    <--->      @Consumes

服务器使用它从客户端接收到的内容(其格式在Content-Type 中指定)并产生客户端接受的内容(其格式在Accept 中指定)。

示例

client 发送以下标头: Content-Type = text/xml(它在正文中发送一个 XML) Accept = application/json(它期望得到一个 JSON 作为响应) server 至少需要为相应的方法提供以下注解(如果该方法没有明确提及,则这些注解取自类级别): @Consumes(MediaType.TEXT_XML)(它从客户端获取 XML) @Produces(MediaType.APPLICATION_JSON)(它向客户端发送 JSON)

观察

    服务器可以更加灵活,被配置为获取/生成多种可能的格式

    例如:一个客户端可以发送一个 XML,而另一个客户端可以发送一个 JSON 到相同的方法,如果它具有以下注解:@Consumes( MediaType.APPLICATION_JSON, MediaType.TEXT_XML )

    MediaType 值只是 String 常量:

    public final static String APPLICATION_JSON = "application/json";
    public final static String TEXT_XML = "text/xml";
    

【讨论】:

【参考方案3】:

如果输入请求中没有提供默认的 Accept 标头,您可以使用 ContainerResponseFilter 设置默认标头。

@Provider
public class EntityResponseFilter implements ContainerResponseFilter 

    private MediaType getExternalMediaType()
        MediaType mediaType = new MediaType("application", "vnd.xxx.resource+json")
        return mediaType; 
    

    @Override
    public void filter( ContainerRequestContext reqc , ContainerResponseContext resc ) throws IOException 
        MediaType mediaType = getExternalMediaType(); 
        List<MediaType> mediaTypes = reqc.getAcceptableMediaTypes();
        if( mediaTypes.contains(mediaType) )    
            resc.setEntity( resc.getEntity(), new Annotation[0], mediaType );
        
        // ...
    

【讨论】:

以上是关于使用 JAX-RS Jersey 2.2 获取带有 Content-Type 和 Accept 标头的请求的主要内容,如果未能解决你的问题,请参考以下文章

JAX-RS(Jersey 2 实现)内容协商,带有 URL 扩展名 .xml 或 .json

为啥使用 JAX-RS / Jersey?

如何使用 JAX-RS 和 Jersey 处理 CORS

JAX-RS (Jersey 2) - 使用 JSR 250 注释的授权

如何使用JAX-RS和Jersey处理CORS

如何在 JAX-RS 客户端中记录请求正文