requestmapping ejemplo spring spring-mvc post http-post http-request-parameters

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.

  1. Spring vincula el cuerpo de la solicitud al parámetro anotado con @RequestBody .

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

  3. Los parámetros se resuelven en orden. El @RequestBody se procesa primero. Spring consumirá todos los HttpServletRequest InputStream . Cuando intenta resolver @RequestParam , que por defecto es required , 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.

  4. El manejador de @RequestParam actúa primero, leyendo lo que puede de HttpServletRequest 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 valor abc asignado al name 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.

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

  6. El controlador para @RequestParam lee tanto del cuerpo como de la cadena de consulta de URL. Por lo general, los pondría en un Map , pero como el parámetro es del tipo String , Spring serializará el Map 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.

  1. igual que su resultado
  2. igual que su resultado
  3. dio body= "name=abc" , y name = "abc"
  4. Igual que 3.
  5. body ="name=abc" , name = "xyz,abc"
  6. 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:

  1. Reemplazando StringHttpMessageConverter . Copie / sobrescriba el método de ajuste de clase original readInternal() .
  2. Envolviendo HttpServletRequest sobrescribiendo los getInputStream() , getReader() y getParameter*() .

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