请求流支持多次获取
- 默认情况下流只能获取一次
- 如果你在
AOP
中获取请求流,则Controller
则无法获取请求流
配置
java
package cloud.xuxiaowei.utils.servlet;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* 请求流支持多次获取 {@link HttpServletRequest}
*
* @author xuxiaowei
* @see <a href=
* "https://stackoverflow.com/questions/10210645/http-servlet-request-lose-params-from-post-body-after-read-it-once/17129256#17129256">http-servlet-request-lose-params-from-post-body-after-read-it-once</a>
* @since 0.0.1
*/
public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* 用于缓存输入流
*/
private ByteArrayOutputStream cachedBytes;
public InputStreamHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (cachedBytes == null) {
// 首次获取流时,将流放入 缓存输入流 中
cacheInputStream();
}
// 从 缓存输入流 中获取流并返回
return new CachedServletInputStream(cachedBytes.toByteArray());
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/**
* 首次获取流时,将流放入 缓存输入流 中
*/
private void cacheInputStream() throws IOException {
// 缓存输入流以便多次读取。为了方便, 我使用 org.apache.commons IOUtils
cachedBytes = new ByteArrayOutputStream();
IOUtils.copy(super.getInputStream(), cachedBytes);
}
/**
* 读取缓存的请求正文的输入流
* <p>
* 用于根据 缓存输入流 创建一个可返回的 {@link ServletInputStream}
*/
public static class CachedServletInputStream extends ServletInputStream {
private final ByteArrayInputStream input;
public CachedServletInputStream(byte[] buf) {
// 从缓存的请求正文创建一个新的输入流
input = new ByteArrayInputStream(buf);
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return input.read();
}
}
}
java
package cloud.xuxiaowei.utils.servlet;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import java.io.*;
/**
* 请求流支持多次获取 {@link HttpServletRequest}
*
* @author xuxiaowei
* @see <a href=
* "https://stackoverflow.com/questions/10210645/http-servlet-request-lose-params-from-post-body-after-read-it-once/17129256#17129256">http-servlet-request-lose-params-from-post-body-after-read-it-once</a>
* @since 0.0.1
*/
public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* 用于缓存输入流
*/
private ByteArrayOutputStream cachedBytes;
public InputStreamHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (cachedBytes == null) {
// 首次获取流时,将流放入 缓存输入流 中
cacheInputStream();
}
// 从 缓存输入流 中获取流并返回
return new CachedServletInputStream(cachedBytes.toByteArray());
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/**
* 首次获取流时,将流放入 缓存输入流 中
*/
private void cacheInputStream() throws IOException {
// 缓存输入流以便多次读取。为了方便, 我使用 org.apache.commons IOUtils
cachedBytes = new ByteArrayOutputStream();
IOUtils.copy(super.getInputStream(), cachedBytes);
}
/**
* 读取缓存的请求正文的输入流
* <p>
* 用于根据 缓存输入流 创建一个可返回的 {@link ServletInputStream}
*/
public static class CachedServletInputStream extends ServletInputStream {
private final ByteArrayInputStream input;
public CachedServletInputStream(byte[] buf) {
// 从缓存的请求正文创建一个新的输入流
input = new ByteArrayInputStream(buf);
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return input.read();
}
}
}
使用
java
package cloud.xuxiaowei.core.filter;
import cloud.xuxiaowei.utils.servlet.InputStreamHttpServletRequestWrapper;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 请求流转换为多次读取的请求流 过滤器
* <p>
* 使用配置文件进行条件注解为 {@link Bean},并将优先级调高或指定请求地址单独配置
* <p>
* 此处将优先级调高,也可根据请求地址单独配置
*
* @author xuxiaowei
* @since 0.0.1
*/
@Slf4j
@Setter
@Component
public class HttpServletRequestInputStreamFilter extends HttpFilter implements Ordered {
public static final int ORDERED = Ordered.HIGHEST_PRECEDENCE + 10000;
private int order = ORDERED;
@Override
public int getOrder() {
return order;
}
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 将请求流转换为可多次读取的请求流
HttpServletRequest requestWrapper = new InputStreamHttpServletRequestWrapper(request);
chain.doFilter(requestWrapper, response);
}
}
java
package cloud.xuxiaowei.core.filter;
import cloud.xuxiaowei.utils.servlet.InputStreamHttpServletRequestWrapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* 请求流转换为多次读取的请求流 过滤器
* <p>
* 使用配置文件进行条件注解为 {@link Bean},并将优先级调高或指定请求地址单独配置
* <p>
* 此处将优先级调高,也可根据请求地址单独配置
*
* @author xuxiaowei
* @since 0.0.1
*/
@Slf4j
@Setter
@Component
public class HttpServletRequestInputStreamFilter extends HttpFilter implements Ordered {
public static final int ORDERED = Ordered.HIGHEST_PRECEDENCE + 10000;
private int order = ORDERED;
@Override
public int getOrder() {
return order;
}
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 将请求流转换为可多次读取的请求流
HttpServletRequest requestWrapper = new InputStreamHttpServletRequestWrapper(request);
chain.doFilter(requestWrapper, response);
}
}