Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring MVC: Doesn't deserialize JSON request body

I'm working on a Spring MVC project and one of the tasks I need to do requires me to have a string of JSON data sent through by the user in a POST request. I know that Spring will deserialize JSON using Jackson to objects, but if I try something like the following:

@RequestMapping(value = "/test", method = RequestMethod.POST)
public void doSomething(@RequestBody String json) {
    // do something
}

I simply get HTTP 400 Bad Request back ("The request sent by the client was syntactically incorrect.").

How can I get the raw JSON sent by the client as a string?

like image 353
Ryan Morrison Avatar asked May 09 '13 16:05

Ryan Morrison


4 Answers

You will usually see this type of error when Spring MVC finds a request mapping that matches the URL path but the parameters (or headers or something) don't match what the handler method is expecting.

If you use the @RequestBody annotation then I believe Spring MVC is expecting to map the entire body of the POST request to an Object. I'm guessing your body is not simply a String, but some full JSON object.

If you have a java model of the JSON object you are expecting then you could replace the String parameter with that in your doSomething declaration, such as

public void doSomething(@RequestBody MyObject myobj) {

If you don't have a Java object that matches the JSON then you could try to get it working by replacing the String type with a Map<String, Object> and see if that gets you closer to a working solution.

You could also turn on debug logging in Spring MVC to get more information on why it was a bad request.

Edit: Given your requirements in the comments, you could simply inject the HttpServletRequest into your method and read the body yourself.

public void doSomething(HttpServletRequest request) {
  String jsonBody = IOUtils.toString( request.getInputStream());
  // do stuff
}
like image 105
digitaljoel Avatar answered Oct 30 '22 06:10

digitaljoel


We had a situation where we wanted some controller methods to map the POST body to beans, and other methods where we just wanted the raw String. To accomplish this using the @RequestBody annotation, you need to configure multiple message converters, something like...

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
  <property name="useDefaultSuffixPattern" value="false"/>
</bean>

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
            <ref bean="jsonConverter" />
            <ref bean="marshallingConverter" />
            <ref bean="stringHttpMessageConverter" />
        </list>
    </property>
</bean>

<bean id="jsonConverter"
      class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
    <property name="supportedMediaTypes" value="application/json" />
</bean>

<bean id="marshallingConverter"
      class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
    <constructor-arg ref="jaxb2Marshaller" />
    <property name="supportedMediaTypes" value="application/xml"/>
</bean>

<bean id="stringHttpMessageConverter"
      class="org.springframework.http.converter.StringHttpMessageConverter">
    <property name="supportedMediaTypes" value="text/plain"/>
</bean>

Then, requests to the various methods must specify the "content-type" header with an appropriate value. For those methods where the request body is mapped to a JAXB bean, specify "application/xml". And for those where the request body is a String, use "text/plain".

like image 36
guest Avatar answered Oct 30 '22 06:10

guest


You could try avoiding @RequestBody altogether and instead grab the request body directly through a InputStream/Reader or a WebRequest/HttpServletRequest.

like image 2
pimlottc Avatar answered Oct 30 '22 05:10

pimlottc


In my case is because the json has not quoted the field names. An example, this is not accepted:

{ entity: "OneEntity"} 

but this one yes:

{ "entity": "OneEntity"}

I haven't found yet how I can configure object mapping in spring context. I know there is a JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES but I don't know how set that for object mapper.

like image 1
Toni Gamez Avatar answered Oct 30 '22 07:10

Toni Gamez