如何将复杂对象作为参数传递给RESTful服务?

时间:2011-05-19 15:44:19

标签: json cxf deserialization argument-passing

我已经成功设置了一个快速测试,创建一个“类似REST”的服务,返回一个序列化为JSON的对象,这非常简单快捷(基于this article)。

但是虽然返回JSON-ified对象很容易像桃子一样,但我还没有看到任何处理非基本输入参数的例子。如何将复杂对象作为参数传递?我正在使用Apache CXF,但是也欢迎使用像Jackson这样的其他框架的示例:)

客户端可能类似于构建javascript对象,将其传递给JSON.stringify(complexObj),并将该字符串作为参数之一传递。

该服务可能看起来像这样

@Service("myService")
class RestService {
    @GET
    @Produces("application/json")
    @Path("/fooBar")
    public Result fooBar(@QueryParam("foo") double foo, @QueryParam("bar") double bar,
        @QueryParam("object") MyComplex object) throws WebServiceException {
    ...
    }
}

将序列化对象作为参数发送可能会快速触及Internet Explorer强加的2KB URL限制。您是否建议在这些情况下使用POST,我是否需要在函数定义中进行更改?

3 个答案:

答案 0 :(得分:30)

挖了一下后我很快发现基本上有两种选择:

选项1

将包含所有其他参数的“包装器对象”传递给服务。您可能需要使用@XmlRootElement等JAXB注释来注释此包装类,以便与基于Jettison的提供程序一起使用,但如果您使用Jackson而不需要。只需将内容类型设置为正确的类型,即可调用正确的邮件正文阅读器。 这仅适用于POST类型的服务(AFAIK)。

实施例

这只是使用包装器对象将原始问题中提到的服务转换为一个的示例。

@Service("myService")
class RestService {

    @POST
    @Produces("application/json")
    @Path("/fooBar")
    public Result fooBar(

          /** 
          * Using "" will inject all form params directly into a ParamsWrapper 
          * @see http://cxf.apache.org/docs/jax-rs-basics.html
          */
          @FormParam("") FooBarParamsWrapper wrapper

        ) throws WebServiceException {
            doSomething(wrapper.foo);
    }
}

class ParamsWrapper {
  double foo, bar;
  MyComplexObject object;
}

选项2

您可以提供一些特殊的字符串格式,将对象打包到其中,然后实现一个构造函数,该构造函数接受一个字符串,一个静态valueOf(String s)或一个静态fromString(String s),该类将接受此字符串和从中创建一个对象。或者非常相似,创建一个完全相同的ParameterHandler。

AFAIK,只有第二个版本允许您使用JSONP从浏览器调用您的服务(因为JSONP是限制为GET的技巧)。我选择此路由以便能够在URI中传递复杂对象的数组。

作为其工作原理的示例,请使用以下域类和服务

实施例

@GET
@Path("myService")
public void myService(@QueryParam("a") MyClass [] myVals) {
    //do something
}

class MyClass {
    public int foo;
    public int bar;

   /** Deserializes an Object of class MyClass from its JSON representation */
   public static MyClass fromString(String jsonRepresentation) {
           ObjectMapper mapper = new ObjectMapper(); //Jackson's JSON marshaller
           MyClass o= null;
           try {
                   o = mapper.readValue(jsonRepresentation, MyClass.class );
           } catch (IOException e) {
                    throw new WebApplicationException()
           }
           return o;
   }
}

在这种情况下,URI http://my-server.com/myService?a={"foo":1, "bar":2}&a={"foo":100, "bar":200}将被反序列化为由两个MyClass对象组成的数组。

答案 1 :(得分:3)

接受的答案缺少@BeanParam。看到 https://docs.jboss.org/resteasy/docs/3.0-rc-1/javadocs/javax/ws/rs/BeanParam.html 有关更多详细信息。它允许您在包装对象内定义查询参数。 例如

public class TestPOJO {

    @QueryParam("someQueryParam")
    private boolean someQueryParam;

    public boolean isSomeQueryParam() {
        return someQueryParam;
    }

    public boolean setSomeQueryParam(boolean value) {
        this.someQueryParam = value;
    }
}

... // inside the Resource class
@GET
@Path("test")
public Response getTest(@BeanParam TestPOJO testPOJO) {
    ...
}

答案 2 :(得分:1)

最好和最简单的解决方案是将您的对象作为json字符串发送,并在服务器端实现一个方法,该方法将解码该json并根据您的需要映射到指定的对象..是的,最好使用POST