Spring MVC: Objeto complejo como GET @RequestParam


Supongamos que tengo una página que enumera los objetos en una tabla y necesito poner un formulario para filtrar la tabla. El filtro se envía como un Ajax GET a una URL como esa: http://foo.com/system/controller/action?page=1&prop1=x&prop2=y&prop3=z

Y en lugar de tener muchos parámetros en mi Controlador como:

@RequestMapping(value = "/action")
public @ResponseBody List<MyObject> myAction(
    @RequestParam(value = "page", required = false) int page,
    @RequestParam(value = "prop1", required = false) String prop1,
    @RequestParam(value = "prop2", required = false) String prop2,
    @RequestParam(value = "prop3", required = false) String prop3) { ... }

Y suponiendo que tengo miObjeto como:

public class MyObject {
    private String prop1;
    private String prop2;
    private String prop3;

    //Getters and setters
    ...
}

Quiero hacer algo como:

@RequestMapping(value = "/action")
public @ResponseBody List<MyObject> myAction(
    @RequestParam(value = "page", required = false) int page,
    @RequestParam(value = "myObject", required = false) MyObject myObject,) { ... }

Es posible? ¿Cómo puedo hacer eso?

Author: renanlf, 2013-06-05

3 answers

Puede hacer eso, simplemente elimine la anotación @RequestParam, Spring enlazará limpiamente sus parámetros de solicitud a su instancia de clase:

public @ResponseBody List<MyObject> myAction(
    @RequestParam(value = "page", required = false) int page,
    MyObject myObject)
 173
Author: Biju Kunjummen,
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
2013-06-05 14:16:49

Voy a añadir un pequeño ejemplo de mí.

La clase DTO:

public class SearchDTO {
    private Long id[];

    public Long[] getId() {
        return id;
    }

    public void setId(Long[] id) {
        this.id = id;
    }
    // reflection toString from apache commons
    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

Solicitud de asignación dentro de la clase del controlador:

@RequestMapping(value="/handle", method=RequestMethod.GET)
@ResponseBody
public String handleRequest(SearchDTO search) {
    LOG.info("criteria: {}", search);
    return "OK";
}

Consulta:

http://localhost:8080/app/handle?id=353,234

Resultado:

[http-apr-8080-exec-7] INFO  c.g.g.r.f.w.ExampleController.handleRequest:59 - criteria: SearchDTO[id={353,234}]

Espero que ayude :)

UPDATE / KOTLIN

Porque actualmente estoy trabajando mucho con Kotlin si alguien quiere definir DTO similar la clase en Kotlin debería tener la siguiente forma:

class SearchDTO {
    var id: Array<Long>? = arrayOf()

    override fun toString(): String {
        // to string implementation
    }
}

Con la clase data como esta:

data class SearchDTO(var id: Array<Long> = arrayOf())

La Primavera (tested in Boot) devuelve el siguiente error para la solicitud mencionada en la respuesta:

"Error al convertir el valor del tipo 'java.lang.String [] ' al tipo requerido 'java.lang.Long []'; la excepción anidada es Java.lang.NumberFormatException: Para la cadena de entrada: \"353,234\""

La clase data solo funcionará para el siguiente formulario de parámetros de solicitud:

http://localhost:8080/handle?id=353&id=234

Sea consciente de esto!

 34
Author: Przemek Nowak,
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-07-18 09:00:51

Tengo un problema muy similar. En realidad el problema es más profundo como pensaba. Estoy usando jquery $.post que usa Content-Type:application/x-www-form-urlencoded; charset=UTF-8 como predeterminado. Desafortunadamente, basé mi sistema en eso y cuando necesitaba un objeto complejo como @RequestParam no podía simplemente hacerlo realidad.

En mi caso estoy tratando de enviar preferencias de usuario con algo como;

 $.post("/updatePreferences",  
    {id: 'pr', preferences: p}, 
    function (response) {
 ...

En el lado del cliente los datos sin procesar reales enviados al servidor son;

...
id=pr&preferences%5BuserId%5D=1005012365&preferences%5Baudio%5D=false&preferences%5Btooltip%5D=true&preferences%5Blanguage%5D=en
...

Analizado como;

id:pr
preferences[userId]:1005012365
preferences[audio]:false
preferences[tooltip]:true
preferences[language]:en

Y el lado del servidor is;

@RequestMapping(value = "/updatePreferences")
public
@ResponseBody
Object updatePreferences(@RequestParam("id") String id, @RequestParam("preferences") UserPreferences preferences) {

    ...
        return someService.call(preferences);
    ...
}

Probé @ModelAttribute, añadí setter/getters, constructores con todas las posibilidades a UserPreferences pero ninguna posibilidad ya que reconoció los datos enviados como 5 parámetros pero de hecho el método mapeado tiene solo 2 parámetros. También probé la solución de Biju, sin embargo, lo que sucede es que, spring crea un objeto UserPreferences con constructor predeterminado y no rellena los datos.

Resolví el problema enviando una cadena JSon de las preferencias desde el lado del cliente y lo manejé como si fuera una cadena en el lado del servidor;

Cliente:

 $.post("/updatePreferences",  
    {id: 'pr', preferences: JSON.stringify(p)}, 
    function (response) {
 ...

Servidor:

@RequestMapping(value = "/updatePreferences")
public
@ResponseBody
Object updatePreferences(@RequestParam("id") String id, @RequestParam("preferences") String preferencesJSon) {


        String ret = null;
        ObjectMapper mapper = new ObjectMapper();
        try {
            UserPreferences userPreferences = mapper.readValue(preferencesJSon, UserPreferences.class);
            return someService.call(userPreferences);
        } catch (IOException e) {
            e.printStackTrace();
        }
}

Para resumir, hice la conversión manualmente dentro del método REST. En mi opinión, la razón por la que Spring no reconoce los datos enviados es el tipo de contenido.

 6
Author: hevi,
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
2014-03-26 01:47:56