首页 文章

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

提问于
浏览
52

使用带有Post请求的HTTP dev客户端和Content-Type application / x-www-form-urlencoded

1)只有@RequestBody

请求 - localhost:8080 / SpringMVC / welcome In 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

请求 - localhost:8080 / SpringMVC / welcome In Body - name = abc

码-

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

//按预期将名称命名为“abc”

3)两者在一起

请求 - localhost:8080 / SpringMVC / welcome In 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位置改变了

请求 - localhost:8080 / SpringMVC / welcome In 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)一起但获取类型url参数

请求 - localhost:8080 / SpringMVC / welcome?name = xyz In 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";
}

//名称是'xyz',正文是'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为空

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

4 回答

  • 0

    @RequestBody javadoc说明

    指示方法参数的注释应绑定到Web请求的主体 .

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

    @RequestParam

    注释,指示应将方法参数绑定到Web请求参数 .

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

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

    • 参数按顺序解析 . 首先处理 @RequestBody . Spring将消耗所有 HttpServletRequest InputStream . 然后,当它尝试解析 @RequestParam (默认为 required )时,查询字符串中没有请求参数或请求体的剩余部分,即 . 没有 . 所以它失败了,因为处理程序方法无法正确处理请求 .

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

    • @RequestBody 的处理程序读取正文并将其绑定到参数 . 然后, @RequestParam 的处理程序可以从URL查询字符串中获取请求参数 .

    • @RequestParam 的处理程序从正文和URL查询字符串中读取 . 它通常会将它们放在 Map 中,但由于参数的类型为 String ,因此Spring会将 Map 序列化为逗号分隔值 . 然后, @RequestBody 的处理程序再没有任何东西可以从正文中读取 .

  • 3

    我知道现在回答这个问题为时已晚,但它仍然可以帮助读者 . 看来版本问题 . 我用Spring 4.1.4运行所有这些测试,发现 @RequestBody@RequestParam 的顺序无关紧要 .

    • 与您的结果相同

    • 与您的结果相同

    • 给了 body= "name=abc"name = "abc"

    • 与3相同 .

    • body ="name=abc"name = "xyz,abc"

    • 与5相同 .

  • 0

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

    @RequestParam(required = false)String name
    
  • 43

    它的发生是因为不是非常直接的Servlet规范 . 如果您正在使用本机 HttpServletRequest 实现,则无法同时获取URL编码主体和参数 . Spring做了一些变通办法,这使得它变得更加奇怪和不透明 .

    在这种情况下,Spring(版本3.2.4)使用 getParameterMap() 方法中的数据为您重新渲染一个实体 . 它混合了GET和POST参数并中断了参数顺序 . 负责混乱的 class 是 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>
    

    第二步描述如下:Http Servlet request lose params from POST body after read it once

相关问题