背景: request.getInputStream()获取请求body里面的内容只能被获取一次,ContentCachingRequestWrapper通过这个类能够解决解决HttpServletRequest inputStream只能读取一次的问题,但是这个类有缺陷(前提必须是doFilter之前不能使用request.getInputStream()方法)
使用场景:
全局异常捕获类中打印异常POST请求的参数。
步骤一: 创建Filter
public class CachingContentFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
if (request.getContentType() != null && !request.getContentType().contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
//测试
String requestBody = getRequestBody(wrapper);
System.out.println("repeat:" + requestBody);
chain.doFilter(wrapper, response);
return;
}
}
chain.doFilter(request, response);
}
private String getRequestBody(HttpServletRequest request) {
String requestBody = "";
ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (wrapper != null) {
try {
requestBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
} catch (IOException e) {
}
}
return requestBody;
}
}
步骤2: 在全局异常捕获类中使用
@RestControllerAdvice
public class ServiceExceptionHandler {
/** logger */
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceExceptionHandler.class);
/**
* 捕获ServiceException
*
* @param e
* @return
*/
@ExceptionHandler({com.cl.exam.core.exception.ServiceException.class})
@ResponseStatus(HttpStatus.OK)
public ApiRest serviceExceptionHandler(HttpServletRequest req, ServiceException e) {
LOGGER.error("服务异常!",e);
printLog(req)
return new ApiRest(e);
}
private void printLog(HttpServletRequest req) {
LOGGER.error("RequestInfo:uri={}, method={}, params={}, body={}",
req.getRequestURI(),
req.getMethod(),
req.getParameterMap(),
getRequestBody(req));
}
private String getRequestBody(HttpServletRequest request) {
String requestBody = "";
ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (wrapper != null) {
try {
requestBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
} catch (IOException e) {
}
}
return requestBody;
}
}
注意事项
该类的使用必须在执行到了Controller层,才能通过缓存获取请求体,通过查看源码实现可以发现,实际缓存请求内容的步骤是在读取request.inputStrem的时候进行的缓存(ContentCachingInputStream的read方法中)。
所以只有请求被实际消费了,才能通过ContentCachingRequestWrapper的getContentAsByteArray方法获取到请求体,否则是空的。
解决办法:
1、重写ContentCachingRequestWrapper,增加手动缓存方法。不推荐
2、对Controller层进行aop
参考文章:
https://blog.youkuaiyun.com/u013887008/article/details/128338085