Spring MVC – 为什么不能一起使用@RequestBody和@RequestParam

使用具有Post请求和Content-Type应用程序的HTTP dev客户端/ x-www-form-urlencoded

1)只有@RequestBody

Request – localhost:8080 / SpringMVC / welcome在Body – name = abc中

码-

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

//如预期的那样给body赋予“name = abc”

2)只有@RequestParam

Request – localhost:8080 / SpringMVC / welcome在Body – name = abc中

码-

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

//按预期命名为“abc”

3)在一起

Request – localhost:8080 / SpringMVC / welcome在Body – name = abc中

码-

 @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"; } 

// HTTP错误代码400 – 客户端发送的请求在语法上不正确。

4)上面的params位置改变了

Request – localhost:8080 / SpringMVC / welcome在Body – name = abc中

码-

 @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"; } 

//没有错误 名字是'abc'。 身体是空的

5)一起,但获得types的URL参数

Request – localhost:8080 / SpringMVC / welcome?name = xyz在Body – name = abc中

码-

 @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"; } 

// name是'xyz',body是'name = abc'

6)与5)相同,但参数位置已更改

代码 –

 @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"; } 

// name ='xyz,abc'body is empty

有人可以解释这种行为吗?

@RequestBody javadoc状态

表示方法参数的注释应该绑定到Web请求的主体。

它使用HttpMessageConverter已注册实例将请求主体反序列化为注释参数types的对象。

@RequestParam

表示方法参数应该绑定到Web请求参数的注释。

  1. Spring将请求的主体绑定到使用@RequestBody注解的参数。

  2. Spring将请求参数从请求主体(url编码参数)绑定到您的方法参数。 Spring将使用参数的名称,即。 name ,来映射参数。

  3. 参数按顺序parsing。 首先处理@RequestBody 。 Spring将会消耗所有的HttpServletRequest InputStream 。 然后,当它试图parsing@RequestParam (默认情况下是required ,查询string中没有请求参数,或者请求体中剩余的请求参数。 没有。 所以它与400失败,因为处理器方法无法正确处理请求。

  4. @RequestParam的处理程序首先起作用,读取HttpServletRequest InputStream的参数,即映射请求参数。 整个查询string/ url-encoded参数。 它这样做并获取映射到参数name的值abc 。 当@RequestBody的处理程序运行时,请求体中没有任何内容,所以使用的参数是空string。

  5. @RequestBody的处理程序读取正文并将其绑定到参数。 @RequestParam的处理程序可以从URL查询string中获取请求参数。

  6. @RequestParam的处理程序读取正文和URL查询string。 它通常会把它们放在一个Map ,但是由于参数是Stringtypes,所以Spring会将Map作为逗号分隔值进行序列化。 然后@RequestBody的处理程序再也没有什么可以从正文读取的了。

这是因为不太直截了当的Servlet规范而发生的。 如果您正在使用本地HttpServletRequest实现,则无法同时获得URL编码体和参数。 spring做了一些变通,这使得它更奇怪,不透明。

在这种情况下,Spring(版本3.2.4)使用getParameterMap()方法中的数据为您重新呈现一个正文。 它混合GET和POST参数,并打破参数顺序。 负责混沌的类是ServletServerHttpRequest 。 不幸的是它不能被replace,但类StringHttpMessageConverter可以。

干净的解决scheme不幸的是不简单:

  1. replaceStringHttpMessageConverter 。 复制/覆盖原来的类调整方法readInternal()
  2. 包装HttpServletRequest覆盖getInputStream()getReader()getParameter*()方法。

在StringHttpMessageConverter#readInternal方法中必须使用下面的代码:

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

然后转换器必须在上下文中注册。

 <mvc:annotation-driven> <mvc:message-converters register-defaults="true/false"> <bean class="my-new-converter-class"/> </mvc:message-converters> </mvc:annotation-driven> 

第二步描述如下: Http Servlet请求在读取一次后会从POST主体中丢失参数

回答这个问题可能为时已晚,但我只是回答了这个问题,所以可以帮助其他读者。 看来版本问题。 我用Spring 4.1.4运行所有这些testing,发现@RequestBody@RequestParam顺序无关紧要。

  1. 和你的结果一样
  2. 和你的结果一样
  3. body= "name=abc" ,并且name = "abc"
  4. 和3一样。
  5. body ="name=abc"name = "xyz,abc"
  6. 与5相同。

您也可以将@RequestParam的默认必需状态更改为false,以便不生成HTTP响应状态代码400。 这将允许您以任何您想要的顺序放置注释。

 @RequestParam(required = false)String name