Spring - 处理后修改每个请求的标头(在postHandle中)

时间:2015-06-08 07:02:56

标签: java spring spring-mvc servlets

我想要做的是,在处理请求后为响应添加新标头。我需要检查已处理的HttpStatus代码(在我的情况下是401未授权的)并添加新的标头。我知道Spring有拦截器,但无法按document

中的说明修改响应
  

请注意,HandlerInterceptor的postHandle方法并不总是非常适合与@ResponseBody和ResponseEntity方法一起使用。在这种情况下,HttpMessageConverter在调用postHandle之前写入并提交响应,这使得无法更改响应,例如添加标头。相反,应用程序可以实现ResponseBodyAdvice并将其声明为@ControllerAdvice bean或直接在RequestMappingHandlerAdapter上配置它。

好吧,我实施了ResponseBodyAdvice。是的,它允许正文修改,但我无法修改标题,事件无法找到从控制器返回的状态代码。

使用servlet过滤器的另一个选项也不成功。我需要在filterChain.doFilter(servletRequest, servletResponse);调用后添加标头。但它再次没有修改标头值。有没有办法完成这个简单的任务?

5 个答案:

答案 0 :(得分:9)

听起来你正在使用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);

答案 1 :(得分:3)

那么,Java会将HTTP响应显示为一个Object,您可以独立地更改不同的字段。

但是,服务器和客户端之间实际交换的是字节流和标题,并在正文之前发送。这就是为什么HttpResponse具有isCommitted()方法的原因:在发送标头时提交响应。当然,一旦提交,您就不能再添加修改标头。一旦将足够的字符写入正文,servlet容器就可以提交并刷新响应。

因此,在处理完请求后,尝试更改标头是不安全的。它只有在未提交请求时才有效。安全的唯一情况是控制器本身不写响应并且只转发到视图。然后在postHandle拦截器方法中,响应尚未提交,您可以更改标头。否则,您必须测试isCommitted(),如果它返回true,则更改标题为时已晚!

当然,在这种情况下,拦截器和过滤器都无法做任何事情......

答案 2 :(得分:1)

如果不需要检查状态代码,那么你可以在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...
}

答案 3 :(得分:0)

您可以实现ServletFilter并只包装原始响应对象。

这将允许您推迟实际写回复并添加自定义标题。

另一方面:这看起来有点像Spring Security Processing链。

答案 4 :(得分:0)

  

好吧,我实现了ResponseBodyAdvice。是的,它允许身体   修改,但我无法修改标题,事件   找不到从控制器返回的状态代码。

好吧,实际上,如果您将ServerHttpResponse转换为ServletServerHttpResponse,则可以。

(根据ServletServerHttpResponse的调用方式,它必须为ResponseBodyAdvice,您可以看到传递给ServerHttpResponse的{​​{1}}实际上是{{1 }}在此method中。

因此只需实现一个ResponseBodyAdvice,而无需再包装ServletServerHttpResponse

ResponseBodyAdvice