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 GET Ajax a una URL como esta: 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 MyObject 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?
Absolutamente puedes hacer eso, simplemente elimina la @RequestParam
anotación, Spring vinculará limpiamente los parámetros de tu solicitud a tu instancia de clase:
public @ResponseBody List<MyObject> myAction(
@RequestParam(value = "page", required = false) int page,
MyObject myObject)
Agregaré un breve ejemplo mío.
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);
}
}
Solicitar mapeo dentro de la clase de 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 :)
ACTUALIZACIÓN / KOTLIN
Debido a que actualmente estoy trabajando mucho con Kotlin, si alguien quiere definir un 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 data
clase como esta:
data class SearchDTO(var id: Array<Long> = arrayOf())
Spring (probado en 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 de datos funcionará solo para el siguiente formulario de parámetros de solicitud:
http://localhost:8080/handle?id=353&id=234
¡Ten en cuenta esto!
Dado que la pregunta sobre cómo configurar los campos como obligatorios aparece debajo de cada publicación, escribí un pequeño ejemplo sobre cómo configurar los campos como obligatorios:
public class ExampleDTO {
@NotNull
private String mandatoryParam;
private String optionalParam;
@DateTimeFormat(iso = ISO.DATE) //accept Dates only in YYYY-MM-DD
@NotNull
private LocalDate testDate;
public String getMandatoryParam() {
return mandatoryParam;
}
public void setMandatoryParam(String mandatoryParam) {
this.mandatoryParam = mandatoryParam;
}
public String getOptionalParam() {
return optionalParam;
}
public void setOptionalParam(String optionalParam) {
this.optionalParam = optionalParam;
}
public LocalDate getTestDate() {
return testDate;
}
public void setTestDate(LocalDate testDate) {
this.testDate = testDate;
}
}
//Add this to your rest controller class
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String testComplexObject (@Valid ExampleDTO e){
System.out.println(e.getMandatoryParam() + " " + e.getTestDate());
return "Does this work?";
}