java Spring - 处理后修改每个请求的标头(在 postHandle 中)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30702970/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring - Modifying headers for every request after processing (in postHandle)
提问by mtyurt
What I want to do is, adding a new header to the response after the request is processed. I need to check the processed HttpStatus
code (401 unauthorized in my case) and add a new header. I know Spring has interceptors, but the response cannot be modified as stated in the document:
我想要做的是,在处理请求后向响应添加一个新标头。我需要检查处理后的HttpStatus
代码(在我的情况下为 401 未授权)并添加一个新的标头。我知道 Spring 有拦截器,但无法按照文档中的说明修改响应:
Note that the postHandle method of HandlerInterceptor is not always ideally suited for use with @ResponseBody and ResponseEntity methods. In such cases an HttpMessageConverter writes to and commits the response before postHandle is called which makes it impossible to change the response, for example to add a header. Instead an application can implement ResponseBodyAdvice and either declare it as an @ControllerAdvice bean or configure it directly on RequestMappingHandlerAdapter.
请注意,HandlerInterceptor 的 postHandle 方法并不总是非常适合与 @ResponseBody 和 ResponseEntity 方法一起使用。在这种情况下,HttpMessageConverter 在调用 postHandle 之前写入并提交响应,这使得无法更改响应,例如添加标头。相反,应用程序可以实现 ResponseBodyAdvice 并将其声明为 @ControllerAdvice bean 或直接在 RequestMappingHandlerAdapter 上配置它。
Well, I implemented the ResponseBodyAdvice
. Yes, it allows bodymodification, but I couldn't manage to modify the headers, event couldn't find the status code returned from the controller.
好吧,我实现了ResponseBodyAdvice
. 是的,它允许修改正文,但我无法修改标题,事件找不到从控制器返回的状态代码。
The other option, using servlet filters is not also successful. I need to add the header after filterChain.doFilter(servletRequest, servletResponse);
call. But it again doesn't modify the header value. Is there a way to accomplish this easy task?
另一个选项,使用 servlet 过滤器也不成功。我需要在filterChain.doFilter(servletRequest, servletResponse);
通话后添加标题。但它再次不会修改标头值。有没有办法完成这个简单的任务?
回答by Ian Roberts
It sounds like you're on the right track with a servlet filter, what you probably need to do is wrap the servlet response object with one that detects when a 401 status code has been set and adds your custom header at that time:
听起来您使用 servlet 过滤器走在正确的轨道上,您可能需要做的是用一个检测何时设置 401 状态代码的响应对象包装 servlet 响应对象,并在那时添加您的自定义标头:
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);
回答by Serge Ballesta
Well, Java shows you the HTTP response as an Object for which you can alter the different fields independently.
好吧,Java 将 HTTP 响应显示为一个对象,您可以独立更改不同的字段。
But what is actually exchanged between the server and the client is a byte stream, and headers and sent before the body. That is the reason why the HttpResponse has the isCommitted()
method : the response is committed when headers have been sent. And of course once it is committed, you can no longer add of modify headers. And the servlet container may commit and flush the response once enoughcharacters have been written to the body.
但实际上服务器和客户端之间交换的是一个字节流,以及headers和body之前发送的。这就是 HttpResponse 具有isCommitted()
方法的原因:在发送标头时提交响应。当然,一旦提交,您就不能再添加修改标头。一旦将足够的字符写入正文,servlet 容器就可以提交和刷新响应。
So trying to change headers is unsafe after the request have been processed. It could work only if request has not been committed. The only case where it is safe is when the controller does not write the response itself and just forwards to a view. Then in the postHandle
interceptor method, the response has not been committed, and you can change headers. Otherwise, you must test isCommitted()
, and if it returns true ... then it is too late to change headers !
因此,在处理请求后尝试更改标头是不安全的。只有在请求尚未提交时它才能工作。唯一安全的情况是控制器不写响应本身而只是转发到视图。那么在postHandle
拦截器方法中,响应还没有提交,就可以改headers了。否则,您必须测试isCommitted()
,如果它返回 true ...那么更改标题为时已晚!
Of course in that case, neither an interceptor nor a filter could do anything ...
当然在那种情况下,拦截器和过滤器都不能做任何事情......
回答by Zaur Guliyev
If checking status code is not required then you can just add those headers on preHandle method (as Spring commits response before postHandle fires, so adding them in postHandle will not work for response returned from @ResponseBody marked controller method):
如果不需要检查状态代码,那么您可以在 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...
}
回答by Ken Chan
Well, I implemented the ResponseBodyAdvice. Yes, it allows body modification, but I couldn't manage to modify the headers, event couldn't find the status code returned from the controller.
好吧,我实现了 ResponseBodyAdvice。是的,它允许修改正文,但我无法修改标题,事件找不到从控制器返回的状态代码。
Well, actually you can if you cast that ServerHttpResponse
to ServletServerHttpResponse
.
好吧,实际上你可以,如果你把它投射ServerHttpResponse
到ServletServerHttpResponse
.
(It must be ServletServerHttpResponse
based on the how the ResponseBodyAdvice
is called , you can see that ServerHttpResponse
passed to ResponseBodyAdvice
is actually an ServletServerHttpResponse
in this method).
(一定要ServletServerHttpResponse
根据ResponseBodyAdvice
被调用的方式,在这个方法中可以看出ServerHttpResponse
传递给ResponseBodyAdvice
实际上是一个)。ServletServerHttpResponse
So simply implement a ResponseBodyAdvice
and no need to wrap the HttpServletResponse
anymore :
因此,只需实现 a 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;
}
}
回答by cbeutenmueller
You can implement a ServletFilter and just wrap the original response object.
您可以实现一个 ServletFilter 并只包装原始响应对象。
This will allow you to defer the actual writing of the response and add your custom headers.
这将允许您推迟响应的实际写入并添加您的自定义标头。
On the other hand: This looks a bit like the Spring Security Processing chain.
另一方面:这看起来有点像 Spring Security Processing 链。