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

时间:2021-09-25 21:06:13

I have successfully set up a quick test of creating a "REST-like" service that returns an object serialized to JSON, and that was quite easy and quick (based on this article).

我已经成功地设置了一个创建“rest式”服务的快速测试,该服务返回一个序列化为JSON的对象,这非常简单和快速(基于本文)。

But while returning JSON-ified objects was easy as peach, I have yet to see any examples dealing with input parameters that are not primitives. How can I pass in a complex object as an argument? I am using Apache CXF, but examples using other frameworks like Jackson are welcome too :)

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

Client side would probably be something like building a javascript object, pass it into JSON.stringify(complexObj), and pass that string as one of the parameters.

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

The service would probably look something like this

服务大概是这样的。

@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 {
    ...
    }
}

Sending serialized objects as parameters would probably quickly touch the 2KB URL-limit imposed by Internet Explorer. Would you recommend using POST in these cases, and would I need to change much in the function definitions?

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

3 个解决方案

#1


27  

After digging a bit I quickly found out there are basically two options:

经过一番挖掘,我很快发现基本上有两种选择:

Option 1

You pass a "wrapper object" containing all the other parameters to the service. You might need to annotate this wrapper class with JAXB annotations like @XmlRootElement in order for this to work with the Jettison based provider, but if you use Jackson in stead there is no need. Just set the content type to the right type and the right message body reader will be invoked. This will only work for POST type services of course (AFAIK).

将包含所有其他参数的“包装器对象”传递给服务。您可能需要使用@XmlRootElement这样的JAXB注释来注释这个包装类,以便与基于Jettison的提供者一起工作,但是如果您使用Jackson作为替代,则不需要这样做。只要将内容类型设置为正确的类型,就会调用正确的消息体读取器。当然,这只适用于POST类型的服务(AFAIK)。

Example

This is just an example of turning the service mentioned in the original question into one using a wrapper object.

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

@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;
}

Option 2

You can provide some special string format that you pack your objects into and then implement either a constructor taking a string, a static valueOf(String s) or a static fromString(String s) in the class that will take this string and create an object from it. Or quite similar, create a ParameterHandler that does exactly the same.

您可以提供一些特殊的字符串格式,您可以将对象打包到其中,然后实现构造函数获取字符串、静态valueOf(string s)或类中的静态fromString(string s),它们将获取该字符串并从中创建一个对象。或者非常类似,创建一个做完全相同的参数处理程序。

AFAIK, only the second version will allow you to call your services from a browser using JSONP (since JSONP is a trick restricted to GET). I chose this route to be able to pass arrays of complex objects in the URI.

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

As an example of how this works, take the following domain class and service

以以下域类和服务为例,说明这是如何工作的

Example

@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;
   }
}

A URI http://my-server.com/myService?a={"foo":1, "bar":2}&a={"foo":100, "bar":200} would in this case be deserialized into an array composed of two MyClass objects.

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

#2


1  

the best and simplest solution is to send your object as a json string and in server side implement a method which will decode that json and map to the specified object as per your need.. and yes it`s better to use POST.

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

#3


0  

The accepted answer is missing @BeanParam. See https://docs.jboss.org/resteasy/docs/3.0-rc-1/javadocs/javax/ws/rs/BeanParam.html for further details. It allows you to define query params inside a wrapper object. E.g.

公认的答案是缺少@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) {
    ...
}

#1


27  

After digging a bit I quickly found out there are basically two options:

经过一番挖掘,我很快发现基本上有两种选择:

Option 1

You pass a "wrapper object" containing all the other parameters to the service. You might need to annotate this wrapper class with JAXB annotations like @XmlRootElement in order for this to work with the Jettison based provider, but if you use Jackson in stead there is no need. Just set the content type to the right type and the right message body reader will be invoked. This will only work for POST type services of course (AFAIK).

将包含所有其他参数的“包装器对象”传递给服务。您可能需要使用@XmlRootElement这样的JAXB注释来注释这个包装类,以便与基于Jettison的提供者一起工作,但是如果您使用Jackson作为替代,则不需要这样做。只要将内容类型设置为正确的类型,就会调用正确的消息体读取器。当然,这只适用于POST类型的服务(AFAIK)。

Example

This is just an example of turning the service mentioned in the original question into one using a wrapper object.

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

@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;
}

Option 2

You can provide some special string format that you pack your objects into and then implement either a constructor taking a string, a static valueOf(String s) or a static fromString(String s) in the class that will take this string and create an object from it. Or quite similar, create a ParameterHandler that does exactly the same.

您可以提供一些特殊的字符串格式,您可以将对象打包到其中,然后实现构造函数获取字符串、静态valueOf(string s)或类中的静态fromString(string s),它们将获取该字符串并从中创建一个对象。或者非常类似,创建一个做完全相同的参数处理程序。

AFAIK, only the second version will allow you to call your services from a browser using JSONP (since JSONP is a trick restricted to GET). I chose this route to be able to pass arrays of complex objects in the URI.

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

As an example of how this works, take the following domain class and service

以以下域类和服务为例,说明这是如何工作的

Example

@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;
   }
}

A URI http://my-server.com/myService?a={"foo":1, "bar":2}&a={"foo":100, "bar":200} would in this case be deserialized into an array composed of two MyClass objects.

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

#2


1  

the best and simplest solution is to send your object as a json string and in server side implement a method which will decode that json and map to the specified object as per your need.. and yes it`s better to use POST.

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

#3


0  

The accepted answer is missing @BeanParam. See https://docs.jboss.org/resteasy/docs/3.0-rc-1/javadocs/javax/ws/rs/BeanParam.html for further details. It allows you to define query params inside a wrapper object. E.g.

公认的答案是缺少@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) {
    ...
}