您的位置:首页 > 编程语言 > Java开发

如何在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。至于如何修改,请大家告诉我。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