Resolving 'Required request body is missing' in Spring Interceptors

When implementing a custom interceptor in a Spring Boot application to validate specific parameters, an exception Required request body is missing may occur. This issue typically arises after adding an interceptor that reads the request body, causing subsequent attempts to read the body (such as via @RequestBody) to fail.

The root cause is that the HttpServletRequest input stream (getInputStream()) can only be read once. If the interceptor consumes the stream to inspect the payload, the dispatcher servlet cannot read it again when processing the controller method.

To solve this, wrap the request in a custom wrapper that caches the body content. Use a Filter to replace the incoming request with this wrapper, allowing the stream to be read multiple times.

Custom Request Wrapper

This class caches the request body bytes in a ByteArrayInputStream.

import org.springframework.util.StreamUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;

public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
    private final byte[] cachedPayload;

    public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
        super(request);
        // Cache the original body bytes
        this.cachedPayload = StreamUtils.copyToByteArray(request.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream() {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedPayload);

        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return byteArrayInputStream.available() == 0;
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
                // Not implemented for simplicity
            }

            @Override
            public int read() {
                return byteArrayInputStream.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));
    }

    public String getCachedBodyAsString() {
        return new String(this.cachedPayload, StandardCharsets.UTF_8);
    }
}

Filter Configuraton

Register a filter to wrap every incoming request with the custom wrapper.

import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component
@WebFilter(urlPatterns = "/**")
public class RequestCachingFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 
            throws IOException, ServletException {
        
        if (servletRequest instanceof HttpServletRequest) {
            CachedBodyHttpServletRequest wrappedRequest = 
                new CachedBodyHttpServletRequest((HttpServletRequest) servletRequest);
            filterChain.doFilter(wrappedRequest, servletResponse);
        } else {
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }
}

Accessing Body in Interceptor

Now, the interceptor can read the body without breaking the controller's @RequestBody.

import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class AuthInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(AuthInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        String payload = "";
        
        if (request instanceof CachedBodyHttpServletRequest) {
            payload = ((CachedBodyHttpServletRequest) request).getCachedBodyAsString();
        } else {
            // Fallback or log error
            return false;
        }

        // Example: Extract a token from the JSON payload
        try {
            JSONObject json = JSONObject.parseObject(payload);
            String authToken = json.getString("sessionToken");
            logger.info("Extracted Token: {}", authToken);
            
            // Perform validation logic here...
            return true;
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return false;
        }
    }
}

Tags: java Spring Boot interceptor HttpServletRequest Request Body

Posted on Tue, 12 May 2026 14:13:08 +0000 by bond00