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?

47
demandé sur abhihello123 2013-10-19 20:56:43

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.

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

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

  3. Les paramètres

    sont résolus dans l'ordre. Le @RequestBody est traité en premier. Le printemps consommera tout le HttpServletRequest InputStream . Lorsqu'il essaie ensuite de résoudre le @RequestParam , qui est par défaut required , 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.

  4. le manipulateur pour @RequestParam agit en premier, en lisant ce qu'il peut du HttpServletRequest 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 valeur abc mappée au paramètre name . 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.

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

  6. le gestionnaire de @RequestParam lit à la fois le corps et la chaîne de requête URL. Il les placerait habituellement dans un Map , mais puisque le paramètre est de type String , le ressort sérialisera le Map comme valeurs séparées par des virgules. Le handler pour @RequestBody puis, encore une fois, n'a plus rien à lire du corps.

41
répondu Sotirios Delimanolis 2015-11-02 14:56:36

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.

  1. même résultat que le vôtre
  2. même résultat que le vôtre
  3. a donné body= "name=abc" , et name = "abc"
  4. de Même que 3.
  5. body ="name=abc" , name = "xyz,abc"
  6. même que 5.
2
répondu Ramesh Karna 2018-07-20 03:10:09

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
0
répondu Sparticles 2013-12-29 05:56:15

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:

  1. remplaçant StringHttpMessageConverter . Copiez/réécrivez la méthode d'ajustement de classe originale readInternal() .
  2. l'Habillage HttpServletRequest écraser getInputStream() , getReader() et getParameter*() 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

0
répondu 30thh 2017-05-23 12:34:45