1. Overview

In this tutorial, we'll look at how we can read the request body multiple times using Spring MVC.

2. HttpServletRequest and Request Body

Spring MVC is built on the Servlet API where Spring MVC's entry point is indeed a servlet, namely the Dispatcher Servlet. So when we're dealing with the HTTP requests, HttpServletRequest provides us two ways to read the request body - getInputStream and getReader methods. Each of these methods relies on the same InputStream. 

So when we read the InputStream once, we can't read it again.

Now we'll look at how we can access the original request content, even if we can't read the same InputStream twice.

3. Using ContentCachingRequestWrapper

Spring MVC provides the ContentCachingRequestWrapper class. It is a wrapper around the original HttpServletRequest object. When we read the request body, ContentCachingRequestWrapper caches the content for later usage.

To use it, we must first create a web filter which wraps the original HttpServletRequest:

@Component
public class CachingRequestBodyFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
      throws IOException, ServletException {
        HttpServletRequest currentRequest = (HttpServletRequest) servletRequest;
        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(currentRequest);

        chain.doFilter(wrappedRequest, servletResponse);
    }
}

Here, we have the CachingRequestBodyFilter class. It runs on every request before our controller methods.

After this step, we can access the request body even if it has been read before:

@RestController
public class GreetController {

    @PostMapping("/greet")
    public String greet(@RequestBody String name, HttpServletRequest request) {
        ContentCachingRequestWrapper requestWrapper = (ContentCachingRequestWrapper) request;

        String requestBody = new String(requestWrapper.getContentAsByteArray());
        return "Greetings " + requestBody;
    }
}

In the GreetController class, we're using the @RequestBody annotation. Hence Spring MVC reads the request body for us and assigns it to the name variable. Any subsequent attempts to read the input stream again will fail.

However, we can get the original byte[] content using the getContentAsByteArray method of ContentCachingRequestWrapper. 

4. Summary

In this tutorial, we've looked at how we can read the request body multiple times using Spring MVC.

As always, the source code for all examples in this tutorial is available on Github.