多次读取 HTTP 请求体
概述
在项目中可能会出现多次读取 http 请求体的需求,但是多次读取会报错。原因是读取 http 请求体的操作,最终都要调用 HttpServletRequest
的 getInputStream()
方法和 getReader()
方法,而这两个方法总共只能被调用一次,第二次调用就会报错,这种情况下我们可以通过自定义过滤器来实现该需求。
功能实现
定义一个类,将 HttpServletRequest
的字节流的数据,保存到类的一个变量中,重写 getInputStream()
方法和 getReader()
方法,每次需要读取数据的时候都从变量中获取,返回给调用者。
过滤器包装类
package com.xl.multirequestbody.filter;
import cn.hutool.core.io.IoUtil;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
private final String charSet = "UTF-8";
public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException {
super(request);
request.setCharacterEncoding(charSet);
response.setCharacterEncoding(charSet);
body = IoUtil.readBytes(request.getInputStream(), false);
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int available() throws IOException {
return body.length;
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
自定义过滤器
package com.xl.multirequestbody.filter;
import cn.hutool.core.util.StrUtil;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import java.io.IOException;
public class RepeatableFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest && StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
}
if (null == requestWrapper) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void destroy() {
}
}
注册到Spring中
package com.xl.multirequestbody.config;
import com.xl.multirequestbody.filter.RepeatableFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@SuppressWarnings({"rawtypes", "unchecked"})
@Bean
public FilterRegistrationBean repeatableFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new RepeatableFilter());
registration.addUrlPatterns("/*");
registration.setName("repeatableFilter");
registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
return registration;
}
}