需求背景
系统需要对请求体内容进行校验计算是否合法,因此准备通过拦截器 Interceptor 来进行统一处理。
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String param = new String(request.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
// 进行校验处理
return check(param);
}
}
由于该拦截器读取了一次 inputStream, 导致 Spring框架在赋值给接口 @RequestBody 参数时无法再获取到 inputStream 导致程序出现错误:Required request body is missing
。
解决方法
- 扩写ServletInputStream
public class RequestBodyInputStreamWrapper extends ServletInputStream {
private ByteArrayInputStream bais;
private ServletInputStream wrapped;
public RequestBodyInputStreamWrapper(ServletInputStream wrapped) {
this.wrapped = wrapped;
}
// 缓存内容用于后期读取
public byte[] resetReading(byte[] bytes) {
this.bais = new ByteArrayInputStream(bytes);
return bytes;
}
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public boolean isFinished() {
return wrapped.isFinished();
}
@Override
public boolean isReady() {
return wrapped.isReady();
}
@Override
public void setReadListener(ReadListener readListener) {
wrapped.setReadListener(readListener);
}
}
- 扩写请求类 ContentCachingRequestWrapper
public class RequestBodyContentWrapper extends ContentCachingRequestWrapper {
private RequestBodyInputStreamWrapper bodyProcessorIS;
public RequestBodyContentWrapper(ContentCachingRequestWrapper wrapped) throws IOException {
super(wrapped);
this.bodyProcessorIS = new RequestBodyInputStreamWrapper(wrapped.getInputStream());
}
@Override
public ServletInputStream getInputStream() {
return bodyProcessorIS;
}
public byte[] prepareInputStream() throws IOException {
return bodyProcessorIS.resetReading(super.getInputStream().readAllBytes());
}
}
- 过滤器中将 HttpServletRequest 替换为包装后的类 RequestBodyContentWrapper
public class RequestBodyWrapperFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(@Nonnull HttpServletRequest request, @Nonnull HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
ContentCachingRequestWrapper cacheRequest = new ContentCachingRequestWrapper(request);
RequestBodyContentWrapper readableRequest = new RequestBodyContentWrapper(cacheRequest);
// 调用该方法来缓存请求体的内容
readableRequest.prepareInputStream();
filterChain.doFilter(readableRequest, response);
}
}
- 拦截器中获取请求体内容
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
RequestBodyContentWrapper request1 = (RequestBodyContentWrapper) request;
String param = new String(request1.getContentAsByteArray());
// 进行校验处理
return check(param);
}
}
参考资料