Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring's @RequestBody providing empty string on POST

I have an application with Spring 3.0.5.RELEASE trying to get the full content of a post using @RequestBody. The method is called, but the string passed is always empty. I have checked, by placing breakpoints, that the StringHttpMessageConverter is called, but the inner HttpInputMessage is empty.

I've seen this issue with both Jetty and Tomcat, so I'm discarding it's a problem with the container.

Here is my sample controller:

@Controller
@RequestMapping("/")
public class SubscriptionController {
    @RequestMapping(value = "/requestbody", method = RequestMethod.POST)
    public ModelAndView mycustomAction(@RequestBody String body) {

        // body is always empty
        Logger.getLogger(this.getClass()).debug("REQUEST BODY '" + body + "'");
        return new ModelAndView("empty");
    }
}

My application context is defined as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <!-- Enable auto detection of controllers -->
    <context:component-scan base-package="com.big.viajerotelcel.controller" />

    <!--
        use annotation driven mvc and one single validator with JSR-303
        standard
    -->
    <mvc:annotation-driven />

    <!--
        Message source for this context, loaded from localized "messages_xx"
        files
    -->

    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basenames" value="classpath:i18n/messages" />
        <property name="defaultEncoding" value="UTF-8" />
    </bean>

    <!-- Declare the Interceptor -->
    <mvc:interceptors>
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
            p:paramName="locale" />
    </mvc:interceptors>

    <!-- Declare the Resolver -->
    <bean id="localeResolver"
        class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />

    <!-- will load Tiles definitions! -->
    <bean id="tilesConfigurer"
        class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
        <property name="definitions">
            <list>
                <value>/WEB-INF/general.xml</value>
            </list>
        </property>
    </bean>

    <!-- Tiles view resolver -->
    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="viewClass"
            value="org.springframework.web.servlet.view.tiles2.TilesView" />
    </bean>

    <!-- Configure the multipart resolver -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--
            one of the properties available; the maximum file size in bytes (5MB)
        -->
        <property name="maxUploadSize" value="5120000" />
    </bean>

    <!-- Adding these lines has no effect, the StringHttpMessageConverter is called either way -->
<!--    <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"/>-->
<!--           -->
<!--    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">-->
<!--        <property name="messageConverters">-->
<!--          <list>-->
<!--            <ref bean="stringHttpMessageConverter"/>-->
<!--          </list>-->
<!--        </property>-->
<!--    </bean>-->
</beans>

I'm testing this using curl as follows:

curl -d asd=123 -d qwe=456 http://localhost:8080/requestbody

Any ideas or help is more than welcomed!

like image 944
Johnco Avatar asked Apr 27 '11 15:04

Johnco


People also ask

Can a request body be null?

body. The read-only body property of the Request interface contains a ReadableStream with the body contents that have been added to the request. Note that a request using the GET or HEAD method cannot have a body and null is return in these cases.

What is@ RequestBody annotation?

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.

Can we make RequestBody optional?

requestBody, content and Media Types content lists the media types consumed by the operation (such as application/json ) and specifies the schema for each media type. Request bodies are optional by default. To mark the body as required, use required: true .

How to get Request body from POST Request?

Enter a Request BodyAs part of a POST, PUT, or PATCH request, a data payload can be sent to the server in the body of the request. When you select one of those methods from the method drop-down button, the API Connector form changes to display an input field for the request body.


1 Answers

Here is a code snippet of ServletServerHttpRequest, which extends HttpInputMessage. I am pretty positive this is the implementation that you are using in your code:

public InputStream getBody() throws IOException {
    return this.servletRequest.getInputStream();
}

In other words, the request body is meant to be read as the input stream of the HttpServletRequest object.

The request's input stream is not valid in several situations, but I can't find the correct documentation for it at the moment. For example, if you call request.getParameter() on a post request, tomcat has to read the input stream in order to interpret the parameters, thus afterwards when you read the input stream, it is empty because it has reached the end already.

Perhaps you are invoking getParameter somewhere in an interceptor or perhaps a filter defined in web.xml. Another option is that Spring is doing that for you, for example, if your controller has some other method with complex @RequestMappings (such as reading param values, or header values).

I have two suggestions for you:

  1. Add a servlet filter (before spring gets a chance to act), and wrap the request with your own wrapper (just extend HttpServletRequestWrapper). This way you can put breakpoints or log messages at some methods of the request object and see who's calling them.

  2. Use a pojo object parameter, and setup the bindings. It seems like a much cleaner way to read post data.

like image 109
Yoni Avatar answered Sep 27 '22 21:09

Yoni