CXF:没有为类找到消息正文编写器 - 自动映射非简单资源 [英] CXF: No message body writer found for class - automatically mapping non-simple resources

查看:95
本文介绍了CXF:没有为类找到消息正文编写器 - 自动映射非简单资源的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用CXF rest客户端,它适用于简单的数据类型(例如:字符串,整数)。但是,当我尝试使用自定义对象时,我得到这个:

I am using the CXF rest client which works well for simple data types (eg: Strings, ints). However, when I attempt to use custom Objects I get this:

Exception in thread "main" org.apache.cxf.interceptor.Fault: .No message body writer found for class : class com.company.datatype.normal.MyObject.
    at org.apache.cxf.jaxrs.client.ClientProxyImpl$BodyWriter.handleMessage(ClientProxyImpl.java:523)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
    at org.apache.cxf.jaxrs.client.ClientProxyImpl.doChainedInvocation(ClientProxyImpl.java:438)
    at org.apache.cxf.jaxrs.client.ClientProxyImpl.invoke(ClientProxyImpl.java:177)
    at $Proxy13.execute(Unknown Source)
    at com.company.JaxTestClient.main(JaxTestClient.java:26)
Caused by: org.apache.cxf.jaxrs.client.ClientWebApplicationException: .No message body writer found for class : class com.company.datatype.normal.MyObject.
    at org.apache.cxf.jaxrs.client.AbstractClient.reportMessageHandlerProblem(AbstractClient.java:491)
    at org.apache.cxf.jaxrs.client.AbstractClient.writeBody(AbstractClient.java:401)
    at org.apache.cxf.jaxrs.client.ClientProxyImpl$BodyWriter.handleMessage(ClientProxyImpl.java:515)
    ... 5 more

我这样称呼它:

JaxExample jaxExample = JAXRSClientFactory.create( "http://localhost:8111/", JaxExample.class );
MyObject before = ...
MyObject after = jaxExample.execute( before );

以下是界面中的方法:

@POST
@Path( "execute" )
@Produces( "application/json" )
MyObject execute( MyObject myObject );

restlet库通过将XStream依赖项添加到您的只是工作的路径,非常简单。 CXF有类似之处吗?

The restlet library does this quite simply, by adding the XStream dependency to your path it "just works". Does CXF something similar?

编辑#1:

我发布了这是对CXF问题管理系统此处的功能改进。我只能希望这会得到照顾。

I've posted this as a feature improvement to the CXF issue management system here. I can only hope this will get attended to.

推荐答案

它不是开箱即用,但CXF确实支持JSON绑定休息服务。请参阅 cxf jax-rs json docs 。你会仍需要做一些最小配置才能使提供者可用,如果你想更好地控制JSON的形成方式,你需要熟悉jettison。

It isn't quite out of the box but CXF does support JSON bindings to rest services. See cxf jax-rs json docs here. You'll still need to do some minimal configuration to have the provider available and you need to be familiar with jettison if you want to have more control over how the JSON is formed.

编辑:根据评论请求,这里有一些代码。我对此没有太多经验,但以下代码在快速测试系统中作为示例。

Per comment request, here is some code. I don't have a lot of experience with this but the following code worked as an example in a quick test system.

//TestApi parts
@GET
@Path ( "test" )
@Produces ( "application/json" )
public Demo getDemo () {
    Demo d = new Demo ();
    d.id = 1;
    d.name = "test";
    return d;
}

//client config for a TestApi interface
List providers = new ArrayList ();
JSONProvider jsonProvider = new JSONProvider ();
Map<String, String> map = new HashMap<String, String> ();
map.put ( "http://www.myserviceapi.com", "myapi" );
jsonProvider.setNamespaceMap ( map );
providers.add ( jsonProvider );
TestApi proxy = JAXRSClientFactory.create ( url, TestApi.class, 
    providers, true );

Demo d = proxy.getDemo ();
if ( d != null ) {
    System.out.println ( d.id + ":" + d.name );
}

//the Demo class
@XmlRootElement ( name = "demo", namespace = "http://www.myserviceapi.com" )
@XmlType ( name = "demo", namespace = "http://www.myserviceapi.com", 
    propOrder = { "name", "id" } )
@XmlAccessorType ( XmlAccessType.FIELD )
public class Demo {

    public String name;
    public int id;
}

注意:


  1. 提供程序列表是您在客户端上配置JSON提供程序的代码。特别是,您可以看到命名空间映射。这需要与服务器端配置相匹配。我对Jettison的选项知之甚少,所以我在控制编组过程中操作所有各种旋钮方面帮助不大。

  2. CXF中的Jettison是通过从JAXB编组XML来实现的。提供者进入JSON。因此,您必须确保将有效负载对象全部标记(或以其他方式配置)以编组为application / xml,然后才能将它们作为JSON进行编组。如果你知道解决这个问题的方法(除了编写你自己的消息体作者之外),我很乐意听到它。

  3. 我在服务器上使用spring,所以我的配置是所有的xml东西。实质上,您需要通过相同的过程将JSONProvider添加到具有相同命名空间配置的服务。没有那么方便的代码,但我想它会很好地反映客户端。

这有点脏例如但希望能让你前进。

This is a bit dirty as an example but will hopefully get you going.

Edit2:基于xstream的消息体编写器的示例,以避免jaxb。

An example of a message body writer that is based on xstream to avoid jaxb.

