Spring MVC - 为什么无法同时使用 @RequestBody 和 @RequestParam

11 浏览
0 Comments

Spring MVC - 为什么无法同时使用 @RequestBody 和 @RequestParam

使用HTTP dev client进行Post请求并设置Content-Type为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";
}
// 结果返回'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";
}
// 结果返回'abc',符合预期

3)同时使用两者

URL: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)与参数位置改变一样

URL: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";
}
// 无错误。name为'abc',body为空

5)同时使用,但获取类型为url参数

URL: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为空

有人能解释这种行为吗?

0
0 Comments

Spring MVC中使用@RequestParam和@RequestBody一起使用时会出现问题,这是因为Servlet规范不是非常直观。如果你使用的是原生的HttpServletRequest实现,你无法同时获取URL编码的请求体和参数。Spring做了一些变通的处理,使得情况变得更加奇怪和不透明。

在这种情况下,Spring(版本3.2.4)会重新渲染一个请求体,使用getParameterMap()方法的数据。它混合了GET和POST的参数,并且打乱了参数的顺序。造成混乱的类是ServletServerHttpRequest。不幸的是,它无法被替换,但是可以替换StringHttpMessageConverter类。

解决方法并不简单:

1. 替换StringHttpMessageConverter类。复制/覆盖原始类,并调整readInternal()方法。

2. 包装HttpServletRequest,覆盖getInputStream()、getReader()和getParameter*()方法。

在StringHttpMessageConverter#readInternal方法中,必须使用以下代码:

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

然后在上下文中注册这个转换器:


    
        
   

第二步的具体描述可以在这里找到:[Http Servlet request lose params from POST body after read it once](https://stackoverflow.com/questions/10210645)

0
0 Comments

在Spring MVC中,当使用`@RequestBody`和`@RequestParam`注解一起使用时,可能会出现问题。问题的原因是版本不兼容,具体表现在Spring 4.1.4版本上。

解决这个问题的方法是,可以改变`@RequestBody`和`@RequestParam`的顺序,或者使用其他方法来处理请求参数和请求体。

在第5种情况下,我们希望获取请求参数但是请求体为空的情况下,可以使用以下方法解决:

@RequestMapping(value = "/example", method = RequestMethod.POST)
public void exampleMethod(@RequestParam(required = false) String name, @RequestBody(required = false) String body) {
    // 处理请求参数和请求体
}

通过将`required`属性设置为`false`,可以让请求参数和请求体可选,即使其中一个为空。这样可以解决在获取请求参数但是请求体为空的情况下出现的问题。

0
0 Comments

Spring MVC中的@RequestParam和@RequestBody注解不能同时使用的原因是参数的解析顺序导致的。@RequestParam注解用于绑定请求参数,而@RequestBody注解用于绑定请求体。参数的解析顺序是先解析请求体,再解析请求参数。如果先解析了请求体,那么在解析请求参数时就无法获取到请求参数了,因此会导致无法正确处理请求。解决这个问题的方法是将请求参数放在请求体中,而不是作为URL参数传递。

具体来说,以下是不同情况下的处理方式:

1. 只使用@RequestParam注解:将请求参数作为URL参数传递。

2. 只使用@RequestBody注解:将请求参数放在请求体中,以URL编码的形式传递。

3. 同时使用@RequestParam和@RequestBody注解:由于参数解析顺序的原因,无法正确处理请求。解决方法是将请求参数放在请求体中,以URL编码的形式传递。

如果需要更详细的了解,可以下载Spring源码,使用调试器逐步跟踪代码。RequestParamMethodArgumentResolver类是处理@RequestParam注解的类。

Spring MVC中的@RequestParam和@RequestBody注解不能同时使用的原因是参数解析顺序导致的,解决方法是将请求参数放在请求体中以URL编码的形式传递。

0