用SpringMVC时,使用ExceptionHandler去做Controller层的统一异常处理。
使用ExceptionHandler注解的异常处理方法可以使用很灵活的方法签名。
如何在异常发生时输出请求
发生异常时,不仅仅需要输出异常本身,经常还需要根据Request的具体内容来分析、排查问题。
比如HttpRequestMethodNotSupportedException、HttpMessageConversionException等等,这些异常发生在业务代码处理之前,业务代码是无法获取到request的数据的,发生异常时如果能够看到请求body的具体内容,那么处理起来就可以对症下药,事半功倍。
说起来简单,做起来却不是很顺当,虽然ExcelptionHandler中可以传入ServerletRequest作为入参,但是ServerletRequest的inputStream只能被读取一次,发生异常的时候再想去读取body只能悲催的得到一个已经Closed的Stream。
使用ContentCachingRequestWrapper
1,通过过滤器将ServerletRequest封装成ContentCachingRequestWrapper,body被读取后,会被它缓存。
@Component
public class RequestWrapperFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(new ContentCachingRequestWrapper(httpServletRequest), httpServletResponse);
}
}
2,ExceptionHandler传入ServletRequest,此时的ServletRequest就是ContentCachingRequestWrapper,输出即可
@ExceptionHandler(value = Exception.class)
public ResponseEntity<Response> returnMediaTypeNotSupportError(Exception ex, ServletRequest request) {
if (request != null && request instanceof ContentCachingRequestWrapper) {
ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
logger.error("request_body:{}", StringUtils.toEncodedString(wrapper.getContentAsByteArray(), Charset.forName(wrapper.getCharacterEncoding())));
}
}