文章内容
在实际项目中,我们常常需要在请求前后进行一些操作,比如:参数解密/返回结果加密,打印请求参数和返回结果的日志等。这些与业务无关的东西,我们不希望写在controller方法中,造成代码重复可读性变差。这里,我们讲讲使用 @ControllerAdvice 和 RequestBodyAdvice、 RequestBodyAdvice 来对请求前后进行处理(本质上就是AOP),来实现日志记录每一个请求的参数和返回结果。
1、请求前处理
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | @Slf4j @ControllerAdvice public class LogRequestBodyAdvice implements RequestBodyAdvice { @Override public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { return true ; } @Override public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException { return httpInputMessage; } @Override public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { Method method=methodParameter.getMethod(); log.info( "{}.{}:{}" ,method.getDeclaringClass().getSimpleName(),method.getName(),JSON.toJSONString(o)); return o; } @Override public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { Method method=methodParameter.getMethod(); log.info( "{}.{}" ,method.getDeclaringClass().getSimpleName(),method.getName()); return o; } } |
RequestBodyAdvice 针对所有以 @RequestBody 的参数,在读取请求body之前或者在body转换成对象之前可以做相应的增强。我们处理了有参数和没有参数的情况,打印出请求类、方法、请求参数。注意:这里要加上 @ControllerAdvice或 @RestControllerAdvice请求才能增强。
2、请求后处理
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | @Slf4j @ControllerAdvice public class LogResponseBodyAdvice implements ResponseBodyAdvice { @Override public boolean supports(MethodParameter methodParameter, Class aClass) { return true ; } @Override public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { Method method=methodParameter.getMethod(); String url=serverHttpRequest.getURI().toASCIIString(); log.info( "{}.{},url={},result={}" ,method.getDeclaringClass().getSimpleName(),method.getName(),url,JSON.toJSONString(o)); return o; } } |
ResponseBodyAdvice 用于响应体写出之前做一些处理。这里我们记录了请求类、方法、链接、请求结果。同 RequestBodyAdvice 一样,也需要加上@ControllerAdvice 或@RestControllerAdvice 进行请求增强。
3、编写请求代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | @RestController @Slf4j @RequestMapping ( "/lm" ) public class HelloController { @RequestMapping ( "/hello1" ) public String hello( @RequestBody JSONObject jsonObject){ return "hello 1:" +jsonObject.getString( "name" ); } @RequestMapping ( "/hello2" ) public String hello( @RequestParam ( "name" ) String name){ return "hello 2:" +name; } } |
可见,我们记录请求参数和返回结果的代码都不用写在controller中了。
4、发起请求
访问hello1接口,日志如下:
1 2 | 2018-11-26 20:36:11.599 INFO 75364 HelloController.hello:{"name":"ggg"} 2018-11-26 20:36:11.611 INFO 75364 HelloController.hello,url=http://localhost:8501/lm/hello1,result="hello 1:ggg" |
访问hello2接口,日志如下:
1 | 2018-11-26 20:37:55.643 INFO 75364 HelloController.hello,url=http://localhost:8501/lm/hello2?name=vvv,result="hello 2:vvv" |
可以看到,hello2接口因为参数没有用 @RequestBody 绑定,只打印了ResponseBodyAdvice里的日志,没有进入RequestBodyAdvice。
总结
使用@ControllerAdvice注解+ResponseBodyAdvice+ResponseBodyAdvice,可以对请求的输入输出进行处理,避免了在controller中对业务代码侵入。
Pingback: SpringBoot接口参数加密解密 – 梓潼