Spring MVC-为什么不能同时使用@RequestBody和@RequestParam

Spring MVC-为什么不能同时使用@RequestBody和@RequestParam,spring,spring-mvc,post,http-post,http-request-parameters,Spring,Spring Mvc,Post,Http Post,Http Request Parameters,将HTTP开发客户端与Post请求和内容类型application/x-www-form-urlencoded一起使用 1) 仅@RequestBody URL:localhost:8080/SpringMVC/welcome Body:name=abc @RequestMapping(method = RequestMethod.POST) public String printWelcome(@RequestBody String body, Model model) { model

将HTTP开发客户端与Post请求和内容类型application/x-www-form-urlencoded一起使用

1) 仅@RequestBody URL: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";
}
// Gives body as 'name=abc' as expected
@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 Error Code 400 - The request sent by the client was syntactically incorrect.
@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";
}
// No Error. Name  is 'abc'. body is empty
@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 is 'xyz' and body is 'name=abc'
2) 仅@RequestParam URL: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";
}
// Gives name as 'abc' as expected
3) 一起 URL: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";
}
// Gives body as 'name=abc' as expected
@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 Error Code 400 - The request sent by the client was syntactically incorrect.
@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";
}
// No Error. Name  is 'abc'. body is empty
@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 is 'xyz' and body is 'name=abc'
4) 以上参数位置已更改 URL: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";
}
// Gives body as 'name=abc' as expected
@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 Error Code 400 - The request sent by the client was syntactically incorrect.
@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";
}
// No Error. Name  is 'abc'. body is empty
@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 is 'xyz' and body is 'name=abc'
5) 一起获取类型url参数 URL:localhost:8080/SpringMVC/welcome?name=xyz
Body:name=abc

@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestBody String body, Model model) {
    model.addAttribute("message", body);
    return "hello";
}
// Gives body as 'name=abc' as expected
@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 Error Code 400 - The request sent by the client was syntactically incorrect.
@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";
}
// No Error. Name  is 'abc'. body is empty
@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 is 'xyz' and body is 'name=abc'
6) 与5)相同,但参数位置已更改
有人能解释这种行为吗?

javadoc声明
@RequestBody

指示方法参数的注释应绑定到主体 web请求的一部分

它使用
HttpMessageConverter
的注册实例将请求正文反序列化为带注释参数类型的对象

以及
@RequestParam
javadoc状态

指示方法参数应绑定到的注释 web请求参数

  • Spring将请求主体绑定到用
    @RequestBody
    注释的参数

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

  • 按顺序解析参数。首先处理
    @RequestBody
    。Spring将使用所有的
    HttpServletRequest
    InputStream
    。然后,当它尝试解析默认情况下所需的
    @RequestParam
    时,查询字符串中没有请求参数或请求正文中剩余的内容,即没有。因此,使用400失败,因为处理程序方法无法正确处理请求

  • @RequestParam
    的处理程序首先执行操作,读取
    HttpServletRequest
    InputStream
    的内容,以映射请求参数,即整个查询字符串/url编码参数。它这样做,并获取映射到参数
    名称
    的值
    abc
    。当
    @RequestBody
    的处理程序运行时,请求正文中没有任何内容,因此使用的参数是空字符串

  • @RequestBody
    的处理程序读取主体并将其绑定到参数。
    @RequestParam
    的处理程序随后可以从URL查询字符串中获取请求参数

  • @RequestParam
    的处理程序同时读取主体和URL查询字符串。它通常将它们放在
    映射中
    ,但由于参数的类型为
    字符串
    ,因此Spring将
    映射
    序列化为逗号分隔的值。然后,
    @RequestBody
    的处理程序再次没有任何内容可从主体中读取


  • 您还可以将@RequestParam default required状态更改为false,这样就不会生成HTTP响应状态代码400。这将允许您按自己喜欢的顺序放置注释

    @RequestParam(required = false)String name
    

    这是因为Servlet规范不是很直截了当。如果您使用的是本机
    HttpServletRequest
    实现,则无法同时获取URL编码正文和参数。Spring做了一些变通,这使得它更加奇怪和不透明

    在这种情况下,Spring(版本3.2.4)使用
    getParameterMap()
    方法中的数据为您重新呈现一个实体。它混合了GET和POST参数,并打破了参数顺序。导致混乱的类是
    ServletServerHttpRequest
    。不幸的是,它不能被替换,但是类
    StringHttpMessageConverter
    可以被替换

    不幸的是,清洁的解决方案并不简单:

  • 更换
    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>
    
    
    

    这里描述了第二步:

    现在回答这个问题已经太迟了,但它可以帮助新读者, 似乎是版本问题。我用Spring4.1.4运行了所有这些测试,发现
    @RequestBody
    @RequestParam
    的顺序并不重要

  • 和你的结果一样
  • 和你的结果一样
  • 给出了
    body=“name=abc”
    ,以及
    name=“abc”
  • 与3相同
  • body=“name=abc”
    name=“xyz,abc”
  • 和5号一样

  • 假设在案例4)中,帖子主体是
    name=abc&age=2
    ,那么根据您的解释,名称应该是
    abc
    ,主体应该是
    &age=2
    ,但实际结果是name是
    abc
    ,主体是空的。@abhihello123它读取整个url编码的表单参数。所以整个
    name=abc&age=2
    。但是如果我再加一个@RequestParam,它会把2放入age。这意味着这个名字只能读abc。很抱歉安装了窃听器。@abhihello123是的,在这种情况下,已读取完整的查询字符串,可以从中生成下一个
    @RequestParam
    。参数存储在一个映射中,每次都可以检查请求参数。@abhihello123,如果您有足够的耐心的话。下载源JAR并在发送请求和逐步完成代码时使用调试器。您要查看的类是
    RequestParamMethodArgumentResolver
    ,它处理
    @RequestParam
    。是的,响应状态代码400不会是g