最近在改造一个老项目的时候遇到了一个问题
1.项目中使用了@Validated注解 对接口入参进行校验
2.项目中有全局异常处理, 能捕获参数校验的异常, 但看不到POST请求的入参
这里就有些问题了
1.既然已经知道 某个字段传参有问题, 是否还需要打印参数?
其实也是看场景, 比如接口对接, 报错查问题还是很有价值的
2.增加了参数打印的逻辑会不会影响性能?
多数项目并不会有很大的并发, 而且完全可以针对环境, 接口, 业务做区分(比如: 开发环境方便联调就打印参数, 线上稳定了就关闭)
3.是否每一个接口都需要打印入参?
可以在 全局异常处理时 打印入参; 换句话说就是 有异常报错的时后才打印
ok, 在确定了 要做打印请求参数这件事后, 就需要确定用什么方案来做了
参考简单测试的结果: Filter, Interceptor, Validator, Aop 执行顺序 Filter, Interceptor, Validator, Aop 执行顺序_zx1323的博客-优快云博客
基本上可知, 用filter是一个不错的选择
Filter在读取POST请求时, 不像GET请求 使用 getParameterMap()来获取入参, POST请求中的body参数是以流形式存在的
在尽量 使用spring原生类的 前提下, 有了如下尝试(原理都是一样的)
方式一 新写一个过滤器 继承AbstractRequestLoggingFilter: 【小家Spring】从OncePerRequestFilter的源码解读去了解Spring内置的Filter的特别之处以及常见过滤器使用介绍_YourBatman的博客-优快云博客
优点: 少量代码即可打印参数, 不受到验证框架影响; 参考上面链接中大佬的现成代码, GET POST请求等参数打印全都搞定
缺点: 没有response的实现, 默认只有GET方法;
方式二 新写一个过滤器, 搭配ContentCachingRequestWrapper
RequestWrapper requestWrapper = new RequestWrapper(request);// 执行doFilter方法前, 把HttpServletRequest包一下, 再传递下去
filterChain.doFilter(requestWrapper, servletResponse);//执行
取值不能用getInputStream(), 应该参考AbstractRequestLoggingFilter类的getMessagePayload(HttpServletRequest request)方法,
如: new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8));
在这个filter之前不能有别的filter调用过getInputStream()方法;
优点: spring已经实现了对 body数据流的拷贝
缺点: 并不支持打印所有参数, 查看isFormPost()方法可知, 只支持POST请求和内容类型应为application/x-www-form-urlencoded, get请求的需要自行实现
方式三 基于方式二的自定义一个Wrapper类, 该方式灵活度更高, 比如能在执行doFilter方法前打印参数
最后, POST请求的流已经复制, 那么完全可以在 全局异常处理的时候再打印入参了
System.out.println(requestWrapper.getBodyString()); // 自定义的RequestWrapper类, 打印入参