4

我正在尝试使用 Jersey 客户端 API 来使用第三方 REST 服务。我计划使用自动 POJO 反序列化从 JSON 响应转到 Java 对象。

不幸的是,第三方服务使用 content type 返回响应"text/javascript"。我的 Jersey 客户端无法理解这应该被视为 JSON 对象并且无法反序列化该对象。

我编写了一个简单的 Jersey 服务器应用程序,通过将内容类型更改为反序列化工作来验证这"text/javascript"一点"application/json"

有了这些信息,我开始使用 Jersey 客户端过滤器来修改响应标头。代码来自这个问题的作者的评论。事实上,这个问题似乎和我的完全一样——但是回答者错误地回答了这个问题,并展示了如何修改请求标头(而不是响应标头)。原作者能够使用答案来创建他的解决方案,但是,他陈述的解决方案似乎无法正常工作。

过滤器代码为:

client.addFilter(new ClientFilter() {
  @Override public ClientResponse handle(ClientRequest cr) 
      throws ClientHandlerException {
    ClientResponse response = getNext().handle(cr); 
    response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); 
    return response;
  }
});

然而,当执行时,UnsupportedOperationException会引发 an:

Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.clear(Collections.java:1035)
at com.sun.jersey.core.util.StringKeyIgnoreCaseMultivaluedMap.putSingle(StringKeyIgnoreCaseMultivaluedMap.java:78)
at com.sun.jersey.core.util.StringKeyIgnoreCaseMultivaluedMap.putSingle(StringKeyIgnoreCaseMultivaluedMap.java:56)
at App$1.handle(App.java:49)
at com.sun.jersey.api.client.Client.handle(Client.java:648)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:680)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:507)
at App.main(App.java:63)

返回的标头似乎包含在不可修改的集合中。

然后我尝试将所有标头复制到一个新集合中,但是我看不到将标头映射设置回响应中。

最后,我想也许我可以创建一个ClientResponse包含修改后的标题的新标题。但是,构造函数ClientResponse具有以下签名:

public ClientResponse(int status, 
                      InBoundHeaders headers, 
                      InputStream entity, 
                      MessageBodyWorkers workers)  

从原始复制status,headers和变量是微不足道的。entity但是,我看不出获得对该workers领域的引用。

如何使用 Jersey 客户端过滤器将响应标头从"text/javascript"to修改,"application/json"以便我的 POJO 反序列化可以工作?

4

5 回答 5

8

在 Jersey 2 中,使用 ClientConfig 注册ClientResponseFilter的实现,以便操作传入响应的 HTTP 标头。

例如,这似乎适用于 Jersey 2.3.1 来处理 HTTP 标头:

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.client.ClientRequestContext;

import org.glassfish.jersey.client.ClientConfig;

/* Ensure that there is an "application/xml" Content-Type header on
 * successful responses without a content type header. */
@Provider
public static class EnsureXmlContentTypeFilter implements ClientResponseFilter {
    @Override
    public void filter(ClientRequestContext requestContext,
                       ClientResponseContext responseContext) {
        if (200 == responseContext.getStatus() &&
            null == responseContext.getHeaderString(HttpHeaders.CONTENT_TYPE)) {

            responseContext.getHeaders().add(
                HttpHeaders.CONTENT_TYPE, "application/xml"
            );

        }
    }
}

private final ClientConfig config = new ClientConfig()
    // Registering this filter adds a "Content-Type: application/xml" 
    // header to each response that lacks Content-Type headers.
    .register(EnsureXmlContentTypeFilter.class)
;
private final Client client = ClientBuilder.newClient(config);

关于过滤器和拦截器的 Jersey 文档并不完美,但它确实有一些指向相关类的 javadocs 的链接:https ://jersey.java.net/documentation/latest/filters-and-interceptors.html

我从响应 XML 内容的服务获取 XML 响应,但缺少“Content-Type: application/xml”标头。可能更好的方法是注册 MessageBodyReaders,但上述方法在我使用该服务的 API 时有效。

于 2013-10-24T15:57:13.847 回答
3

对于您的真正问题,我没有答案,但我想我知道如果您想尝试在过滤器中创建新响应,您如何获得该工人实例。

您需要的“工人”对象似乎是一个单例。如果您可以获取您的 com.sun.jersey.api.client.Client 实例,则可以检索工作人员对象。在我的例子中,Jersey 客户端代码在一个单元测试中,该单元测试是 JerseyTest 的子类。JerseyTest 定义了一个返回 Client 对象的方法“client()”。我添加了以下测试代码(不完全是,但很接近):

  MessageBodyWorkers workers = client().getMessageBodyWorkers();

然后我在 ClientResponse 的构造函数中设置了一个断点(这是 Jersey 返回的原始 ClientResponse。我没有尝试克隆它,因为我不需要进行测试)。传递给构造函数的工人是同一个实例。因此,即使您无法从响应对象中获取workers 对象,您也应该能够在其他地方获取它。

于 2012-10-05T02:07:13.447 回答
3

Guido 的回答提供了创建新ClientResponse对象并将其返回所需的洞察力。由于我还没有费心追查的原因,创建一个新InboundHeadersUnsupportedOperationException. 因此,为了重写标头,我们迭代原始标头并迭代构建正确的集合:

final Client client = Client.create(clientConfig);
client.addFilter(new ClientFilter()
{
  @Override
  public ClientResponse handle(ClientRequest cr) throws ClientHandlerException
  {
    final ClientResponse response = getNext().handle(cr); 
    final InBoundHeaders headers = new InBoundHeaders();

    for (String header : response.getHeaders().keySet())
    {
      if (header.equals(HttpHeaders.CONTENT_TYPE))
      {
        headers.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); 
      }
      else
      {
        headers.put(header, headers.get(header));
      }
    }

    return new ClientResponse(response.getStatus(),
                              headers,
                              response.getEntityInputStream(),
                              client.getMessageBodyWorkers());
  }
}
于 2012-10-08T00:50:04.000 回答
1

在 Jersey 2 中,您应该使用ClientResponseFilter. 然后你就可以打电话了responseContext.getHeaders().putSingle(...)

在 Java 8 下,您可以使用 lambda 来完成:

client.register((ClientResponseFilter) (requestContext, responseContext) ->
                responseContext.getHeaders().putSingle("Content-Type", "application/json"));
于 2015-09-17T18:40:35.263 回答
0

如果您想重用现有的过滤器实例,只需将其注册到Client而不是ClientConfig.

旧方式(Jersey-1.9)

     import com.sun.jersey.api.client.Client;
     import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;

     Client client = new Client();
     client.addFilter(new HTTPBasicAuthFilter(username, password));

新方式(Jersey-2.3):

     import javax.ws.rs.client.Client;
     import javax.ws.rs.client.ClientBuilder;
     import org.glassfish.jersey.client.filter.HttpBasicAuthFilter;

     Client client = ClientBuilder.newClient();
     client.register(new HttpBasicAuthFilter(username, password));

这不是最好的解决方案,但它可以帮助您迁移。

于 2014-05-17T18:48:57.423 回答