如何覆盖 JAX-RS 内容协商期间做出的决定?
我使用 RESTEasy 2.2.1.GA 作为 JAX-RS 实现来创建客户端以连接到第三方服务提供商。 (Education.com 的 REST API,如果重要的话)
为了确保我没有错过重要的实现细节,这里有代码示例:
服务接口
@Path("/")
public interface SchoolSearch {
@GET
@Produces({MediaType.APPLICATION_XML})
Collection<SchoolType> getSchoolsByZipCode(@QueryParam("postalcode") int postalCode);
}
调用类
public class SimpleSchoolSearch {
public static final String SITE_URL = "http://api.education.com/service/service.php?f=schoolSearch&key=****&sn=sf&v=4";
SchoolSearch service = ProxyFactory.create(SchoolSearch.class, SITE_URL);
public Collection<SchoolType> getSchools() throws Exception {
Collection<SchoolType> schools = new ArrayList<SchoolType>();
Collection<SchoolType> response = service.getSchoolsByZipCode(35803);
schools.addAll(response);
return schools;
}
}
在设置测试后进行此调用,我执行并看到抛出以下异常。
org.jboss.resteasy.plugins.providers.jaxb.JAXBUnmarshalException: Unable to find JAXBContext for media type: text/html;charset="UTF-8"
根据我的理解,通过阅读 RESTEasy/JAX-RS 文档,当响应返回到客户端时,在数据解组之前,会确定(内容协商??)关于使用哪种机制进行解组。 (我认为我们在这里讨论的是 MessageBodyReader,但我不确定。)通过查看响应正文,我发现返回的是格式正确的 XML,但是内容协商 (通过 HTTP 标头内容类型确实是 text/html;charset ="UTF-8") 不允许 JAXB 解析文本。
我认为实现行为正确,错误的是服务,但是,我不控制服务,但仍然想使用它。
话虽这么说:
我对为什么抛出异常的理解是否正确?
我该如何解决这个问题?
是否有一个简单的一行注释可以强制 JAXB 解组数据,或者我是否需要实现自定义 MessageBodyReader? (如果那是要实现的正确类)。
谢谢!
跟进:
我只是想发布我对艾登的答案所做的一些更改。我使用他的代码和 Resteasy ClientExecutionInterceptor 文档。我的最终课程看起来像
@Provider
@ClientInterceptor
public class SimpleInterceptor implements ClientExecutionInterceptor {
@Override
public ClientResponse execute(ClientExecutionContext ctx) throws Exception {
final ClientResponse response = ctx.proceed();
response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML);
return response;
}
}
最大的区别是添加了 @Provider 和 @ClientExecutionInterceptor 注释。这应该确保拦截器已正确注册。
另外,为了完整起见,我在测试中注册的拦截器略有不同。我用过:
providerFactory.registerProvider(SimpleInterceptor.class);
I'm using RESTEasy 2.2.1.GA as my JAX-RS implementation to create a client to connect to a third party service provider. (Education.com's REST API if it matters)
To make sure I haven't missed an important implementation detail here are code samples:
Service Interface
@Path("/")
public interface SchoolSearch {
@GET
@Produces({MediaType.APPLICATION_XML})
Collection<SchoolType> getSchoolsByZipCode(@QueryParam("postalcode") int postalCode);
}
Calling Class
public class SimpleSchoolSearch {
public static final String SITE_URL = "http://api.education.com/service/service.php?f=schoolSearch&key=****&sn=sf&v=4";
SchoolSearch service = ProxyFactory.create(SchoolSearch.class, SITE_URL);
public Collection<SchoolType> getSchools() throws Exception {
Collection<SchoolType> schools = new ArrayList<SchoolType>();
Collection<SchoolType> response = service.getSchoolsByZipCode(35803);
schools.addAll(response);
return schools;
}
}
After setting up tests to make this call, I execute and see the following exception being thrown.
org.jboss.resteasy.plugins.providers.jaxb.JAXBUnmarshalException: Unable to find JAXBContext for media type: text/html;charset="UTF-8"
From reading the RESTEasy/JAX-RS documentation, as I understand it, when the response is returned to the client, prior to the unmarshaling of the data, a determination is made (Content Negotiation??) about which mechanism to use for unmarshalling. (I think we're talking about a MessageBodyReader here but I'm unsure.) From looking at the body of the response, I see that what is returned is properly formatted XML, but the content negotiation (via HTTP header content-type is indeed text/html;charset ="UTF-8") is not allowing the text to be parsed by JAXB.
I think that the implementation is behaving correctly, and it is the service that is in error, however, I don't control the service, but would still like to consume it.
So that being said:
Am I correct in my understanding of why the exception is thrown?
How do I work around it?
Is there a simple one line annotation that can force JAXB to unmarshal the data, or will I need to implement a custom MessageBodyReader? (If that is even the correct class to implement).
Thanks!
Follow Up:
I just wanted to post the few changes I made to Eiden's answer. I created a ClientExecutionInterceptor using his code and the information available at Resteasy ClientExecutionInterceptor documentation. My final class looks like
@Provider
@ClientInterceptor
public class SimpleInterceptor implements ClientExecutionInterceptor {
@Override
public ClientResponse execute(ClientExecutionContext ctx) throws Exception {
final ClientResponse response = ctx.proceed();
response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML);
return response;
}
}
The big difference is the addition of the @Provider and @ClientExecutionInterceptor annotations. This should insure that the interceptor is properly registered.
Also, just for completeness, I registered the Interceptor slightly differently for my tests. I used:
providerFactory.registerProvider(SimpleInterceptor.class);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我确信这个问题有多种解决方案,但我只能想到一个。
尝试使用 ClientExecutionInterceptor 设置内容类型:
I'm sure there are several solutions to this problem, but I can only think of one.
Try so set the content-type using a ClientExecutionInterceptor:
我不知道任何此类注释,其他人可能知道,但解决方法是创建本地代理。创建一个控制器,使用以下方法将所有参数传递给 education.com
java.Net.URL.get()
返回您收到的答案,但修改标头。然后将您的客户端连接到本地代理控制器。
I dont know about any such annotation, others might do, but a workaround is to create a local proxy. Create a controller, that passes all parameters to education.com using a
java.Net.URL.get()
return the answer that you received, but modify the header. Then connect your client to the local proxy controller.