春天启动2 - OncePerRequestFilter - 修改响应头控制器处理后

问题描述 投票:0回答:3

你好,我想我已经完成了处理(计算机执行逻辑)后,并用HTTP状态代码总结修改我的一些API的响应头。

例如,如果响应为404,则包括具体例如Cache-Control头例如不缓存,或者类似的东西。

我已经2个OncePerRequestFilter注册,其做工精细 - 但显然我不能这样做的逻辑 - 一旦处理完成。该CacheControlFilter已经有逻辑,默认情况下增加了一些Cache-Control头 - 比如缓存,持续15秒等,但似乎出现这种情况的调度非常早期的阶段(在响应除了头),当它到达的阶段的执行实际Controller/Endpoint并且有一个例外,或者明明是要通过咨询等进行处理错误,我不能mutate那些已经被过滤器添加了这些已经存在的包头中。


  @Bean
  public FilterRegistrationBean filterOne() {
    Filter filter = new FilterOne();
    return createFilter(filter, "FilterOne",List.of("/*"));
  }

  @Bean
  public FilterRegistrationBean cacheControlFilter() {
    Filter filter = new CacheControlFilter();
    return createFilter(filter, "CacheControlFilter", List.of("/*"));
  }

  private FilterRegistrationBean createFilter(Filter aFilter, String filterName,
      List<String> urlPatterns) {
    FilterRegistrationBean filterRegBean = new FilterRegistrationBean(aFilter);
    filterRegBean.addUrlPatterns(urlPatterns.toArray(new String[0]));
    filterRegBean.setName(filterName);
    filterRegBean.setEnabled(true);
    filterRegBean.setAsyncSupported(true);
    return filterRegBean;
  }

我已经试过了,对这些岗位HttpServletResponseWrapperherehere所示添加一个CacheControlFilter,但似乎并没有工作。我也看到了类似的S.O线程here

HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper(response) {
   @Override
   public void setStatus(int sc) {
      super.setStatus(sc);
      handleStatus(sc);
  }

 @Override
 @SuppressWarnings("deprecation")
 public void setStatus(int sc, String sm) {
     super.setStatus(sc, sm);
     handleStatus(sc);
 }

 @Override
 public void sendError(int sc, String msg) throws IOException {
     super.sendError(sc, msg);
     handleStatus(sc);
  }

 @Override
 public void sendError(int sc) throws IOException {
     super.sendError(sc);
     handleStatus(sc);
 }

 private void handleStatus(int code) {
    if(code == 404)
         addHeader("Cache-Control, "xxx");
 }

};

但代码根本没有执行!所以,我想只有经过操纵的Cache-Control头在第二过滤器虽然已处理完毕,我准备返回响应。

我不知道,如果是,我也有,做在错误的一些清理和设置的响应 - 混合的东西了!

@ControllerAdvice
@Slf4j
public class GlobalErrorHandler

更新:作为一个说明,当我的控制器被抛出异常或错误,上述GlobalErrorHandler被调用,我有一个执行特殊处理,返回一个error response。我看到的是,虽然magically响应早已被过滤器(CacheControlFilter)填充default头。所以它最终是一个有点怪异,我添加额外的逻辑,改变控制头和我结束了与具有相同的头2次(1响应与我试图通过CacheControlFilter设定的值,那么任何特殊价值覆盖在ControllerAdvice

任何提示或帮助表示赞赏感谢!我使用Spring引导2.1.2Undertow作为我的基本的servlet容器。

spring spring-boot spring-mvc servlet-filters undertow
3个回答
1
投票

你提到的link说,不能获取状态代码或修改标题中ResponseBodyAdvice是不正确的。如果你投ServerHttpResponseServletServerHttpResponse,你可以做他们两个。所以,简单地实现ResponseBodyAdvice

@ControllerAdvice
public class CacheControlBodyAdvice 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);
                if(res.getServletResponse().getStatus() == 400){
                    res.getServletResponse().setHeader("Cache-Control", "XXXXX");
                }           
        }
        return body;
    }
}

还有一点需要注意的是,如果你的控制器方法抛出正常完成前一个例外,这取决于如何处理异常,ResponseBodyAdvice可能不会触发。所以,我建议落实在GlobalErrorHandler安全警卫相同的逻辑:

@ControllerAdvice
public class GlobalErrorHandler{

   @ExceptionHandler(value = Exception.class)
   public void handle(HttpServletRequest request, HttpServletResponse response) {

        if(response.getStatus() == 400){
           response.setHeader("Cache-Control", "XXXXX");
        }       
    }
}

1
投票

有很多弹回至客户端之前改变HttpServletResponse和注入响应到处理方法或利用ControllerAdvice是有效的解决方案的方式一打。不过,我不明白你的问题的基本前提是过滤器不能做的工作:

我已经2 OncePerRequestFilter注册,其做工精细 - 但显然我不能这样做的逻辑 - 一旦处理完成。

至于修改HttpServletResponse而言,Filters为我工作完全正常,且至少适合作为这项工作的任何其他工具:

@Bean
public FilterRegistrationBean createFilter() {
    Filter filter = new OncePerRequestFilter() {
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            super.doFilter(request, response, filterChain);
            response.setHeader("Cache-Control", "xxx");
        }
    };
    return new FilterRegistrationBean(filter);
}

0
投票

我假设你正在使用spring-mvc(如你在标签中提到);如果是这样,你可以绑定到HttpServletResponse添加您的标题。你可以在你的方法处理程序,如下所示:

@RestController
class HelloWordController{

    @GetMapping("/hello")
    public String test(HttpServletResponse response){
        response.addHeader("test", "123");
        return "hola";
    }
}

另一种解决方案(时尚)将返回ResponseEntity代替:

@RestController
class HelloWorkController{

    @GetMapping("/hello")
    public ResponseEntity<String> test(HttpServletResponse response){
        return ResponseEntity.status(HttpStatus.OK)
                .header("test", "4567")
                .body("hello world");
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.