【问题标题】:How to set response status code in Filter when controller returns ResponseEntity?控制器返回ResponseEntity时如何在Filter中设置响应状态码?
【发布时间】:2019-08-20 18:11:06
【问题描述】:

我正在开发一个带有 servlet 过滤器的简单 Spring Boot 应用程序,旨在设置响应状态代码:

@Component
public class TestFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {

        HttpServletResponse response = (HttpServletResponse) resp;
        response.setStatus(201);
        chain.doFilter(req, resp);
    }

如果控制器返回一个字符串,一切都会按预期进行(状态为 201)。但是如果控制器返回一个ResponseEntity,那么在调用doFilter()之后状态码是200而不是201:

@RestController
public class TestController {

    @GetMapping("/string")
    public String testString() {
        return "OK"; // status code is 201 as set by Filter
    }
}

@RestController
public class TestController {

    @GetMapping("/entity")
    public ResponseEntity<String> testResponseEntity() {
        return ResponseEntity.ok("OK"); // status code is 200
    }
}

为什么过滤器在使用 ResponseEntity 时不改变状态码?

--

Github 上的项目:https://github.com/timofeev-denis/set-status-code

【问题讨论】:

    标签: java servlet-filters


    【解决方案1】:

    使用/string 端点,您无需修改​​状态代码。使用 /entity 端点,您明确地是(您通过返回 ok 将其设置为 200)。

    您的过滤器实现更改了响应状态代码,然后继续运行过滤器链的其余部分 -- 和 then the servlet。我们刚刚建立了 servlet(您的控制器)正在将响应状态代码设置为其他内容。

    因此,您需要在 servlet/控制器完成其操作后更改响应代码。

    您的第一个想法可能是像这样重新实现您的过滤器:

    @Component
    public class TestFilter implements Filter {
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
            try {
                chain.doFilter(req, resp);
            } finally {
                HttpServletResponse response = (HttpServletResponse) resp;
    
                response.setStatus(205);
            }
        }
    }
    

    但不幸的是,这也行不通!根据documentation

    请注意 postHandle 与 @ResponseBody 和 为其编写和提交响应的 ResponseEntity 方法 在 HandlerAdapter 中和 postHandle 之前。这也意味着 迟到对响应进行任何更改,例如添加额外的 标题。对于此类场景,您可以实现 ResponseBodyAdvice 和 要么将其声明为 Controller Advice bean,要么对其进行配置 直接在 RequestMappingHandlerAdapter 上。

    这可能会达到您想要的效果(注意我设置为“CHECKPOINT”只是为了证明这一点!):

    @ControllerAdvice
    public class TestResponseBodyAdvice<T> implements ResponseBodyAdvice<T> {
    
        @Override
        public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
            return true;
        }
    
        @Override
        public T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType,
                Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
            //
            // insert status code of choice here
            response.setStatusCode(HttpStatus.CHECKPOINT);
    
            return body;
        }
    
    }
    

    【讨论】:

    • 你是救生员!谢谢你。我花了几个小时试图找到解决方案,直到我遇到这个。 (我的问题与 OP 的问题有点不同,但解决方案是一样的。)
    【解决方案2】:

    正如文档所述,ResponseEntity#ok 返回 HTTP OK (https://httpstatuses.com/200) 状态代码。过滤器运行较早,因此稍后会覆盖状态。

    您应该使用https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html 中描述的其他方式创建ResponseEntity

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-20
      • 2021-07-21
      • 2016-06-16
      • 2020-10-01
      • 1970-01-01
      相关资源
      最近更新 更多