使用Ajax将@RequestBody中的多个变量传递给Spring MVC控制器

时间:2012-10-15 10:17:45

标签: java spring http 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"
    }
}

这是对的吗?我的另一种选择是将RequestMethod更改为GET并在查询字符串中使用@RequestParam或将@PathVariableRequestMethod一起使用。

14 个答案:

答案 0 :(得分:74)

你是对的,@ RequestBody带注释的参数应该保存请求的整个主体并绑定到一个对象,所以你基本上必须选择你的选项。

如果你绝对想要你的方法,你可以做一个自定义的实现:

说这是你的json:

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

你想把它绑定到这两个参数:

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

首先使用JSON路径定义自定义注释,例如@JsonArg,如所需信息的路径:

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中注册即可。有点涉及,但这应该干净利落。

答案 1 :(得分:65)

虽然@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"

答案 2 :(得分:8)

您可以通过使用body和path变量来混合post参数以获得更简单的数据类型:

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

答案 3 :(得分:2)

@RequestParam是客户端发送的HTTP GETPOST参数,请求映射是URL的一段变量:

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

var1&amp; var2是请求参数。

http:/host/form/{params}

{params}是请求映射。您可以拨打以下服务:http:/host/form/userhttp:/host/form/firm 公司和公司用户用作Pathvariable

答案 4 :(得分: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"
}

答案 5 :(得分:1)

用于传递多个对象,参数,变量等。您可以使用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.
}

我希望有帮助。

答案 6 :(得分:0)

您可以做一些简单的事情,而不是使用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";
}

希望这会对你有所帮助。

答案 7 :(得分:0)

GET和POST都存在请求参数,For Get它将作为查询字符串附加到URL,但对于POST,它位于Request Body中

答案 8 :(得分:0)

我已经改编了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 =&gt;中读取了参数的类型。所以解决方案应该是通用的(我用字符串和DTO测试)

BR

答案 9 :(得分:0)

不知道你在哪里添加json但是如果我这样做的话有角度它没有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);
}

答案 10 :(得分:0)

好。 我建议创建一个包含所需字段的值对象(Vo)。代码更简单,我们不会改变杰克逊的功能,它更容易理解。 此致!

答案 11 :(得分:0)

您可以使用@RequestParam实现您想要的。为此,您应该执行以下操作:

  1. 声明代表您的对象的RequestParams参数,并将required选项设置为false(如果您希望能够发送空值)。
  2. 在前端,对要发送的对象进行字符串化,并将其作为请求参数包括在内。
  3. 在后端,使用Jackson ObjectMapper或类似的东西将JSON字符串转换回它们表示的对象,瞧!

我知道,这有点hack,但是可以用! ;)

答案 12 :(得分:0)

您还可以使用@RequestBody Map<String, String> params,然后使用params.get("key")来获取参数的值

答案 13 :(得分:0)

您还可以使用MultiValue Map来保留requestBody。 这是一个例子。

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

与使用地图保存请求正文时使用的@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");
    }