簡體   English   中英

Spring MVC - 為什么不能同時使用 @RequestBody 和 @RequestParam

[英]Spring MVC - Why not able to use @RequestBody and @RequestParam together

使用帶有 Post 請求和 Content-Type application/x-www-form-urlencoded 的 HTTP 開發客戶端

1) 只有@RequestBody

網址:本地主機:8080/SpringMVC/welcome
正文:名稱=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

2) 只有@RequestParam

網址:本地主機:8080/SpringMVC/welcome
在正文中 - 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)兩者一起

網址:本地主機:8080/SpringMVC/welcome
正文:名稱=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 Error Code 400 - The request sent by the client was syntactically incorrect.

4) 上面的參數位置改變了

網址:本地主機:8080/SpringMVC/welcome
正文:名稱=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";
}
// No Error. Name  is 'abc'. body is empty

5)一起但獲取類型url參數

URL: localhost:8080/SpringMVC/welcome?name=xyz
正文:名稱=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 is 'xyz' and body is '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注冊實例將請求正文反序列化為帶注釋的參數類型的對象。

@RequestParam javadoc 狀態

指示方法參數應綁定到 Web 請求參數的注釋。

  1. Spring 將請求的主體綁定到用@RequestBody注釋的參數。

  2. Spring 將請求主體中的請求參數(url 編碼的參數)綁定到您的方法參數。 Spring 將使用參數的名稱,即。 name ,映射參數。

  3. 參數按順序解析。 首先處理@RequestBody Spring 將消耗所有HttpServletRequest InputStream 當它然后嘗試解析@RequestParam ,默認情況下是required ,查詢字符串中沒有請求參數或請求正文的剩余部分,即。 沒什么。 因此它以 400 失敗,因為處理程序方法無法正確處理請求。

  4. @RequestParam的處理程序首先起作用,讀取HttpServletRequest InputStream以映射請求參數,即。 整個查詢字符串/url 編碼的參數。 它這樣做並獲取映射到參數name的值abc @RequestBody的處理程序運行時,請求正文中沒有任何內容,因此使用的參數是空字符串。

  5. @RequestBody的處理程序讀取正文並將其綁定到參數。 @RequestParam的處理程序然后可以從 URL 查詢字符串中獲取請求參數。

  6. @RequestParam的處理程序從正文和 URL 查詢字符串中讀取。 它通常會將它們放在Map ,但由於參數是String類型,Spring 會將Map序列化為逗號分隔值。 然后,@ @RequestBody的處理程序再次從正文中讀取任何內容。

回答這個問題為時已晚,但它可以幫助新讀者,似乎是版本問題。 我用 spring 4.1.4 運行了所有這些測試,發現@RequestBody@RequestParam的順序無關緊要。

  1. 和你的結果一樣
  2. 和你的結果一樣
  3. 給了body= "name=abc"name = "abc"
  4. 與 3 相同。
  5. body ="name=abc"name = "xyz,abc"
  6. 與 5 相同。

這是因為不是很直接的 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();
    }

然后轉換器必須在上下文中注冊。

<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 正文中的參數

您也可以將 @RequestParam 默認必需狀態更改為 false,以便不生成 HTTP 響應狀態代碼 400。 這將允許您按照您喜歡的任何順序放置注釋。

@RequestParam(required = false)String name

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM