SpringMVC requestBody和responseBody重复获取
最近有个需求,要根据响应结果来判断,是否需要保存requestBody的内容到数据库。
想法使用拦截器HandlerInterceptor,拦截需要处理的url,根据响应结果判断是否需要保存内容到数据库。
遇到问题
- requestBody不能重复获取问题
- responseBody 在HandlerInterceptor afterCompletion无法获取到内容
其实际都是因为requestBody、responseBody 对应只能读取一次的问题,这里要解决的是,让requestBody、responseBody 可以重复读取。
解决思路
查资料发现 Spring提供了ContentCachingRequestWrapper、ContentCachingResponseWrapper 实现重复读取body的功能。
只需配置了一个过滤器,对需要处理的url,做requestServlet、response的封装,然后在HandlerInterceptor就可以读取到body的内容了。
@WebFilter(filterName = "httpServletFilter",urlPatterns = "/")
public class HttpServletFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest requestToCache = new ContentCachingRequestWrapper((HttpServletRequest) servletRequest);
HttpServletResponse responseToCache = new ContentCachingResponseWrapper((HttpServletResponse) servletResponse);
filterChain.doFilter(requestToCache, responseToCache);
}
@Override
public void destroy() {
}
}
备注,这里过滤器的使用,踩了个小坑,同时使用了@Component、@WebFilter 2个注解,实际是实例化了2个,@Component 是对所有url生成 ,@WebFilter是对配置里的url生效。 使用@WebFilter + @ServletComponentScan(放在启动类上) ,就只对@WebFilter是对配置里的url生效。
处理业务
拦截里有2个方法,用获取request、response的body内容,主要依赖其包装类的getContentAsByteArray()方法,获取字节数组,转化实际的内容,我这里是需要Json字符串,所以转成String就可以了。
public class BusinessHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String responseBody=this.getResponseData(response);
//todo 做业务判断是否需要保存requestBody
String requestBody=this.getRequestData(request);
//todo 保存数据到数据库
}
private String getRequestData(final HttpServletRequest request) throws UnsupportedEncodingException {
String payload = null;
ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (wrapper != null) {
byte[] buf = wrapper.getContentAsByteArray();
if (buf.length > 0) {
payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
}
}
return payload;
}
private String getResponseData(final HttpServletResponse response) throws UnsupportedEncodingException, IOException {
String payload = null;
ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
if (wrapper != null) {
byte[] buf = wrapper.getContentAsByteArray();
if (buf.length > 0) {
payload = new String(buf, 0, buf.length, StandardCharsets.UTF_8.name());
}
//将内容复制回响应体
wrapper.copyBodyToResponse(); // in other case you send null to the response
}
return payload;
}
}
参考 https://stackoverflow.com/questions/26380214/java-spring-get-body-of-post-request