Java 当控制器返回ResponseEntity时,如何在筛选器中设置响应状态代码?

Java 当控制器返回ResponseEntity时,如何在筛选器中设置响应状态代码?,java,servlet-filters,Java,Servlet Filters,我正在开发一个带有servlet过滤器的简单Spring引导应用程序,用于设置响应状态代码: @Component public class TestFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {

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

@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
    }
}
@RestController
公共类测试控制器{
@GetMapping(“/string”)
公共字符串testString(){
返回“OK”;//过滤器设置的状态码为201
}
}
@RestController
公共类测试控制器{
@GetMapping(“/entity”)
公共响应度测试响应度(){
return ResponseEntity.ok(“ok”);//状态代码为200
}
}
为什么过滤器在使用ResponseEntity时不更改状态代码

--


Github上的项目:

正如文档中所述,
响应属性#ok
返回HTTP ok()状态代码。过滤器运行较早,因此状态稍后会被覆盖


您应该使用。

中描述的其他方法创建
响应属性
,因为文档中说明
响应属性#ok
返回HTTP ok()状态代码。过滤器运行较早,因此状态稍后会被覆盖


您应该使用中描述的其他方法创建
响应属性。

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

您的过滤器实现更改响应状态代码,然后继续运行过滤器链的其余部分。我们刚刚建立的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);
        }
    }
}
但不幸的是,这也行不通!根据报告:

请注意,postHandle对于@ResponseBody和 ResponseEntity为其编写和提交响应的方法 在HandlerAdapter内和postHandle前。那就意味着它太大了 延迟对响应进行任何更改,例如添加额外的 标题。对于此类场景,您可以实施ResponseBodyAdvice和 要么将其声明为控制器建议bean,要么对其进行配置 直接在RequestMappingHandlerAdapter上

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

@ControllerAdvice
公共类TestResponseBodyAdvice实现ResponseBodyAdvice{
@凌驾
公共布尔支持(MethodParameter returnType,Class>converterType){
返回true;
}
@凌驾
public T beforeBodyWrite(T body,MethodParameter returnType,MediaType selectedContentType,
类>selectedConverterType、ServerHttpRequest请求、ServerHttpResponse响应){
//
//在此处插入所选的状态代码
response.setStatusCode(HttpStatus.CHECKPOINT);
返回体;
}
}

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

您的过滤器实现更改响应状态代码,然后继续运行过滤器链的其余部分。我们刚刚建立的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);
        }
    }
}
但不幸的是,这也行不通!根据报告:

请注意,postHandle对于@ResponseBody和 ResponseEntity为其编写和提交响应的方法 在HandlerAdapter内和postHandle前。那就意味着它太大了 延迟对响应进行任何更改,例如添加额外的 标题。对于此类场景,您可以实施ResponseBodyAdvice和 要么将其声明为控制器建议bean,要么对其进行配置 直接在RequestMappingHandlerAdapter上

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

@ControllerAdvice
公共类TestResponseBodyAdvice实现ResponseBodyAdvice{
@凌驾
公共布尔支持(MethodParameter returnType,Class>converterType){
返回true;
}
@凌驾
public T beforeBodyWrite(T body,MethodParameter returnType,MediaType selectedContentType,
类>selectedConverterType、ServerHttpRequest请求、ServerHttpResponse响应){
//
//在此处插入所选的状态代码
response.setStatusCode(HttpStatus.CHECKPOINT);
返回体;
}
}