Ajaxを使用して@RequestBodyの複数の変数をSpring MVCコントローラーに渡す


112

バッキングオブジェクトでラップする必要はありますか?私はこれをしたいです:

@RequestMapping(value = "/Test", method = RequestMethod.POST)
@ResponseBody
public boolean getTest(@RequestBody String str1, @RequestBody String str2) {}

そして、次のようなJSONを使用します。

{
    "str1": "test one",
    "str2": "two test"
}

しかし、代わりに私は使用する必要があります:

@RequestMapping(value = "/Test", method = RequestMethod.POST)
@ResponseBody
public boolean getTest(@RequestBody Holder holder) {}

次に、このJSONを使用します。

{
    "holder": {
        "str1": "test one",
        "str2": "two test"
    }
}

あれは正しいですか?私の他のオプションを変更するだろうRequestMethodGETし、使用@RequestParamクエリ文字列または使用中@PathVariableのいずれかでRequestMethod

回答:


92

正解です。@ RequestBodyアノテーション付きパラメーターは、リクエストの本文全体を保持し、1つのオブジェクトにバインドする必要があるため、基本的にオプションを使用する必要があります。

あなたが絶対にあなたのアプローチを望んでいるなら、あなたができるカスタム実装があります:

これがあなたのjsonだとしましょう:

{
    "str1": "test one",
    "str2": "two test"
}

ここで2つのパラメーターにバインドします。

@RequestMapping(value = "/Test", method = RequestMethod.POST)
public boolean getTest(String str1, String str2)

まず@JsonArg、必要な情報へのパスなどのJSONパスを使用して、カスタムアノテーションを定義します。

public boolean getTest(@JsonArg("/str1") String str1, @JsonArg("/str2") String str2)

今カスタム書くHandlerMethodArgumentResolver使用JsonPathは、実引数を解決するために、上記で定義されました:

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.IOUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import com.jayway.jsonpath.JsonPath;

public class JsonPathArgumentResolver implements HandlerMethodArgumentResolver{

    private static final String JSONBODYATTRIBUTE = "JSON_REQUEST_BODY";
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(JsonArg.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        String body = getRequestBody(webRequest);
        String val = JsonPath.read(body, parameter.getMethodAnnotation(JsonArg.class).value());
        return val;
    }

