0%

Java重复读取httpRequest中的body

参考文档:
ContentCachingRequestWrapper解决入参读取

使用方法

增加拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;

/**
* 将HttpServletRequest包装成ContentCachingRequestWrapper,使其body可以多次读取
*/
@Component
@Order(1)
public class RequestWrapFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
ContentCachingRequestWrapper wrapper =
new ContentCachingRequestWrapper(httpServletRequest);
filterChain.doFilter(wrapper, httpServletResponse);
}
}

注册拦截器

注册方法不赘述。例如上述代码中@Component @Order(1)会自动注册Filter

读取方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletRequest;

import com.google.common.io.ByteSource;

import org.springframework.web.util.ContentCachingRequestWrapper;

public class HttpBodyReader {
private HttpBodyReader() {
}

/**
* 获取POST请求HttpServletRequest中的body信息
* HttpServletRequest本身是不可以重复读取body的,filter中对request进行了wrap,使其变成了ContentCachingRequestWrapper
* ContentCachingRequestWrapper是可重复读取的body的实现
* 必须在已经通过getInputStream()读取了数据后,改方法才能返回数据
* @param request
* @return
* @throws IOException
*/
public static String getRequestBody(HttpServletRequest request) throws IOException {
String responseBody = "";
if (request instanceof ContentCachingRequestWrapper) {
ContentCachingRequestWrapper cachingWrapper = (ContentCachingRequestWrapper) request;
byte[] buf = cachingWrapper.getContentAsByteArray();
if (buf.length > 0) {
responseBody = ByteSource.wrap(buf)
.asCharSource(StandardCharsets.UTF_8)
.read();
}
}
return responseBody;
}
}

原理

在spring框架读入输入流时会调用getInputStream()方法

wrapper重写getInputStream()方法,将流替换成ContentCachingInputStream内部类

1
2
3
4
5
6
7
@Override
public ServletInputStream getInputStream() throws IOException {
if (this.inputStream == null) {
this.inputStream = new ContentCachingInputStream(getRequest().getInputStream());
}
return this.inputStream;
}

ContentCachingInputStream重写了read方法,在流式读取的过程中把内容复写到外部类的cachedContent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public int read() throws IOException {
int ch = this.is.read();
if (ch != -1 && !this.overflow) {
if (contentCacheLimit != null && cachedContent.size() == contentCacheLimit) {
this.overflow = true;
handleContentOverflow(contentCacheLimit);
}
else {
cachedContent.write(ch);
}
}
return ch;
}

再通过getContentAsByteArray()方法即可反复读取cachedContent中内容