目录
问题症状:
不能同时使用HttpServletRequestWrapper,又在Controller类里用@RequestBody。不然应该是会报「流已关闭」错误


豆包解释:[1](#豆包解释:1)
需要在 Filter 中手动读取 Body(关键:避免流已关闭)。
由于 Request Body 的输入流(ServletInputStream)是 只能读取一次 的,直接读取后会导致后续 Controller 无法再读取,因此需要先「缓存 Body」,再通过包装类(HttpServletRequestWrapper)重写流读取逻辑。
解决:

请求包装类代码如下,其关键应是把json body缓存在byte[] cachedBody中了:
java
import com.alibaba.fastjson.JSONArray;
import com.yrd.gpt.util.JsonUtil;
import com.yrd.gpt.util.StringUtil;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StreamUtils;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* 支持重复读取
*/
@Slf4j
public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
private final byte[] cachedBody;
private Map<String, String[]> parameterMap;
public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
// 读取原始流并缓存(Spring 的 StreamUtils 更简洁)
this.cachedBody = StreamUtils.copyToByteArray(request.getInputStream());
String jsonBody = getBody();
HashMap<String, Object> requestMap = JsonUtil.fromJson(jsonBody, HashMap.class);
parameterMap = new HashMap<>();
if (null != requestMap && !requestMap.isEmpty()) {
for (Map.Entry<String, Object> entry : requestMap.entrySet()) {
String key = entry.getKey();
if (StringUtil.isEmpty(key)) {
continue;
}
Object value = entry.getValue();
if (null == value) {
continue;
}
if (value instanceof JSONArray) {
String[] values = Arrays.stream(((JSONArray) value).toArray())
.map(Object::toString)
.toArray(String[]::new);
log.warn("array: key@" + key + ";values@" + Arrays.toString(values));
this.parameterMap.put(entry.getKey(), values);
} else {
this.parameterMap.put(entry.getKey(), new String[]{String.valueOf(value)});
}
}
}
}
// 重写输入流,返回缓存的字节数组(支持重复读取)
@Override
public ServletInputStream getInputStream() throws IOException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(cachedBody);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return byteArrayInputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
}
};
}
// 重写 getReader(避免某些场景下通过 Reader 读取流失败)
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));
}
// 获取 JSON 字符串的便捷方法
public String getBody() {
return new String(cachedBody, StandardCharsets.UTF_8);
}
@Override
public String getParameter(String name) {
String[] results = this.parameterMap.get(name);
if (null == results || results.length < 1) {
return null;
} else {
return results[0];
}
}
@Override
public Map<String, String[]> getParameterMap() {
return this.parameterMap;
}
@Override
public Enumeration<String> getParameterNames() {
Vector<String> vector = new Vector<>(this.parameterMap.keySet());
return vector.elements();
}
@Override
public String[] getParameterValues(String name) {
return this.parameterMap.get(name);
}
}