    private String getRequestBody(NativeWebRequest webRequest){
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        String jsonBody = (String) servletRequest.getAttribute(JSONBODYATTRIBUTE);
        if (jsonBody==null){
            try {
                String body = IOUtils.toString(servletRequest.getInputStream());
                servletRequest.setAttribute(JSONBODYATTRIBUTE, body);
                return body;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return "";

    }
}

これをSpring MVCに登録するだけです。少し複雑ですが、これは問題なく機能するはずです。


2
@JsonArgと言ってカスタムアノテーションを作成するにはどうすればよいですか?
Surendra Jnawali 2014

どうしてこれなの?今、私はバックエンドで多くの異なるラッパークラスを作成する必要があります。Struts2アプリケーションをSpringbootに移行していますが、ajaxを使用して送信されたJSONオブジェクトが実際にはモデルの2つ以上のオブジェクトである場合が多くありました。たとえば、ユーザーとアクティビティ
Jose Ospina

このリンクは、あなたが「どのように春のMVCでこれを登録するには」を示しgeekabyte.blogspot.sg/2014/08/...
ボディル

3
このオプションが春に追加されない理由はまだ興味深い。2つの
tibi

@SurendraJnawaliあなたはこのようにすることができます@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface JsonArg { String value() default ""; }
Epono

87

@RequestBody単一のオブジェクトにマップする必要があることは事実ですが、そのオブジェクトはである可能性があるMapため、これにより、達成しようとしていることへの良い方法が得られます(1つのオフバッキングオブジェクトを記述する必要はありません)。

@RequestMapping(value = "/Test", method = RequestMethod.POST)
@ResponseBody
public boolean getTest(@RequestBody Map<String, String> json) {
   //json.get("str1") == "test one"
}

完全なJSONツリーが必要な場合は、JacksonのObjectNodeにバインドすることもできます。

public boolean getTest(@RequestBody ObjectNode json) {
   //json.get("str1").asText() == "test one"

@JoseOspinaなぜそうできないのか。requestBodyを持つMap <String、Object>に関連するリスク
Ben Cheng

@Ben Mapつまり、1つのオブジェクトを使用してオブジェクトをいくつでも格納できますが、最上位のオブジェクトは1つだけでなければならず、2つの最上位のオブジェクトは存在できません。
ホセ・オスピナ

1
動的なアプローチの欠点Map<String, String>は次のとおりだと思います。APIドキュメントライブラリ(swagger / springfoxなど)は、おそらくソースコードから要求/応答スキーマを解析できません。
ストラトバリウス

10

より単純なデータ型のbody変数とpath変数を使用して、post引数を混同できます。

@RequestMapping(value = "new-trade/portfolio/{portfolioId}", method = RequestMethod.POST)
    public ResponseEntity<List<String>> newTrade(@RequestBody Trade trade, @PathVariable long portfolioId) {
...
}

10

複数のオブジェクト、パラメータ、変数などを渡すため。ジャクソンライブラリのObjectNodeをパラメーターとして動的に使用できます。あなたはこのようにそれを行うことができます:

@RequestMapping(value = "/Test", method = RequestMethod.POST)
@ResponseBody
public boolean getTest(@RequestBody ObjectNode objectNode) {
   // And then you can call parameters from objectNode
   String strOne = objectNode.get("str1").asText();
   String strTwo = objectNode.get("str2").asText();

   // When you using ObjectNode, you can pas other data such as:
   // instance object, array list, nested object, etc.
}

これがお役に立てば幸いです。


2

@RequestParamはクライアントから送信されるHTTP GETまたはPOSTパラメータであり、リクエストマッピングは変数のURLのセグメントです。

http:/host/form_edit?param1=val1&param2=val2

var1var2はリクエストパラメータです。

http:/host/form/{params}

{params}リクエストのマッピングです。あなたのようなあなたのサービスを呼び出すことができますhttp:/host/form/userhttp:/host/form/firm どこの会社&ユーザーは次のように使用されていますPathvariable


これは質問に答えず、間違っています
。POST

1
@NimChimpsky:できます。POSTリクエストには、URLにパラメータを含めることができます。
Martijn Pieters

2

簡単な解決策は、str1とstr2を属性として持つペイロードクラスを作成することです。

@Getter
@Setter
public class ObjHolder{

String str1;
String str2;

}

合格したら

@RequestMapping(value = "/Test", method = RequestMethod.POST)
@ResponseBody
public boolean getTest(@RequestBody ObjHolder Str) {}

そしてあなたのリクエストの本文は:

{
    "str1": "test one",
    "str2": "two test"
}

1
この注釈のパッケージは何ですか?自動インポートはインポートjdk.nashorn.internal.objects.annotations.Setterのみを提供しました。編集。私はそれがLombok projectlombok.org/features/GetterSetterだと思います。私が間違っている場合は修正してください
Gleichmut

@Gleichmutでは、変数に単純なゲッターとセッターを使用できます。期待どおりに動作します。
Gimnath

1

jsonを使用する代わりに、簡単なことを行うことができます。

$.post("${pageContext.servletContext.contextPath}/Test",
                {
                "str1": "test one",
                "str2": "two test",

                        <other form data>
                },
                function(j)
                {
                        <j is the string you will return from the controller function.>
                });

コントローラーで、ajaxリクエストを次のようにマッピングする必要があります。

 @RequestMapping(value="/Test", method=RequestMethod.POST)
    @ResponseBody
    public String calculateTestData(@RequestParam("str1") String str1, @RequestParam("str2") String str2, HttpServletRequest request, HttpServletResponse response){
            <perform the task here and return the String result.>

            return "xyz";
}

これがお役に立てば幸いです。


1
それはjsonであり、動作しません。メソッドでrequestparamを指定していますが、ajax postリクエストでjsonを使用してequestbodyを定義しています。
NimChimpsky 2012年

ajax呼び出しでJSON形式を使用していないことを確認してください。私は単純に2つの要求パラメーターを使用しており、コントローラーで@RequestParamアノテーションを使用してそれらのパラメーターを取得できます。動いています。私はこれを使います。試してみてください。
日本Trivedi

私はそれを試しました、それはその疑惑のポイントです。そのようには動作しません。
NimChimpsky 2012年

何を試したか正確に明記してください。それを質問に示してください。私の理解とは異なる要件があると思います。
日本トリベディ2012年

1
最初の試みで私のために働いた。ありがとう!
Humppakäräjät

1

私はBijuの解決策を採用しました:

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.IOUtils;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;


public class JsonPathArgumentResolver implements HandlerMethodArgumentResolver{

    private static final String JSONBODYATTRIBUTE = "JSON_REQUEST_BODY";

    private ObjectMapper om = new ObjectMapper();

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(JsonArg.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        String jsonBody = getRequestBody(webRequest);

        JsonNode rootNode = om.readTree(jsonBody);
        JsonNode node = rootNode.path(parameter.getParameterName());    

        return om.readValue(node.toString(), parameter.getParameterType());
    }


    private String getRequestBody(NativeWebRequest webRequest){
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);

        String jsonBody = (String) webRequest.getAttribute(JSONBODYATTRIBUTE, NativeWebRequest.SCOPE_REQUEST);
        if (jsonBody==null){
            try {
                jsonBody = IOUtils.toString(servletRequest.getInputStream());
                webRequest.setAttribute(JSONBODYATTRIBUTE, jsonBody, NativeWebRequest.SCOPE_REQUEST);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return jsonBody;

    }

}

何が違うのですか?

  • Jacksonを使用してjsonを変換しています
  • アノテーションに値は必要ありません。MethodParameterからパラメーターの名前を読み取ることができます
  • Methodparameter =>からパラメーターのタイプも読み取ったので、ソリューションは汎用的である必要があります(文字列とDTOでテストしました)

BR


0

リクエストパラメータはGETとPOSTの両方に存在し、Getの場合はクエリ文字列としてURLに追加されますが、POSTの場合はリクエストボディ内にあります


0

jsonを追加する場所がわかりませんが、angularを使用してこのようにすると、requestBody:angluarなしで機能します。

    const params: HttpParams = new HttpParams().set('str1','val1').set('str2', ;val2;);
    return this.http.post<any>( this.urlMatch,  params , { observe: 'response' } );

java:

@PostMapping(URL_MATCH)
public ResponseEntity<Void> match(Long str1, Long str2) {
  log.debug("found: {} and {}", str1, str2);
}

0

良い。必要なフィールドを含む値オブジェクト(Vo)を作成することをお勧めします。コードは単純で、ジャクソンの機能を変更せず、さらに理解しやすくなっています。よろしく!


0

を使用することで、希望どおりの結果を得ることができます@RequestParam。このためには、次のことを行う必要があります。

  1. オブジェクトを表すRequestParamsパラメータを宣言し、 required、null値を送信できるようにする場合オプションをfalseにます。
  2. フロントエンドで、送信するオブジェクトを文字列化し、リクエストパラメータとして含めます。
  3. バックエンドで、JSON文字列を、Jackson ObjectMapperなどを使用して表すオブジェクトに戻します。

私は知っています、それは少しハックですが、うまくいきます!;)


0

また、ユーザーは@RequestBody Map<String, String> paramsparams.get("key")パラメータの値を取得するために使用できます


0

MultiValueマップを使用してrequestBodyを保持することもできます。ここにその例を示します。

    foosId -> pathVariable
    user -> extracted from the Map of request Body 

Mapを使用してリクエスト本文を保持するときの@RequestBodyアノテーションとは異なり、@ RequestParamでアノテーションを付ける必要があります。

Json RequestBodyでユーザーを送信します

  @RequestMapping(value = "v1/test/foos/{foosId}", method = RequestMethod.POST, headers = "Accept=application"
            + "/json",
            consumes = MediaType.APPLICATION_JSON_UTF8_VALUE ,
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public String postFoos(@PathVariable final Map<String, String> pathParam,
            @RequestParam final MultiValueMap<String, String> requestBody) {
        return "Post some Foos " + pathParam.get("foosId") + " " + requestBody.get("user");
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.