如何在handler执行后在@responsebody生效前修改返回值
2018-03-08 19:27
323 查看
0 前言
Interceptor作为springmvc的一个组件,可以在调用handler之前和之后处理其他事情。不同于Filter的是,Interceptor能够感知handler。但是该组件有一个坑你可能没有注意过,那就是Interceptor的postHandle不能修改@ResponseBody和ResponseEntity方法的响应。我们先介绍Interceptor组件和配置方法,最后再来解释这个坑。1 Interceptor
preHandle(..) — 在相应的handler执行之前调用;如果return false,则不会再执行postHandler,甚至连handler也不会执行postHandle(..) — 在相应的handler执行之后调用
afterCompletion(..) — 在请求完成之后调用
2 Interceptor配置(笔者使用springboot)
Controller@Controller public class InterceptorController { @GetMapping("index") public String hello() { System.out.println("handler..."); return "hello"; } }
Interceptor
public class HelloInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { httpServletResponse.setHeader("username", "Json"); System.out.println("preHandle .... "); return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { httpServletResponse.setHeader("username", "Marry"); System.out.println("postHandle ...."); } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { httpServletResponse.setHeader("username", "Mike"); System.out.println("afterCompletion .... "); } }
Configuration
@Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new HelloInterceptor()) .addPathPatterns("/index"); } }
request(笔者使用 restlet client 插件发送请求)
http://localhost:8080/index
response
username: Marry Content-Type: text/html;charset=UTF-8 Content-Language: zh-CN Transfer-Encoding: chunked Date: Thu, 08 Mar 2018 10:22:59 GMT
console
preHandle .... handler... postHandle .... afterCompletion ....
2.1 实验小结
观察控制台(console)的信息发现执行顺序确实按我们预想。不过如果你将preHandle方法返回false那么,之后的都不会被执行,你将只看到 preHandle …. 信息其次观察response,发现username为Marry,是postHandle方法设置的,而afterCompletion方法设置的Mike并没有效果。那是因为该方法是请求结束之后执行的,也就是说响应已经被发送了。注意该情况是发生在handler返回的是视图,那如果返回的是json呢?
3 修改handler返回json数据
只需要修改Controller,其他不变@RestController public class InterceptorController { @GetMapping("/hello1") public String hello() { return "hello"; } @GetMapping("index") public String hello2() { System.out.println("handler..."); return "hello"; } }
response
username: Json Content-Type: text/plain;charset=UTF-8 Content-Length: 5 bytes Date: Thu, 08 Mar 2018 10:42:44 GMT
是否发现postHandle方法也失效了。原因是 @ResponseBody注释 或者返回 ResponseEntity的方法在先于postHandle方法之前将响应提交给HandlerAdapter(调用handler和Interceptor方法者),所以之后的修改就无效了。那如果有这样的确实有这样的需求呢?比如对响应结果加密等。
其实ResponseBody将object转json字符串是由相应的HttpMessageConverter的write()方法负责的,你可以考虑三种方式:
(1) aop在这个HttpMessageConverter的write前加逻辑
(2) aop替换相关Controller方法的返回值
(3) @ControllerAdvice + ResponseBodyAdvice
对于spring开发,AOP应该是比较熟悉的。那么我们来实现方法3。添加下面这个类就行了。
@ControllerAdvice public class MyResponseBody implements ResponseBodyAdvice{ // 这里直接返回true,表示对任何handler的responsebody都调用beforeBodyWrite方法 @Override public boolean supports(MethodParameter methodParameter, Class aClass) { return true; } // 修改responsebody @Override public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { return "changed body"; } }
response
username: Json Content-Type: text/plain;charset=UTF-8 Content-Length: 12 bytes Date: Thu, 08 Mar 2018 11:07:37 GMT body changed body
3.1小结
注意,上述方法其实也不能修改response header,只能修改body。至于如何修改,请大家告诉我。相关文章推荐
- 利用ResponseBodyAdvice修改返回值
- 如何实现@ResponseBody,把Json字符串转换为指定类型
- 修改完linux bashrc文件之后,如何不重启系统使其生效
- 如何使/etc/profile中修改的配置立刻生效?
- 如何修改可执行文件的图标
- 修改Internet用户密码后,如何使之立即生效
- SpringMVC中使用@ResponseBody注解返回值,Ajax取得中文乱码解决方法
- 『架构』C#间接继承:如何修改 继承函数的 返回值类型
- Android游戏开发:如何利用Thread与Handler执行多线程的方法
- 如何解决httpResponse = httpClient.execute(httpGet);无法执行的问题?
- Windows和Linux下,如何在不关闭浏览器的方式下快速生效hosts修改
- 在try...catch语句中执行Response.End()后如何停止执行catch语句中的内容
- Mysql 修改数据库 User 表 如何立即生效
- vbs 批量修改文件,bat 批处理文件调用执行vbs,并在cmd窗口打印返回值(vbs运行结果)
- Web API-如何将Controller的返回值转换成HTTP response消息
- SSM框架——SpringMVC中使用@ResponseBody注解返回值,Ajax取得中文乱码解决方法
- 如何使profile修改后生效
- Exchange 2016邮箱配额调整后的生效时间如何修改?
- 查看Oracle执行计划的几种方法 / oracle中DateTime类型的字段,建立索引后,查寻时索引如何生效?/ oracle 中sql语句怎么加多个强制索引
- 通过Java反射在运行时修改TimerTask的执行周期并且立即生效