如何覆盖 JAX-RS 内容协商期间做出的决定?

发布于 2024-12-17 09:46:22 字数 2475 浏览 1 评论 0原文

我使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

骄傲 2024-12-24 09:46:22

我确信这个问题有多种解决方案,但我只能想到一个。

尝试使用 ClientExecutionInterceptor 设置内容类型:

public class Interceptor 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;
    }

}

public void getSchools() throws Exception {
    ResteasyProviderFactory.getInstance()
        .getClientExecutionInterceptorRegistry()
        .register( new Interceptor() );

    SchoolSearch service =
            ProxyFactory.create(SchoolSearch.class, SITE_URL);
}

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:

public class Interceptor 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;
    }

}

public void getSchools() throws Exception {
    ResteasyProviderFactory.getInstance()
        .getClientExecutionInterceptorRegistry()
        .register( new Interceptor() );

    SchoolSearch service =
            ProxyFactory.create(SchoolSearch.class, SITE_URL);
}
腻橙味 2024-12-24 09:46:22

我不知道任何此类注释,其他人可能知道,但解决方法是创建本地代理。创建一个控制器,使用以下方法将所有参数传递给 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文