一尘不染

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

json

我已经成功地建立了一个快速测试,以创建一个“类似REST的”服务,该服务返回序列化为JSON的对象,并且这非常容易且快速(基于本文)。

但是,虽然返回桃子一样的JSON格式的对象很容易,但我还没有看到任何处理非原语输入参数的示例。如何传递复杂的对象作为参数?我正在使用Apache
CXF,但也欢迎使用其他框架(例如杰克逊)的示例:)

客户端可能类似于构建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,我是否需要在函数定义中进行很多更改?


阅读 937

收藏
2020-07-27

共1个答案

一尘不染

深入研究后,我很快发现基本上有两个选择:

选项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

您可以提供一些特殊的字符串格式,将您的对象打包到其中,然后实现采用字符串的构造函数,将采用该字符串并创建对象的类中的static
valueOf(String s)或static 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;
   }
}

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

2019年评论:
看到这个答案在2019年仍然很受欢迎,我觉得我应该发表评论。事后看来,我不会建议选项2,因为仅为了能够执行GET调用而执行这些步骤会增加可能不值得的复杂性。如果您的服务接受了如此复杂的输入,由于输入的排列数量众多,您可能仍将无法使用客户端缓存。我只想在服务器上配置正确的跨域共享(CORS)标头,然后输入输入。然后集中精力在服务器上缓存所有内容。

2020-07-27