NimChimpsky NimChimpsky - 7 months ago 109
Java Question

Passing multple variables in Requestbody to a spring mvc controller using Ajax

Is it necessary to wrap in a backing object ?

I want to do this :

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


And use json like this :

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


But instead I have to use :

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


And then use this JSON :

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


Is that correct? My other option would be, to change the request type to 'GET' and use '@RequestParam' in query string or use '@PathVariable' with either request type.

Answer

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.PSOT)
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.