Utilisation du client de développement HTTP avec une demande de publication et une application de type de contenu/x-www-form-urlencoded
1) Seulement @RequestBody
Demande - localhost: 8080/SpringMVC/welcome In Body - nom = abc
Code-
@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestBody String body, Model model) {
model.addAttribute("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 String printWelcome(@RequestParam String name, Model model) {
model.addAttribute("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 String printWelcome(@RequestBody String body, @RequestParam String name, Model model) {
model.addAttribute("name", name);
model.addAttribute("message", body);
return "hello";
}
// Code d'erreur HTTP 400 - La demande envoyée par le client était syntaxiquement incorrecte.
4) Ci-dessus avec params changé de position
Demande - localhost: 8080/SpringMVC/welcome In Body - nom = abc
Code-
@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";
}
// Pas d'erreur. Le nom est 'abc'. le corps est vide
5) Ensemble mais obtenez les paramètres de type url
Demande - localhost: 8080/SpringMVC/welcome? Name = xyz Dans Body - name = abc
Code-
@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";
}
// nom est 'xyz' et le corps est 'name = abc'
6) Identique à 5) mais avec les paramètres de position modifiés
Code -
@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";
}
// nom = 'xyz, abc' le corps est vide
Quelqu'un peut-il expliquer ce comportement?
Le @RequestBody
états javadoc
L'annotation indiquant un paramètre de méthode doit être liée au corps de la demande Web.
Il utilise des instances enregistrées de HttpMessageConverter
pour désérialiser le corps de la demande en un objet 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.
Spring lie le corps de la requête au paramètre annoté avec @RequestBody
.
Spring lie les paramètres de requête du corps de la requête (paramètres encodés en URL) au paramètre de votre méthode. Spring utilisera le nom du paramètre, c'est-à-dire. name
, pour mapper le paramètre.
Les paramètres sont résolus dans l'ordre. Le @RequestBody
est traité en premier. Spring utilisera tous les HttpServletRequest
InputStream
. Quand il essaie ensuite de résoudre le @RequestParam
, qui est par défaut required
, il n'y a pas de paramètre de requête dans la chaîne de requête ou ce qui reste du corps de la requête, c'est-à-dire. rien. Donc, il échoue avec 400 parce que la demande ne peut pas être correctement traitée par la méthode du gestionnaire.
Le gestionnaire pour @RequestParam
_ _ agit d'abord en lisant ce qu'il peut du HttpServletRequest
InputStream
pour mapper le paramètre de requête, c'est-à-dire. l'ensemble de la requête/les paramètres encodés dans l'url. Il le fait et obtient la valeur abc
mappée sur le paramètre name
. Quand le gestionnaire pour @RequestBody
s'exécute, il ne reste plus rien dans le corps de la demande, l'argument utilisé est donc la chaîne vide.
Le gestionnaire pour @RequestBody
lit le corps et le lie au paramètre. Le gestionnaire pour @RequestParam
peut ensuite obtenir le paramètre de requête à partir de la chaîne de requête URL.
Le gestionnaire pour @RequestParam
lit à la fois le corps et la chaîne de requête URL. Cela les mettrait généralement dans un Map
, mais comme le paramètre est de type String
, Spring sérialisera le Map
sous forme de valeurs séparées par des virgules. Le gestionnaire pour @RequestBody
alors, encore une fois, n’a plus rien à lire dans le corps.
Il est trop tard pour répondre à cette question, mais cela pourrait aider les nouveaux lecteurs. J'ai exécuté tous ces tests avec le printemps 4.1.4 et constaté que l'ordre de @RequestBody
et @RequestParam
_ n'a pas d'importance.
body= "name=abc"
, et name = "abc"
body ="name=abc"
, name = "xyz,abc"
Cela se produit à cause d'une 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 code de l'URL et les paramètres. Spring propose des solutions de contournement qui le rendent 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 paramètres GET et POST et rompt l'ordre des paramètres. La classe responsable du chaos est ServletServerHttpRequest
. Malheureusement, elle ne peut pas être remplacée, mais la classe StringHttpMessageConverter
peut être.
La solution propre n'est malheureusement pas simple:
StringHttpMessageConverter
. Copier/écraser la méthode de réglage de classe d'origine readInternal()
.HttpServletRequest
en remplaçant les méthodes getInputStream()
, getReader()
et getParameter*()
.Dans la méthode, StringHttpMessageConverter # readInternal 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.
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true/false">
<bean class="my-new-converter-class"/>
</mvc:message-converters>
</mvc:annotation-driven>
La deuxième étape est décrite ici: La demande de servlet HTTP perd les paramètres de POST le corps après l'avoir lu une fois
Vous pouvez également modifier l'état requis par défaut de @RequestParam sur false afin que le code d'état de réponse HTTP 400 ne soit pas généré. Cela vous permettra de placer les annotations dans l'ordre de votre choix.
@RequestParam(required = false)String name