Spring MVC – Pourquoi ne pas pouvoir utiliser @RequestBody et @RequestParam ensemble

Utilisation du client de développement HTTP avec la demande de publication et l’application Content-Type / x-www-form-urlencoded

1) Seulement @RequestBody

Demande – localhost: 8080 / SpringMVC / welcome In Body – nom = abc

Code-

@RequestMapping(method = RequestMethod.POST) public Ssortingng printWelcome(@RequestBody Ssortingng body, Model model) { model.addAtsortingbute("message", body); return "hello"; } 

// Donne le corps comme ‘nom = abc’ comme prévu

2) Seulement @RequestParam

Demande – localhost: 8080 / SpringMVC / welcome In Body – nom = abc

Code-

 @RequestMapping(method = RequestMethod.POST) public Ssortingng printWelcome(@RequestParam Ssortingng name, Model model) { model.addAtsortingbute("name", name); return "hello"; } 

// Donne le nom comme ‘abc’ comme prévu

3) Les deux ensemble

Demande – localhost: 8080 / SpringMVC / welcome In Body – nom = abc

Code-

 @RequestMapping(method = RequestMethod.POST) public Ssortingng printWelcome(@RequestBody Ssortingng body, @RequestParam Ssortingng name, Model model) { model.addAtsortingbute("name", name); model.addAtsortingbute("message", body); return "hello"; } 

// HTTP Error Code 400 – La requête envoyée par le client était syntaxiquement incorrecte.

4) Au-dessus avec la position des parameters modifiée

Demande – localhost: 8080 / SpringMVC / welcome In Body – nom = abc

Code-

 @RequestMapping(method = RequestMethod.POST) public Ssortingng printWelcome(@RequestParam Ssortingng name, @RequestBody Ssortingng body, Model model) { model.addAtsortingbute("name", name); model.addAtsortingbute("message", body); return "hello"; } 

// Pas d’erreur. Le nom est ‘abc’. le corps est vide

5) Ensemble mais obtenez les parameters d’url de type

Request – localhost: 8080 / SpringMVC / welcome? Name = xyz In Body – nom = abc

Code-

 @RequestMapping(method = RequestMethod.POST) public Ssortingng printWelcome(@RequestBody Ssortingng body, @RequestParam Ssortingng name, Model model) { model.addAtsortingbute("name", name); model.addAtsortingbute("message", body); return "hello"; } 

// name est ‘xyz’ et le corps est ‘name = abc’

6) Identique à 5) mais avec la position des parameters modifiée

Code –

 @RequestMapping(method = RequestMethod.POST) public Ssortingng printWelcome(@RequestParam Ssortingng name, @RequestBody Ssortingng body, Model model) { model.addAtsortingbute("name", name); model.addAtsortingbute("message", body); return "hello"; } 

// nom = ‘xyz, abc’ le corps est vide

Quelqu’un peut-il expliquer ce comportement?

L’ @RequestBody javadoc de @RequestBody

L’annotation indiquant un paramètre de méthode doit être liée au corps de la requête Web.

Il utilise des instances enregistrées de HttpMessageConverter pour désérialiser le corps de la requête dans un object du type de paramètre annoté.

Et @RequestParam

Annotation indiquant qu’un paramètre de méthode doit être lié à un paramètre de requête Web.

  1. Spring lie le corps de la requête au paramètre annoté avec @RequestBody .

  2. Spring lie les parameters de requête du corps de la requête (parameters encodés en URL) à votre paramètre de méthode. Spring utilisera le nom du paramètre, c.-à-d. name , pour mapper le paramètre.

  3. Les parameters sont résolus dans l’ordre. Le @RequestBody est traité en premier. Spring consumra tout le InputStream HttpServletRequest . Quand il essaie ensuite de résoudre le @RequestParam , qui est required par défaut, il n’y a aucun paramètre de requête dans la chaîne de requête ou ce qui rest du corps de la requête, c.-à-d. rien. Il échoue donc avec 400 car la requête ne peut pas être gérée correctement par la méthode du gestionnaire.

  4. Le gestionnaire de @RequestParam agit en premier, en lisant ce que peut faire le InputStream HttpServletRequest pour mapper le paramètre de requête, c.-à-d. la chaîne de requête entière / les parameters encodés par url. Il le fait et obtient la valeur abc mappée sur le name du paramètre. Lorsque le gestionnaire de @RequestBody s’exécute, il ne rest plus rien dans le corps de la requête. L’argument utilisé est donc la chaîne vide.

  5. Le gestionnaire de @RequestBody lit le corps et le lie au paramètre. Le gestionnaire de @RequestParam peut alors obtenir le paramètre de requête à partir de la chaîne de requête URL.

  6. Le gestionnaire de @RequestParam lit à la fois le corps et la requête URL Ssortingng. Il les mettrait généralement dans une Map , mais comme le paramètre est de type Ssortingng , Spring sérialisera la Map tant que valeurs séparées par des virgules. Le gestionnaire de @RequestBody alors plus rien à lire dans le corps.

Je sais qu’il est trop tard pour répondre à cette question, mais cela pourrait néanmoins aider les lecteurs. Il semble que les problèmes de version. Je lance tous ces tests avec le spring 4.1.4 et j’ai trouvé que l’ordre de @RequestBody et @RequestParam importait peu.

  1. même chose que votre résultat
  2. même chose que votre résultat
  3. donne body= "name=abc" , et name = "abc"
  4. Identique à 3.
  5. body ="name=abc" , name = "xyz,abc"
  6. identique à 5.

Vous pouvez également modifier le statut requirejs par défaut de @RequestParam en false pour que le code d’état de réponse HTTP 400 ne soit pas généré. Cela vous permettra de placer les annotations dans n’importe quel ordre.

 @RequestParam(required = false)Ssortingng name 

Cela se produit en raison de la spécification Servlet pas très simple. Si vous travaillez avec une implémentation native HttpServletRequest vous ne pouvez pas obtenir à la fois le corps du codage URL et les parameters. Le spring fait des solutions de contournement, ce qui le rend encore plus étrange et non transparent.

Dans ce cas, Spring (version 3.2.4) restitue un corps pour vous en utilisant les données de la méthode getParameterMap() . Il mélange les parameters GET et POST et rompt l’ordre des parameters. La classe responsable du chaos est ServletServerHttpRequest . Malheureusement, il ne peut pas être remplacé, mais la classe SsortingngHttpMessageConverter peut l’être.

La solution propre n’est malheureusement pas simple:

  1. Remplacement de SsortingngHttpMessageConverter . Copiez / readInternal() méthode d’ajustement de classe d’origine readInternal() .
  2. Envelopper les getInputStream() HttpServletRequest remplacement des getInputStream() , getReader() et getParameter*() .

Dans la méthode SsortingngHttpMessageConverter # readInternal le code suivant doit être utilisé:

  if (inputMessage instanceof ServletServerHttpRequest) { ServletServerHttpRequest oo = (ServletServerHttpRequest)inputMessage; input = oo.getServletRequest().getInputStream(); } else { input = inputMessage.getBody(); } 

Ensuite, le convertisseur doit être enregistré dans le contexte.

      

La deuxième étape est décrite ici: Http Servlet demande de perdre les parameters du corps POST après l’avoir lu une fois