Khi xử lý dữ liệu JSON qua @RequestBody trong Spring Boot, luồng đầu vào chỉ được đọc một lần. Nếu không xử lý đúng cách, các thành phần phía sau sẽ không nhận được dữ liệu. Giải pháp là tạo wrapper lưu trữ luồng và cung cấp lại cho các thành phần tiếp theo.
public class CustomRequestWrapper extends HttpServletRequestWrapper {
private final String payload;
public CustomRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder content = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8))) {
char[] buffer = new char[2048];
int length;
while ((length = reader.read(buffer)) != -1) {
content.append(buffer, 0, length);
}
}
payload = content.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
ByteArrayInputStream input = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
return new ServletInputStream() {
@Override
public int read() throws IOException {
return input.read();
}
@Override
public boolean isFinished() {
return input.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));
}
public String getPayload() {
return payload;
}
}Áp dụng trong filter để ghi log và xử lý tiếp:
@Component
public class RequestDataFilter implements Filter {
private static final Logger log = LoggerFactory.getLogger(RequestDataFilter.class);
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) req;
String path = httpReq.getRequestURI();
String httpMethod = httpReq.getMethod();
CustomRequestWrapper wrappedRequest = new CustomRequestWrapper(httpReq);
String paramData = JsonUtils.serialize(httpReq.getParameterMap());
log.info("URI: {}, Method: {}, Params: {}, Body: {}",
path, httpMethod, paramData, wrappedRequest.getPayload());
chain.doFilter(wrappedRequest, res);
}
}Có thể sử dụng interceptor cho các controller, nhưng filter thường được ưa chuộng hơn.