0%

post请求体内容无法重复获取

post请求体内容无法重复获取

为什么会无法重复读取呢?

以tomcat为例,在进行请求体读取时实际底层调用的是org.apache.catalina.connector.Request的getInputStream()方法,而该方法返回的是CoyoteInputStream输入流

1
2
3
4
5
6
7
8
9
10
11
12
13
public ServletInputStream getInputStream() throws IOException {

if (usingReader) {
throw new IllegalStateException(sm.getString("coyoteRequest.getInputStream.ise"));
}

usingInputStream = true;
if (inputStream == null) {
inputStream = new CoyoteInputStream(inputBuffer);
}
return inputStream;

}

在使用CoyoteInputStream进行读取时

1
2
3
4
5
6
7
8
9
10
11
12
13
public int read(byte[] b, int off, int len) throws IOException {
// 如果流关闭,则抛出异常
if (closed) {
throw new IOException(sm.getString("inputBuffer.streamClosed"));
}
// 如果已经读完了,则返回-1
if (checkByteBufferEof()) {
return -1;
}
int n = Math.min(len, bb.remaining());
bb.get(b, off, n);
return n;
}

而流读取完毕都会进行close,这个流close之后,close状态就置为了true,所以导致流无法进行二次读取

那么如何解决呢?将tomcat的Request类进行重新实现吗?代价太大了,sun公司当初在设计的时候就已经提供了解决方法,对于请求和响应,sun公司提供了包装类,可以HttpServletRequestWrapper类包装原始的request对象,实现了HttpServletRequest接口的所有方法,内部调用了所包装的request对象的对应方法;相应的也有HttpServletResponseWrapper类来包装原始的response对象继承HttpServletRequestWrapper来进行方法重写

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
// 存储请求体
private byte[] body;

private HttpServletRequest orgRequest;

public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
this.orgRequest = request;
body = HttpHelper.getBody(request);
}

public HttpServletRequest getOrgRequest() {
return this.orgRequest;
}

// 重写读取,从存储的字节数组中读
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}

// 重写读取,从存储的字节数组中读
@Override
public ServletInputStream getInputStream() throws IOException {

final ByteArrayInputStream bais = new ByteArrayInputStream(body);

return new ServletInputStream() {

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

@Override
public boolean isFinished() {
return false;
}

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

@Override
public void setReadListener(ReadListener readListener) {

}
};
}
}