admin管理员组文章数量:1353303
I'm developing a Spring Boot application (with Spring Security) and need to add default headers (specifically, Content-Type and Content-Length) to requests targeting the endpoint /api/v1/mpos/set-token when those headers are missing. To achieve this, I wrote a custom filter that extends OncePerRequestFilter and wraps the incoming HttpServletRequest so that I can read and cache its body. I use the following code:
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SetTokenDefaultHeadersFilter extends OncePerRequestFilter {
private static final String DEFAULT_CONTENT_TYPE = "application/json";
private static final String TARGET_URI = "/api/v1/mpos/set-token";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (TARGET_URI.equals(request.getRequestURI())
&& "POST".equalsIgnoreCase(request.getMethod())
&& (request.getHeader("Content-Type") == null || request.getHeader("Content-Length") == null)) {
HttpServletRequest wrappedRequest = new HttpServletRequestWrapper(request) {
private final byte[] cachedBody = toByteArray(request.getInputStream());
private byte[] toByteArray(InputStream input) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
return baos.toByteArray();
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream bais = new ByteArrayInputStream(cachedBody);
return new ServletInputStream() {
@Override
public int read() {
return bais.read();
}
@Override
public boolean isFinished() {
return bais.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
// Not implemented
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));
}
@Override
public int getContentLength() {
return cachedBody.length;
}
@Override
public long getContentLengthLong() {
return cachedBody.length;
}
@Override
public String getHeader(String name) {
if ("Content-Type".equalsIgnoreCase(name)) {
String header = super.getHeader(name);
return header == null ? DEFAULT_CONTENT_TYPE : header;
}
if ("Content-Length".equalsIgnoreCase(name)) {
String header = super.getHeader(name);
return header == null ? String.valueOf(cachedBody.length) : header;
}
return super.getHeader(name);
}
};
filterChain.doFilter(wrappedRequest, response);
} else {
filterChain.doFilter(request, response);
}
}
}
I have verified (via logging and FilterRegistrationBean) that my filter is registered with the highest precedence and should run first. However, when I call input.read(buffer), it immediately returns -1 (i.e. EOF), and the cached body is an empty array.
What I've tried so far:
Verified the filter order – my filter is running first.
Checked for other filters that might be reading the body; the logs indicate that my filter is before them.
My questions:
What could be consuming the request body before my filter even gets a chance to read it?
How can I ensure that my filter has access to the full request body so I can wrap it and add the default headers?
Is there a recommended approach in Spring Boot to cache the request body before any processing?
Any insights or suggestions would be greatly appreciated.
版权声明:本文标题:java - Custom OncePerRequestFilter reads empty request body – why is the body already consumed? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1743874494a2554081.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论