ejemplo - requestmapping spring boot
Spring MVC: por qué no pueden usar @RequestBody y @RequestParam juntos (4)
Usar el cliente dev de HTTP con la solicitud de publicación y la aplicación de tipo de contenido / x-www-form-urlencoded
1) Solo @RequestBody
Solicitud - localhost: 8080 / SpringMVC / welcome In Body - name = abc
Código-
@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestBody String body, Model model) {
model.addAttribute("message", body);
return "hello";
}
// Da cuerpo como ''nombre = abc'' como se esperaba
2) Solo @RequestParam
Solicitud - localhost: 8080 / SpringMVC / welcome In Body - name = abc
Código-
@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestParam String name, Model model) {
model.addAttribute("name", name);
return "hello";
}
// Da nombre como ''abc'' como se esperaba
3) Ambos juntos
Solicitud - localhost: 8080 / SpringMVC / welcome In Body - name = abc
Código-
@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestBody String body, @RequestParam String name, Model model) {
model.addAttribute("name", name);
model.addAttribute("message", body);
return "hello";
}
// Código de error HTTP 400 - La solicitud enviada por el cliente fue sintácticamente incorrecta.
4) Arriba, con la posición de params cambiada
Solicitud - localhost: 8080 / SpringMVC / welcome In Body - name = abc
Código-
@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestParam String name, @RequestBody String body, Model model) {
model.addAttribute("name", name);
model.addAttribute("message", body);
return "hello";
}
// No hay error. El nombre es ''abc''. el cuerpo está vacío
5) Juntos pero obtienen los parámetros de URL de tipo
Solicitud - localhost: 8080 / SpringMVC / welcome? Name = xyz In Body - name = abc
Código-
@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestBody String body, @RequestParam String name, Model model) {
model.addAttribute("name", name);
model.addAttribute("message", body);
return "hello";
}
// el nombre es ''xyz'' y el cuerpo es ''name = abc''
6) Igual que 5) pero con la posición de los parámetros cambiada
Código -
@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestParam String name, @RequestBody String body, Model model) {
model.addAttribute("name", name);
model.addAttribute("message", body);
return "hello";
}
// nombre = ''xyz, abc'' cuerpo está vacío
¿Alguien puede explicar este comportamiento?
Los estados @RequestBody
javadoc
La anotación que indica un parámetro de método debe vincularse al cuerpo de la solicitud web.
Utiliza instancias registradas de HttpMessageConverter
para deserializar el cuerpo de la solicitud en un objeto del tipo de parámetro anotado.
Y @RequestParam
Anotación que indica que un parámetro de método debe vincularse a un parámetro de solicitud web.
Spring vincula el cuerpo de la solicitud al parámetro anotado con
@RequestBody
.Spring vincula los parámetros de solicitud desde el cuerpo de la solicitud (parámetros codificados en url) a su parámetro de método. Spring usará el nombre del parámetro, es decir.
name
, para mapear el parámetro.Los parámetros se resuelven en orden. El
@RequestBody
se procesa primero. Spring consumirá todos losHttpServletRequest
InputStream
. Cuando intenta resolver@RequestParam
, que por defecto esrequired
, no hay ningún parámetro de solicitud en la cadena de consulta o lo que queda del cuerpo de la solicitud, es decir. nada. Por lo tanto, falla con 400 porque el método del manejador no puede manejar correctamente la solicitud.El manejador de
@RequestParam
actúa primero, leyendo lo que puede deHttpServletRequest
InputStream
para mapear el parámetro de solicitud, es decir. toda la cadena de consulta / parámetros codificados en url. Lo hace y obtiene el valorabc
asignado alname
parámetro. Cuando se ejecuta el controlador para@RequestBody
, no queda nada en el cuerpo de la solicitud, por lo que el argumento utilizado es la cadena vacía.El controlador de
@RequestBody
lee el cuerpo y lo vincula al parámetro. El controlador de@RequestParam
puede obtener el parámetro de solicitud de la cadena de consulta de URL.El controlador para
@RequestParam
lee tanto del cuerpo como de la cadena de consulta de URL. Por lo general, los pondría en unMap
, pero como el parámetro es del tipoString
, Spring serializará elMap
como valores separados por comas. El controlador de@RequestBody
, una vez más, no tiene nada que leer del cuerpo.
Sé que es demasiado tarde para responder a esta pregunta, pero aún así podría ayudar a los lectores. Parece problemas de versión. Ejecuto todas estas pruebas con la primavera 4.1.4 y encontré que el orden de @RequestBody
y @RequestParam
no importa.
- igual que su resultado
- igual que su resultado
- dio
body= "name=abc"
, yname = "abc"
- Igual que 3.
-
body ="name=abc"
,name = "xyz,abc"
- lo mismo que 5.
Sucede debido a la especificación de Servlet no muy directa. Si está trabajando con una implementación HttpServletRequest
nativa, no puede obtener el cuerpo de la codificación URL y los parámetros. Spring hace algunas soluciones, que lo hacen aún más extraño y no transparente.
En tales casos, Spring (versión 3.2.4) vuelve a procesar un cuerpo para usted utilizando datos del método getParameterMap()
. Mezcla los parámetros GET y POST y rompe el orden de los parámetros. La clase, que es responsable del caos, es ServletServerHttpRequest
. Desafortunadamente no puede ser reemplazado, pero puede ser la clase StringHttpMessageConverter
.
La solución limpia lamentablemente no es simple:
- Reemplazando
StringHttpMessageConverter
. Copie / sobrescriba el método de ajuste de clase originalreadInternal()
. - Envolviendo
HttpServletRequest
sobrescribiendo losgetInputStream()
,getReader()
ygetParameter*()
.
En el método StringHttpMessageConverter # readInternal se debe usar el siguiente código:
if (inputMessage instanceof ServletServerHttpRequest) {
ServletServerHttpRequest oo = (ServletServerHttpRequest)inputMessage;
input = oo.getServletRequest().getInputStream();
} else {
input = inputMessage.getBody();
}
Entonces el convertidor debe estar registrado en el contexto.
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true/false">
<bean class="my-new-converter-class"/>
</mvc:message-converters>
</mvc:annotation-driven>
El paso dos se describe aquí: Http Servlet request pierde parámetros del cuerpo POST después de leerlo una vez
También podría simplemente cambiar el estado obligatorio predeterminado de @RequestParam a falso para que no se genere el código de estado de respuesta HTTP 400. Esto te permitirá colocar las Anotaciones en el orden que desees.
@RequestParam(required = false)String name