我想要做的是,添加一个新的头到响应的请求被处理后。我需要检查处理HttpStatus
码(401未经授权在我的情况),并添加一个新的头。我知道春天有拦截器,但反应不能被修改为在document指出:
需要注意的是HandlerInterceptor接口的的postHandle方法并不总是非常适用于和@ResponseBody方法ResponseEntity使用。在这种情况下一个HttpMessageConverter写入并提交的postHandle被调用之前,这使得它不可能改变应答,例如增加一个报头的响应。相反,应用程序可以实现ResponseBodyAdvice,要么宣布它作为一个@ControllerAdvice Bean或直接RequestMappingHandlerAdapter配置。
好吧,我实现了ResponseBodyAdvice
。是的,它可以让修饰身材,但我不能设法修改标题,事件找不到状态码从控制器返回。
另一种选择,使用Servlet过滤器是不是也是成功的。我需要filterChain.doFilter(servletRequest, servletResponse);
呼叫后添加标题。但它再次不修改标头值。有没有办法来完成这个简单的任务?
这听起来像你在正确的轨道上一个Servlet过滤器上,你可能需要做的是包装servlet响应对象有一个当401个状态码已经设置检测并在那个时候将您的自定义标题:
HttpServletResponse wrappedResponse = new HttpServletResponseWrapper(response) {
public void setStatus(int code) {
super.setStatus(code);
if(code == 401) handle401();
}
// three similar methods for the other setStatus and the two
// versions of sendError
private void handle401() {
this.addHeader(...);
}
};
filterChain.doFilter(request, wrappedResponse);
那么,Java的说明你的HTTP响应,而您可以独立改变不同字段的对象。
但什么是服务器之间交换的实际和客户端是一个字节流,而头和身体之前发送。这就是为什么HttpResponse对象有isCommitted()
方法的原因:当头部已发送响应被提交。当然还有一旦承诺,就无法再添加修改头。和servlet容器可能犯,一旦足够的字符已被写入车身齐平的响应。
因此,试图改变头请求已被处理之后是不安全的。如果申请尚未提交它只能工作。它是安全的唯一情况是当控制器不写响应本身,只是转发到一个视图。然后在postHandle
拦截方法,响应尚未提交,您可以更改标题。否则,您必须测试isCommitted()
,如果返回true ...那是来不及改头!
在这种情况下,当然,更没有拦截也不是一个过滤器可以做什么...
如果检查状态码,不需要那么你可以加上preHandle方法的头文件(例如Spring提交的postHandle火灾前的反应,因此增加他们的postHandle不会从返回@ResponseBody响应工作标志着控制器的方法):
public class ControllerHandleInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
}
return true;
}
// other code...
}
您可以实现ServletFilter中,只是换了原来的响应对象。
这将允许您推迟响应的实际写作和添加自定义标题。
在另一方面:这看起来有点像Spring Security的处理链。
好吧,我实现了ResponseBodyAdvice。是的,它可以让修饰身材,但我不能设法修改标题,事件找不到状态码从控制器返回。
嗯,其实你可以,如果你投的是ServerHttpResponse
到ServletServerHttpResponse
。
(必须ServletServerHttpResponse
基础上,ResponseBodyAdvice
是怎么叫,你可以看到,ServerHttpResponse
传递给ResponseBodyAdvice
实际上是在这个ServletServerHttpResponse
的method)。
所以,简单地实现ResponseBodyAdvice
,没有必要再包裹HttpServletResponse
:
@ControllerAdvice
public class FooBodyAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if(response instanceof ServletServerHttpResponse) {
ServletServerHttpResponse res= (ServletServerHttpResponse)(response);
res.getServletResponse().getStatus(); //get the status code
res.getHeaders().set("fooHeader", "fooValue"); //modify headers
res.getHeaders().setETag("33a64df551425fcc55e4d42a148795d9f25f89d4") //use "type safe" methods to modify header
}
return body;
}
}