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请求参数的注释。
-
Spring将请求的主体绑定到使用
@RequestBody
注解的参数。 -
Spring将请求参数从请求主体(url编码参数)绑定到您的方法参数。 Spring将使用参数的名称,即。
name
,来映射参数。 -
参数按顺序parsing。 首先处理
@RequestBody
。 Spring将会消耗所有的HttpServletRequest
InputStream
。 然后,当它试图parsing@RequestParam
(默认情况下是required
,查询string中没有请求参数,或者请求体中剩余的请求参数。 没有。 所以它与400失败,因为处理器方法无法正确处理请求。 -
@RequestParam
的处理程序首先起作用,读取HttpServletRequest
InputStream
的参数,即映射请求参数。 整个查询string/ url-encoded参数。 它这样做并获取映射到参数name
的值abc
。 当@RequestBody
的处理程序运行时,请求体中没有任何内容,所以使用的参数是空string。 -
@RequestBody
的处理程序读取正文并将其绑定到参数。@RequestParam
的处理程序可以从URL查询string中获取请求参数。 -
@RequestParam
的处理程序读取正文和URL查询string。 它通常会把它们放在一个Map
,但是由于参数是String
types,所以Spring会将Map
作为逗号分隔值进行序列化。 然后@RequestBody
的处理程序再也没有什么可以从正文读取的了。
这是因为不太直截了当的Servlet规范而发生的。 如果您正在使用本地HttpServletRequest
实现,则无法同时获得URL编码体和参数。 spring做了一些变通,这使得它更奇怪,不透明。
在这种情况下,Spring(版本3.2.4)使用getParameterMap()
方法中的数据为您重新呈现一个正文。 它混合GET和POST参数,并打破参数顺序。 负责混沌的类是ServletServerHttpRequest
。 不幸的是它不能被replace,但类StringHttpMessageConverter
可以。
干净的解决scheme不幸的是不简单:
- replace
StringHttpMessageConverter
。 复制/覆盖原来的类调整方法readInternal()
。 - 包装
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
顺序无关紧要。
- 和你的结果一样
- 和你的结果一样
- 给
body= "name=abc"
,并且name = "abc"
- 和3一样。
-
body ="name=abc"
,name = "xyz,abc"
- 与5相同。
您也可以将@RequestParam的默认必需状态更改为false,以便不生成HTTP响应状态代码400。 这将允许您以任何您想要的顺序放置注释。
@RequestParam(required = false)String name