How can I get a Spring @RestController to accept parameters in JSON format rather than www-form-urlencoded?

12,876

The @RequestParam javadoc states

Annotation which indicates that a method parameter should be bound to a web request parameter.

This is retrieved through the various ServletRequest methods for request parameters. For example, ServletRequest#getParameterMap() which states

Request parameters are extra information sent with the request. For HTTP servlets, parameters are contained in the query string or posted form data.

In your second snippet, you aren't sending either. You're sending JSON in the request body.

Spring has a mechanism (it has many, and custom ones) for deserializing that into the data you expect. The standard solution is @RequestBody, assuming you have an appropriately registered HttpMessageConverter that can handle the JSON. Spring automatically registers MappingJackson2HttpMessageConverter if you have Jackson 2 on the classpath, which can correctly deserialize JSON into Java POJO types.

The documentation gives a number of examples and explains how you would use it. With JSON, you could define a POJO type with fields that correspond to the ones you send

class RequestPojo {
    private String paramA;
    private String paramB;
    private String paramC;
    // and corresponding getters and setters
}

and add a @RequestBody annotated parameter of this type to your handler method

public ServiceAResponse serviceA(@RequestBody RequestPojo pojo) {
    pojo.getParamB(); // do something
    return ...;
}

Jackson lets you define, through annotations or through configuration of the ObjectMapper, how to deal with absent values.

@RestController is not involved here. As its javadoc states,

Types that carry this annotation are treated as controllers where @RequestMapping methods assume @ResponseBody semantics by default.

Share:
12,876
Jared
Author by

Jared

I work for Amazon Web Services. Opinions expressed on social media are my own.

Updated on June 04, 2022

Comments

  • Jared
    Jared almost 2 years

    Using Spring 4.1.7 on JDK 1.8, I have an @RestController class that looks like this:

    @RestController
    public class ServiceAController {
    
        public static final Logger LOG = Logger.getLogger(ServiceAController.class);
    
        @RequestMapping(value="/rest/servicea", method=RequestMethod.POST)
        public ServiceAResponse serviceA(@RequestParam(value="parmA", defaultValue="defaultParmA") String parmA,
                @RequestParam(value="parmB", defaultValue="defaultParmB") String parmB,
                @RequestParam(value="parmC", defaultValue="defaulParmC") String parmC) {
            LOG.info("Inside Service A handler: " + parmA + " B: "+ parmB + " C: "+ parmC);
    }
    

    When I send a POST to /rest/servicea from a javascript like this, everything works, and I see the values "a", "b", and "c" printed in my log:

        var data = {        
            "parmA": "a",
            "parmB": "b",
            "parmC": "c"
        }
    
        $.ajax({
            type: "POST",
            url: "./rest/servicea",
            contentType: "application/x-www-form-urlencoded",
            data: data,
            dataType: "json",
            success: submitAuthSuccess,
            error: submitAuthFailure 
        })
    

    However, when I try to change the call from the javascript to this (to change the protocol to REST rather than www-urlencode), I get the default values (defaultParmA, defaultParmB, defaultParmC) in my log:

        var data = {        
            "parmA": "a",
            "parmB": "b",
            "parmC": "c"
        }
    
        $.ajax({
            type: "POST",
            url: "./rest/servicea",
            contentType: "application/json",
            data: JSON.stringify(data),
            dataType: "json",
            success: submitAuthSuccess,
            error: submitAuthFailure 
        })
    

    I think I'm missing something in the @RestController class to get it to parse the JSON rather than expecting www-urlencoded data.

    I tried changing the @RequestMapping annotation on the serviceA method to add the consumes="application/json" attribute, but that had no effect.

    What can I change to make this work, using JSON rather than urlencoded data in the POST body?