Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I pass complex objects as arguments to a RESTful service?

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).

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 :)

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.

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?

like image 931
oligofren Avatar asked May 19 '11 15:05

oligofren


People also ask

Can we pass object as parameter in REST API?

OpenAPI 2 doesn't support objects as query parameters; only primitive values and arrays of primitives are supported. Because of that, we'll instead want to define our JSON parameter as a string.

How do you pass an object as a parameter in Restful Web Services in Java?

You can not pass your Java Object to query param. You should convert your java object into JSON String and then pass it in query param. You need not to cast manually object into JSON String. There is already created Jackson Library that wil convert your Object to JSON String and vice versa.

Is it possible to pass multiple complex types in Web API?

Web API doesn't allow you to pass multiple complex objects in the method signature of a Web API controller method — you can post only a single value to a Web API action method. This value in turn can even be a complex object.


2 Answers

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).

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.

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.

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.

2019 comment: Seeing that this answer still gets some hits in 2019, I feel I should comment. In hindsight, I would not recomment option 2, as going through these steps just to be able to be able to do GET calls adds complexity that's probably not worth it. If your service takes such complex input, you will probably not be able to utilize client side caching anyway, due to the number of permutations of your input. I'd just go for configuring proper Cross-Origin-Sharing (CORS) headers on the server and POST the input. Then focus on caching whatever you can on the server.

like image 100
oligofren Avatar answered Sep 21 '22 14:09

oligofren


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.

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) {     ... } 
like image 29
r_ganaus Avatar answered Sep 18 '22 14:09

r_ganaus