春天 - 处理(的postHandle)修改后为每个请求头

问题描述 投票:7回答:5

我想要做的是,添加一个新的头到响应的请求被处理后。我需要检查处理HttpStatus码(401未经授权在我的情况),并添加一个新的头。我知道春天有拦截器,但反应不能被修改为在document指出:

需要注意的是HandlerInterceptor接口的的postHandle方法并不总是非常适用于和@ResponseBody方法ResponseEntity使用。在这种情况下一个HttpMessageConverter写入并提交的postHandle被调用之前,这使得它不可能改变应答,例如增加一个报头的响应。相反,应用程序可以实现ResponseBodyAdvice,要么宣布它作为一个@ControllerAdvice Bean或直接RequestMappingHandlerAdapter配置。

好吧,我实现了ResponseBodyAdvice。是的,它可以让修饰身材,但我不能设法修改标题,事件找不到状态码从控制器返回。

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

java spring spring-mvc servlets
5个回答
10
投票

这听起来像你在正确的轨道上一个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);

3
投票

那么,Java的说明你的HTTP响应,而您可以独立改变不同字段的对象。

但什么是服务器之间交换的实际和客户端是一个字节流,而头和身体之前发送。这就是为什么HttpResponse对象有isCommitted()方法的原因:当头部已发送响应被提交。当然还有一旦承诺,就无法再添加修改头。和servlet容器可能犯,一旦足够的字符已被写入车身齐平的响应。

因此,试图改变头请求已被处理之后是不安全的。如果申请尚未提交它只能工作。它是安全的唯一情况是当控制器不写响应本身,只是转发到一个视图。然后在postHandle拦截方法,响应尚未提交,您可以更改标题。否则,您必须测试isCommitted(),如果返回true ...那是来不及改头!

在这种情况下,当然,更没有拦截也不是一个过滤器可以做什么...


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...
}

0
投票

您可以实现ServletFilter中,只是换了原来的响应对象。

这将允许您推迟响应的实际写作和添加自定义标题。

在另一方面:这看起来有点像Spring Security的处理链。


0
投票

好吧,我实现了ResponseBodyAdvice。是的,它可以让修饰身材,但我不能设法修改标题,事件找不到状态码从控制器返回。

嗯,其实你可以,如果你投的是ServerHttpResponseServletServerHttpResponse

(必须ServletServerHttpResponse基础上,ResponseBodyAdvice是怎么叫,你可以看到,ServerHttpResponse传递给ResponseBodyAdvice实际上是在这个ServletServerHttpResponsemethod)。

所以,简单地实现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;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.