Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing multiple variables in @RequestBody to a Spring MVC controller using Ajax

People also ask

How do I pass multiple parameters in spring REST URL?

Just add two request parameters, and give the correct path. Send that as a POST with the JSON data in the request body, not in the URL, and specify a content type of application/json .

Can we use RequestParam and RequestBody together?

The handler for @RequestBody reads the body and binds it to the parameter. The handler for @RequestParam can then get the request parameter from the URL query string. The handler for @RequestParam reads from both the body and the URL query String.

What is the difference between @RequestBody and @RequestParam?

So basically, while @RequestBody maps entire user request (even for POST) to a String variable, @RequestParam does so with one (or more - but it is more complicated) request param to your method argument.

What is the use of @RequestBody in Spring MVC?

Simply put, the @RequestBody annotation maps the HttpRequest body to a transfer or domain object, enabling automatic deserialization of the inbound HttpRequest body onto a Java object. Spring automatically deserializes the JSON into a Java type, assuming an appropriate one is specified.


While it's true that @RequestBody must map to a single object, that object can be a Map, so this gets you a good way to what you are attempting to achieve (no need to write a one off backing object):

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

You can also bind to Jackson's ObjectNode if you want a full JSON tree:

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

You are correct, @RequestBody annotated parameter is expected to hold the entire body of the request and bind to one object, so you essentially will have to go with your options.

If you absolutely want your approach, there is a custom implementation that you can do though:

Say this is your json:

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

and you want to bind it to the two params here:

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

First define a custom annotation, say @JsonArg, with the JSON path like path to the information that you want:

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

Now write a Custom HandlerMethodArgumentResolver which uses the JsonPath defined above to resolve the actual argument:

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

    }
}

Now just register this with Spring MVC. A bit involved, but this should work cleanly.


For passing multiple object, params, variable and so on. You can do it dynamically using ObjectNode from jackson library as your param. You can do it like this way:

@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.
}

I hope this help.


You can mix up the post argument by using body and path variable for simpler data types:

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

@RequestParam is the HTTP GET or POST parameter sent by client, request mapping is a segment of URL which's variable:

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

var1 & var2 are request params.

http:/host/form/{params}

{params} is a request mapping. you could call your service like : http:/host/form/user or http:/host/form/firm where firm & user are used as Pathvariable.


The easy solution is to create a payload class that has the str1 and the str2 as attributes:

@Getter
@Setter
public class ObjHolder{

String str1;
String str2;

}

And after you can pass

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

and the body of your request is:

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

Instead of using json, you can do simple thing.

$.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.>
                });

Now in the controller you need to map the ajax request as below:

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

Hope this helps you.