¿Cómo puedo pasar objetos complejos como argumentos a un servicio RESTful?


He configurado con éxito una prueba rápida de crear un servicio "similar a REST" que devuelve un objeto serializado a JSON, y eso fue bastante fácil y rápido (basado en este artículo).

Pero si bien devolver objetos JSON-ifed fue tan fácil como peach, todavía no he visto ningún ejemplo que trate con parámetros de entrada que no sean primitivos. ¿Cómo puedo pasar un objeto complejo como argumento? Estoy usando Apache CXF, pero los ejemplos que usan otros frameworks como Jackson también son bienvenidos :)

El lado del cliente probablemente sería algo como construir un objeto javascript, pasarlo a JSON.stringify (complexObj), y pasa esa cadena como uno de los parámetros.

El servicio, probablemente se vería algo como esto

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

Enviar objetos serializados como parámetros probablemente tocaría rápidamente el límite de URL de 2 KB impuesto por Internet Explorer. ¿Recomendaría usar POST en estos casos, y tendría que cambiar mucho en las definiciones de la función?

Author: oligofren, 2011-05-19

3 answers

Después de cavar un poco rápidamente descubrí que hay básicamente dos opciones:

Opción 1

Pasa un "objeto wrapper" que contiene todos los demás parámetros al servicio. Es posible que necesite anotar esta clase wrapper con anotaciones JAXB como @XmlRootElement para que esto funcione con el proveedor basado en Jettison, pero si usa Jackson en su lugar no es necesario. Simplemente establezca el tipo de contenido en el tipo correcto y se invocará el lector de cuerpo del mensaje correcto. Esta voluntad solo trabaja para servicios de tipo POST por supuesto (AFAIK).

Ejemplo

Esto es solo un ejemplo de convertir el servicio mencionado en la pregunta original en uno usando un objeto wrapper.

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

Opción 2

Puede proporcionar algún formato de cadena especial en el que empaquete sus objetos y luego implementar un constructor que tome una cadena, un valueOf estático(Cadena s) o un fromString estático(Cadena s) en la clase que tomará esta cadena y creará un objeto a partir de ella. O muy similar, crear un ParameterHandler que hace exactamente lo mismo.

AFAIK, solo la segunda versión le permitirá llamar a sus servicios desde un navegador utilizando JSONP (ya que JSONP es un truco restringido para OBTENER). Elegí esta ruta para poder pasar matrices de objetos complejos en la URI.

Como ejemplo de cómo funciona esto, tome la siguiente clase de dominio y servicio

Ejemplo

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

UN URI http://my-server.com/myService?a={"foo":1, "bar":2}&a={"foo":100, "bar":200} en este caso se deserializa en una matriz compuesta de dos objetos MyClass.

 28
Author: oligofren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-07 11:26:49

La mejor y más simple solución es enviar su objeto como una cadena json y en el lado del servidor implementar un método que decodifique ese json y mapear al objeto especificado según su necesidad.. y sí, es mejor usar POST.

 1
Author: Koustuv Ganguly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-15 19:06:18

Falta la respuesta aceptada @BeanParam. Ver https://docs.jboss.org/resteasy/docs/3.0-rc-1/javadocs/javax/ws/rs/BeanParam.html para más detalles. Le permite definir parámetros de consulta dentro de un objeto wrapper. Por ejemplo,

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
Author: r_ganaus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-21 13:37:59