web-dev-qa-db-ja.com

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

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

@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を使用するか、RequestMethod@PathVariableを使用することです。

88
NimChimpsky

正しいです。@ 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)

次に、上記で定義した JsonPath を使用して実際の引数を解決するカスタム HandlerMethodArgumentResolver を記述します。

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に登録するだけです。少し複雑ですが、これはきれいに機能するはずです。

81
Biju Kunjummen

@RequestBodyは単一のオブジェクトにマップする必要があるのは事実ですが、そのオブジェクトはMapになる可能性があるため、これは達成しようとしているものへの良い方法を提供しますオブジェクト):

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

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

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

単純なデータ型のbody変数とpath変数を使用して、post引数を混在させることができます。

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

複数のオブジェクト、パラメーター、変数などを渡すため。パラメータとしてjacksonライブラリの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.
}

これが役立つことを願っています。

3
azwar_akbar

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

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

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

http:/Host/form/{params}

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

2
psisodia

@RequestParamを使用して、目的を達成できます。このためには、次のことを行う必要があります。

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

ちょっとしたハックですが、動作します! ;)

0
Maurice

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
tibi

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

0
Matias Zamorano

@RequestBody Map<String, String> paramsを使用してから、params.get("key")を使用してパラメーターの値を取得することもできます。

0
Will

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

0
Kaleem

私は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;

    }

}

違いは何ですか:

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

BR

0
user3227576

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

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

0
Japan Trivedi