@Produces ( "application/json" )
@Consumes ( "application/json" )
@Provider
public class XstreamJsonProvider implements MessageBodyReader<Object>,
    MessageBodyWriter<Object> {

@Override
public boolean isWriteable ( Class<?> type, Type genericType, 
    Annotation[] annotations, MediaType mediaType ) {
    return MediaType.APPLICATION_JSON_TYPE.equals ( mediaType ) 
        && type.equals ( Demo.class );
}

@Override
public long getSize ( Object t, Class<?> type, Type genericType, 
    Annotation[] annotations, MediaType mediaType ) {
    // I'm being lazy - should compute the actual size
    return -1;
}

@Override
public void writeTo ( Object t, Class<?> type, Type genericType, 
    Annotation[] annotations, MediaType mediaType, 
    MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream ) 
    throws IOException, WebApplicationException {
    // deal with thread safe use of xstream, etc.
    XStream xstream = new XStream ( new JettisonMappedXmlDriver () );
    xstream.setMode ( XStream.NO_REFERENCES );
    // add safer encoding, error handling, etc.
    xstream.toXML ( t, entityStream );
}

@Override
public boolean isReadable ( Class<?> type, Type genericType, 
    Annotation[] annotations, MediaType mediaType ) {
    return MediaType.APPLICATION_JSON_TYPE.equals ( mediaType ) 
        && type.equals ( Demo.class );
}

@Override
public Object readFrom ( Class<Object> type, Type genericType, 
    Annotation[] annotations, MediaType mediaType, 
    MultivaluedMap<String, String> httpHeaders, InputStream entityStream ) 
    throws IOException, WebApplicationException {
    // add error handling, etc.
    XStream xstream = new XStream ( new JettisonMappedXmlDriver () );
    return xstream.fromXML ( entityStream );
}
}

//now your client just needs this
List providers = new ArrayList ();
XstreamJsonProvider jsonProvider = new XstreamJsonProvider ();
providers.add ( jsonProvider );
TestApi proxy = JAXRSClientFactory.create ( url, TestApi.class, 
    providers, true );

Demo d = proxy.getDemo ();
if ( d != null ) {
    System.out.println ( d.id + ":" + d.name );
}

示例代码缺少强大的媒体类型支持,错误处理,线程安全等等。但是,它应该用最少的代码解决jaxb问题。

The sample code is missing the parts for robust media type support, error handling, thread safety, etc. But, it ought to get you around the jaxb issue with minimal code.

编辑3 - 样本服务器端配置
正如我之前所说,我的服务器端是弹簧配置的。以下是一个用于在提供程序中连接的示例配置:

EDIT 3 - sample server side configuration As I said before, my server side is spring configured. Here is a sample configuration that works to wire in the provider:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
    http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">

<import resource="classpath:META-INF/cxf/cxf.xml" />

<jaxrs:server id="TestApi">
    <jaxrs:serviceBeans>
        <ref bean="testApi" />
    </jaxrs:serviceBeans>
    <jaxrs:providers>
        <bean id="xstreamJsonProvider" class="webtests.rest.XstreamJsonProvider" />
    </jaxrs:providers>
</jaxrs:server>

<bean id="testApi" class="webtests.rest.TestApi">
</bean>

</beans>

我还注意到在我使用的最新版cxf中有一个区别媒体类型,因此xstream消息体读者/作者上面的示例需要快速修改,其中isWritable / isReadable更改为:

I have also noted that in the latest rev of cxf that I'm using there is a difference in the media types, so the example above on the xstream message body reader/writer needs a quick modification where isWritable/isReadable change to:

return MediaType.APPLICATION_JSON_TYPE.getType ().equals ( mediaType.getType () )
    && MediaType.APPLICATION_JSON_TYPE.getSubtype ().equals ( mediaType.getSubtype () )
    && type.equals ( Demo.class );

编辑4 - 非弹簧配置
使用您的servlet首选容器,配置

EDIT 4 - non-spring configuration Using your servlet container of choice, configure

org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet

至少有2个init参数:

with at least 2 init params of:

jaxrs.serviceClasses
jaxrs.providers

其中serviceClasses是以空格分隔的列表您想要绑定的服务实现,例如上面提到的TestApi,提供者是一个空格分隔的消息体提供者列表,例如上面提到的XstreamJsonProvider。在tomcat中,您可以将以下内容添加到web.xml:

where the serviceClasses is a space separated list of the service implementations you want bound, such as the TestApi mentioned above and the providers is a space separated list of message body providers, such as the XstreamJsonProvider mentioned above. In tomcat you might add the following to web.xml:

<servlet>
    <servlet-name>cxfservlet</servlet-name>
    <servlet-class>org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet</servlet-class>
    <init-param>
        <param-name>jaxrs.serviceClasses</param-name>
        <param-value>webtests.rest.TestApi</param-value>
    </init-param>
    <init-param>
        <param-name>jaxrs.providers</param-name>
        <param-value>webtests.rest.XstreamJsonProvider</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

这是在没有弹簧的情况下运行它的最快捷方式。如果您没有使用servlet容器,则需要使用XstreamJsonProvider实例配置JAXRSServerFactoryBean.setProviders,并通过JAXRSServerFactoryBean.setResourceProvider方法设置服务实现。检查CXFNonSpringJaxrsServlet.init方法,看看他们在servlet容器中设置时是如何做到的。

That is pretty much the quickest way to run it without spring. If you are not using a servlet container, you would need to configure the JAXRSServerFactoryBean.setProviders with an instance of XstreamJsonProvider and set the service implementation via the JAXRSServerFactoryBean.setResourceProvider method. Check the CXFNonSpringJaxrsServlet.init method to see how they do it when setup in a servlet container.

无论你的情况如何,这都应该让你顺利。

That ought to get you going no matter your scenario.

这篇关于CXF:没有为类找到消息正文编写器 - 自动映射非简单资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