Spring-web 使用ContentCachingRequestWrapper包装HttpServletRequest,依旧出现HttpMessageNotReadableException: Required request body is missing的解决方案

这里先列出我的环境信息:
Group ID: org.springframework.boot Artifact ID: spring-boot Version: 3.5.0-SNAPSHOT
Group ID: org.springframework Artifact ID: spring-web Version: 6.2.7-SNAPSHOT
Group ID: org.springframework Artifact ID: spring-webmvc Version: 6.2.7-SNAPSHOT

我在过滤器Filter中使用ContentCachingRequestWrapper包装了HttpServletRequest类型的参数,
依旧不好用,还是会抛出 HttpMessageNotReadableException: Required request body is missing 错误。
原因在于我在过滤器中进行提前读取了请求体Body中的内容导致的。
因为你在过滤器提前消耗了请求流中的内容,在Controller层的控制器那里就获取不到流中的内容了。
我的控制器大概方法如下:

@PostMapping
void controller(@RequestBody String body)

反之,如果你不消耗流的话,ContentCachingRequestWrapper里的方法内容获取的都是空的。
如何提取消耗流的内容,让ContentCachingRequestWrapper实例有值呢?
答案就是wrappedRequest.getInputStream().readAllBytes();调用这个方法即可。

wrappedRequest是ContentCachingRequestWrapper的实例变量

也就是纵然是ContentCachingRequestWrapper对象,getInputStream方法也只能读取一次。
解决方法就是根据ContentCachingRequestWrapper实现,自己定义一个支持可重复读取流的方法。
这里直接给出Wrapper的示例,用法和ContentCachingRequestWrapper一样。

import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.util.StreamUtils;
import org.springframework.web.util.ContentCachingRequestWrapper;

import java.io.*;

/**
 *
 */
public class JSONContentCachingRequestWrapper extends ContentCachingRequestWrapper {
    public JSONContentCachingRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    public JSONContentCachingRequestWrapper(HttpServletRequest request, int contentCacheLimit) {
        super(request, contentCacheLimit);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        ServletInputStream inputStream = this.getInputStream();
        if(inputStream.isFinished()){
            byte[] contentAsByteArray = this.getContentAsByteArray();
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(contentAsByteArray);
            return new BufferedReader(new InputStreamReader(byteArrayInputStream, getCharacterEncoding()));
        }
        return super.getReader();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        ServletInputStream inputStream = super.getInputStream();
        if(inputStream.isFinished()){
            return new CachedBodyServletInputStream(super.getContentAsByteArray());
        }
        return inputStream;
    }

    public static class CachedBodyServletInputStream extends ServletInputStream {

        private final ByteArrayInputStream cachedBodyInputStream;

        public CachedBodyServletInputStream(byte[] cachedBody) {
            this.cachedBodyInputStream = new ByteArrayInputStream(cachedBody);
        }

        @Override
        public boolean isFinished() {
            return cachedBodyInputStream.available() == 0;
        }

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

        @Override
        public void setReadListener(ReadListener listener) {
            throw new UnsupportedOperationException();
        }

        @Override
        public int read() throws IOException {
            return cachedBodyInputStream.read();
        }

    }

}

参考资料:
https://stackoverflow.com/questions/63791808/required-request-body-is-missing-after-using-contentcachingrequestwrapper
https://www.baeldung.com/spring-reading-httpservletrequest-multiple-times

出奇的,遇到这种问题的人不多哈。

posted @ 2025-05-14 14:56  星小梦  阅读(591)  评论(0)    收藏  举报