Java Spring-在处理后修改每个请求的头(在postHandle中)
我想做的是,在处理请求后向响应添加一个新的头。我需要检查已处理的Java Spring-在处理后修改每个请求的头(在postHandle中),java,spring,spring-mvc,servlets,Java,Spring,Spring Mvc,Servlets,我想做的是,在处理请求后向响应添加一个新的头。我需要检查已处理的HttpStatus代码(在我的例子中是401 unauthorized),并添加一个新的头。我知道Spring有拦截器,但响应不能按照以下说明进行修改: 请注意,HandlerInterceptor的postHandle方法并不总是非常适合与@ResponseBody和ResponseEntity方法一起使用。在这种情况下,HttpMessageConverter在调用postHandle之前写入并提交响应,这使得无法更改响应,例
HttpStatus
代码(在我的例子中是401 unauthorized),并添加一个新的头。我知道Spring有拦截器,但响应不能按照以下说明进行修改:
请注意,HandlerInterceptor的postHandle方法并不总是非常适合与@ResponseBody和ResponseEntity方法一起使用。在这种情况下,HttpMessageConverter在调用postHandle之前写入并提交响应,这使得无法更改响应,例如添加标头。相反,应用程序可以实现ResponseBodyAdvice并将其声明为@ControllerAdvice bean,或者直接在RequestMappingHandlerAdapter上配置它
嗯,我实现了ResponseBodyAdvice
。是的,它允许修改body,但我无法修改标题,事件无法找到从控制器返回的状态代码
另一个选项,使用servlet过滤器也不成功。我需要在filterChain.doFilter(servletRequest,servletResponse)之后添加头代码>呼叫。但它同样不会修改标题值。有没有办法完成这个简单的任务?您可以实现一个ServletFilter,只需包装原始响应对象 这将允许您推迟响应的实际写入,并添加自定义标题
另一方面:这看起来有点像Spring安全处理链。听起来使用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
interceptor方法中,响应尚未提交,您可以更改头。否则,必须测试isCommitted()
,如果返回true。。。那么现在更改标题就太晚了
当然,在这种情况下,拦截器和过滤器都不能做任何事情…如果不需要检查状态代码,那么您可以在预处理方法上添加这些头(因为Spring在postHandle激发之前提交响应,所以在postHandle中添加它们对于@ResponseBy标记的控制器方法返回的响应不起作用): 嗯,我执行了建议。是的,它允许你的身体 修改,但我无法修改标题,事件 找不到从控制器返回的状态代码 实际上,如果您将
ServerHttpResponse
转换为ServletServerHttpResponse
,您就可以了
(必须是ServletServerHttpResponse
根据ResponseByAdvice
的调用方式,您可以看到传递给ResponseByAdvice
的ServerHttpResponse
实际上是ServletServerHttpResponse
)
因此,只需实现一个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;
}
}
你说的是servlet过滤器。您在链中的何处添加了过滤器?你能不能快速检查一下对你有用?我试过了。我将在filterChain.doChain()之后添加标题,因为我需要链完成后的状态代码信息。它不起作用,Spring提交响应并且不允许在过滤器中进行修改。这与Spring无关,但是纯java一旦响应已经(部分)发送到客户端,就不能再修改头信息。你只能在那之前改变它。但我想你是自己还401,那么为什么不干脆在那个地方还呢?它在不同的地方还。文档声明修改标题不可能在postHandle中,而是在其他地方,我正在寻找那个地方以及如何做到这一点。在
preHandle
中。一旦代码发送完毕,就再也无法对其进行任何处理。代码库中到处都是这种味道,不是有点难闻吗?难道不应该在单个位置执行安全性操作吗?您有包装响应对象的示例吗?正如Ian Roberts在下面所述:您还必须覆盖sendError
方法和附加(不推荐的)setStatus
消息。只是为了确保捕获所有案例。@M.Denium是的,正如我在代码中的评论中所指出的那样。这些方法的实现留给读者作为练习…谢谢你的回答,这解决了我在alfresco Stacks上的问题。如果他们不调用setStatus()怎么办?因为当你用对象响应时,默认设置是200?
@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;
}
}