How to modify request body before reaching controller in spring boot

Short Answer
Yes, but not easily.

Details
I know of three options to change the body of a request "before" it arrives at the handler method in the controller;

  1. Use AOP to change the request before the method is called.
  2. Create an HTTP filter.
  3. Create a custom Spring HandlerInterceptor.

Since you are already using spring-boot, option 3, custom Spring HandlerInterceptor, seems like the best option for you.

Here is a link to a Baeldung Article covering spring HandlerInterceptors.

The Baeldung article is not the full answer for your problem because you can only read the InputStrem returned by HttpServletRequest one time.

You will need to create a wrapper class that extends HttpServletRequest and wrap every request in your wrapper class within your custom HandlerInterceptor or in a custom Filter (Filter might be the way to go here).

Wrapper Class

  1. Read the HttpServletRequest InputStream in the wrapper class constructor
  2. Modify the body per your requirements.
  3. Write the modified body to a ByteArrayOutputStream.
  4. Use toByteArray to retrieve the actual byte[] from the stream.
  5. Close the ByteArrayOutputStream (try-with-resources is good for this).
  6. Override the getInputStream method.
  7. Wrap the byte[] in a ByteArrayInputStream every time the getInputStream is called. Return this stream.

How To Wrap the Request

  1. In your Filter, instantiate your wrapper class and pass in the original request (which is a parameter to the doFilter method).
  2. Pass the wrapper to the chain.doFilter method (not the original request).

Another alternative would be adding an attribute to the HttpServletRequest object. And after that you can read that attribute in the Controller class with @RequestAttribute annotation.

In the Interceptor

@Component
    public class SimpleInterceptor extends HandlerInterceptorAdapter {

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws ServletException, IOException {
            String parameter = request.getParameter("parameter");
            if (parameter == "somevalue") {
                request.setAttribute("customAttribute", "value");
            }

            return true;
        }

    }

In the Controller

@RestController
@RequestMapping("")
public class SampleController {


    @RequestMapping(value = "/sample",method = RequestMethod.POST)
    public String work(@RequestBody SampleRequest sampleRequest, @RequestAttribute("customAttribute") String customAttribute) {
        System.out.println(customAttribute);
        return "This works";
    }
}

This has advantage of not modifying the request body.


My answer using HTTP Filter.

RequestFilter.java

@Component
public class RequestFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        RequestWrapper wrappedRequest = new RequestWrapper((HttpServletRequest) request);
        chain.doFilter(wrappedRequest, response);
    }

    @Override
    public void destroy() {

    }

}

RequestWrapper.java

 public class RequestWrapper extends HttpServletRequestWrapper {
        private final String body;
        private ObjectMapper objectMapper = new ObjectMapper();
    
        public RequestWrapper(HttpServletRequest request) throws IOException {
            // So that other request method behave just like before
            super(request);
    
            StringBuilder stringBuilder = new StringBuilder();
            BufferedReader bufferedReader = null;
            try {
                InputStream inputStream = request.getInputStream();
                if (inputStream != null) {
                    bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                    char[] charBuffer = new char[128];
                    int bytesRead = -1;
                    while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                        stringBuilder.append(charBuffer, 0, bytesRead);
                    }
    
                } else {
                    stringBuilder.append("");
                }
            } catch (IOException ex) {
                throw ex;
            } finally {
                if (bufferedReader != null) {
                    try {
                        bufferedReader.close();
                    } catch (IOException ex) {
                        throw ex;
                    }
                }
            }
            // Store request body content in 'requestBody' variable
            String requestBody = stringBuilder.toString();
            JsonNode jsonNode = objectMapper.readTree(requestBody);
            //TODO -- Update your request body here 
            //Sample
            ((ObjectNode) jsonNode).remove("key");
            // Finally store updated request body content in 'body' variable
            body = jsonNode.toString();
    
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
            ServletInputStream servletInputStream = new ServletInputStream() {
                public int read() throws IOException {
                    return byteArrayInputStream.read();
                }
    
                @Override
                public boolean isFinished() {
                    return false;
                }
    
                @Override
                public boolean isReady() {
                    return false;
                }
    
                @Override
                public void setReadListener(ReadListener listener) {
    
                }
            };
            return servletInputStream;
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(this.getInputStream()));
        }
}