[英]Unable to override a response header using a Jersey client filter
我正在尝试使用Jersey客户端API来使用第三方REST服务。 我计划使用自动POJO反序列化从JSON响应转到Java对象。
不幸的是,第三方服务使用内容类型"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
:
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)
从原始headers
复制status
, headers
和entity
变量是微不足道的。 但是,我看不到获得对workers
字段的引用的方法。
如何使用Jersey客户端过滤器将响应头从"text/javascript"
为"application/json"
以便我的POJO反序列化可以工作?
在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文档并不完美,但它确实有一些链接到相关类的javadoc: https : //jersey.java.net/documentation/latest/filters-and-interceptors.html
我从一个响应XML内容的服务获得XML响应,但缺少“Content-Type:application / xml”标头。 可能更好的方法是注册MessageBodyReaders,但上面的方法在我使用该服务的API时有效。
我没有回答你的真实问题,但我想如果你想尝试在你的过滤器中创建一个新的响应,我会看到你如何获得这个工人实例。
您需要的“工人”对象似乎是单身人士。 如果您可以获取com.sun.jersey.api.client.Client实例,则可以检索workers对象。 在我的例子中,Jersey客户端代码在一个单元测试中,其子类是JerseyTest。 JerseyTest定义了一个返回Client对象的方法“client()”。 我添加了以下测试代码(不完全但非常接近):
MessageBodyWorkers workers = client().getMessageBodyWorkers();
然后我在ClientResponse的构造函数中设置了一个断点(这是Jersey返回的原始ClientResponse。我没有尝试克隆它,因为我不需要进行测试)。 传递给构造函数的worker是同一个实例。 因此,即使您无法从响应对象获取workers对象,您也应该能够在其他地方获取它。
Guido的答案提供了创建新ClientResponse
对象并返回它所需的洞察力。 由于我还没有打算追查的原因,创建新的InboundHeaders
, InboundHeaders
添加所有现有标头,然后修改有问题的单个标头仍然失败并出现UnsupportedOperationException
。 因此,为了重新编写标头,我们迭代原始标头并迭代地构建正确的集合:
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());
}
}
在Jersey 2中,您应该使用ClientResponseFilter
。 然后你可以调用responseContext.getHeaders().putSingle(...)
。
在Java 8下,您可以使用lambda:
client.register((ClientResponseFilter) (requestContext, responseContext) ->
responseContext.getHeaders().putSingle("Content-Type", "application/json"));
如果要重新使用现有过滤器实例 ,只需在Client
而不是ClientConfig
上注册它。
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));
这不是最好的解决方案 ,但它可以帮助您迁移。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.