MVC de printemps-pourquoi ne pas pouvoir utiliser @RequestBody et @RequestParam ensemble
utilisant le client HTTP dev avec la demande de poste et l'application de type de contenu / x-www-form-urlencoded
1) Seulement @RequestBody
Demande - localhost:8080/spring mvc/bienvenue Dans le Corps - name=abc
Code -
@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestBody String body, Model model) {
model.addAttribute("message", body);
return "hello";
}
// Donne corps comme "nom=abc" comme prévu
2) Seulement @RequestParam
Demande - localhost:8080/spring mvc/bienvenue Dans le Corps - name=abc
Code -
@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestParam String name, Model model) {
model.addAttribute("name", name);
return "hello";
}
// Donne le nom de " abc " comme prévu
3) ensemble des deux
Demande - localhost:8080/spring mvc/bienvenue Dans le Corps - 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";
}
/ / code D'erreur HTTP 400 - la requête envoyée par le client était incorrecte sur le plan syntaxique.
4) ci-dessus avec changement de position de params
Demande - localhost:8080/spring mvc/bienvenue Dans le Corps - name=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. Nom: "abc". le corps est vide
5) ensemble mais obtenir les paramètres d'url de type
Demande - localhost:8080/spring mvc/bienvenue?nom = xyz Dans le Corps - 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";
}
/ / le nom est "xyz" et le corps est "name=abc
6) même que 5) mais avec les paramètres position changée
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?
4 réponses
Le @RequestBody
javadoc états
Annotation indiquant qu'un paramètre de méthode doit être lié au corps de la demande web.
il utilise les instances enregistrées de HttpMessageConverter
pour desérialiser le corps de la requête en un objet du type de paramètre annoté.
et @RequestParam
Annotation qui indique qu'un paramètre de méthode doit être lié à un paramètre de requête web.
-
le ressort lie le corps de la requête au paramètre annoté
@RequestBody
. -
le ressort lie les paramètres de requête du corps de la requête (paramètres encodés par url) au paramètre de votre méthode. Le printemps va utiliser le nom du paramètre, c'est à dire.
name
, pour cartographier le paramètre. - Les paramètres
sont résolus dans l'ordre. Le
@RequestBody
est traité en premier. Le printemps consommera tout leHttpServletRequest
InputStream
. Lorsqu'il essaie ensuite de résoudre le@RequestParam
, qui est par défautrequired
, il n'y a aucun paramètre request dans la chaîne de requête ou ce qui reste du corps de requête, c'est-à-dire. rien. Donc il échoue avec 400 parce que la requête ne peut pas être correctement traitée par la méthode handler. -
le manipulateur pour
@RequestParam
agit en premier, en lisant ce qu'il peut duHttpServletRequest
InputStream
pour cartographier le paramètre request, i.e.. toute la chaîne de requête/paramètres encodés url. Il le fait et obtient la valeurabc
mappée au paramètrename
. Lorsque le gestionnaire de@RequestBody
tourne, il ne reste plus rien dans le corps de la requête, donc l'argument utilisé est la chaîne vide. -
Le gestionnaire pour
@RequestBody
lit le corps et le lie à la paramètre. Le gestionnaire pour@RequestParam
peut alors obtenir le paramètre request à partir de la chaîne de requête URL. -
le gestionnaire de
@RequestParam
lit à la fois le corps et la chaîne de requête URL. Il les placerait habituellement dans unMap
, mais puisque le paramètre est de typeString
, le ressort sérialisera leMap
comme valeurs séparées par des virgules. Le handler pour@RequestBody
puis, encore une fois, n'a plus rien à lire du corps.
je sais qu'il est trop tard pour répondre à cette question, mais encore il pourrait aider quelqu'un Lecteurs.
On dirait des problèmes de version. J'ai effectué tous ces essais avec le ressort 4.1.4 et j'ai trouvé que l'ordre de @RequestBody
et @RequestParam
n'a pas d'importance.
- même résultat que le vôtre
- même résultat que le vôtre
- a donné
body= "name=abc"
, etname = "abc"
- de Même que 3.
-
body ="name=abc"
,name = "xyz,abc"
- même que 5.
vous pouvez aussi simplement changer le statut requis par défaut de @RequestParam en false de sorte que le code de statut 400 de la réponse HTTP ne soit pas généré. Cela vous permettra de placer les Annotations dans n'importe quel ordre que vous voulez.
@RequestParam(required = false)String name
cela se produit parce que la spécification des servlets n'est pas très simple. Si vous travaillez avec une implémentation native HttpServletRequest
, vous ne pouvez pas obtenir à la fois le corps D'encodage de L'URL et les paramètres. Le printemps fait quelques solutions de rechange, qui le rendent encore plus étrange et non transparent.
dans de tels cas, Spring (version 3.2.4) renverse un corps pour vous en utilisant les données de la méthode getParameterMap()
. Il mélange les paramètres GET et POST et casse l'ordre des paramètres. Le classe, qui est responsable du chaos est ServletServerHttpRequest
. Malheureusement, il ne peut pas être remplacé, mais la classe StringHttpMessageConverter
peut-être.
la solution propre n'est malheureusement pas simple:
- remplaçant
StringHttpMessageConverter
. Copiez/réécrivez la méthode d'ajustement de classe originalereadInternal()
. - l'Habillage
HttpServletRequest
écrasergetInputStream()
,getReader()
etgetParameter*()
méthodes.
dans la méthode StringHttpMessageConverter#read internal le code suivant doit être utilisé:
if (inputMessage instanceof ServletServerHttpRequest) {
ServletServerHttpRequest oo = (ServletServerHttpRequest)inputMessage;
input = oo.getServletRequest().getInputStream();
} else {
input = inputMessage.getBody();
}
alors 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: demande de Servlet Http perdre params de la poste après l'avoir lu une fois