web-dev-qa-db-ja.com

Spring MVC-@RequestBodyと@RequestParamを一緒に使用できない理由

Post要求およびContent-Type application/x-www-form-urlencodedでHTTP devクライアントを使用する

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";
}

//本体を期待どおり '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 is empty

誰かがこの行動を説明できますか?

56
abhihello123

@RequestBody javadocの状態

メソッドパラメータを示す注釈は、Web要求の本文にバインドする必要があります。

HttpMessageConverterの登録済みインスタンスを使用して、リクエストの本文を注釈付きパラメータータイプのオブジェクトに逆シリアル化します。

そして@RequestParam

メソッドパラメーターをWeb要求パラメーターにバインドする必要があることを示す注釈。

  1. Springは、リクエストの本文を@RequestBodyアノテーションが付けられたパラメーターにバインドします。

  2. Springは、リクエストボディ(urlエンコードされたパラメーター)からのリクエストパラメーターをメソッドパラメーターにバインドします。 Springはパラメーターの名前を使用します。 name、パラメーターをマップします。

  3. パラメータは順番に解決されます。 @RequestBodyが最初に処理されます。 SpringはすべてのHttpServletRequestInputStreamを消費します。次に、デフォルトでrequiredである@RequestParamを解決しようとすると、クエリ文字列にリクエストパラメータがないか、リクエスト本文に残っているものがありません。何もない。そのため、ハンドラメソッドでリクエストを正しく処理できないため、400で失敗します。

  4. @RequestParamのハンドラーが最初に動作し、HttpServletRequestInputStreamの機能を読み取って要求パラメーターをマップします。クエリ文字列/ URLエンコードされたパラメータ全体。そうすると、パラメータabcにマップされた値nameを取得します。 @RequestBodyのハンドラーが実行されると、リクエスト本文に何も残っていないため、使用される引数は空の文字列です。

  5. @RequestBodyのハンドラーは、本体を読み取り、パラメーターにバインドします。 @RequestParamのハンドラーは、URLクエリ文字列からリクエストパラメーターを取得できます。

  6. @RequestParamのハンドラーは、本体とURLクエリ文字列の両方から読み取ります。通常はMapに入れられますが、パラメーターはString型であるため、SpringはMapをコンマ区切り値としてシリアル化します。 @RequestBodyのハンドラーには、本体から読み取るものが何もありません。

47

この質問に答えるのは遅すぎますが、新しい読者には役立つかもしれません。バージョンの問題のようです。これらすべてのテストを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と同じ。
3
Ramesh Karna

これは、サーブレットの仕様がそれほど単純ではないために発生します。ネイティブ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>

手順2については、次のとおりです。 Httpサーブレットリクエストは、一度読み取った後、POST本文からパラメータを失います

1
30thh

HTTP要求のステータスコード400が生成されないように、@ RequestParamのデフォルトの必須ステータスをfalseに変更することもできます。これにより、好きな順序で注釈を配置できます。

@RequestParam(required = false)String name
0
Sparticles