简体   繁体   中英

How to set response status code in Filter when controller returns ResponseEntity?

I'm developing a simple Spring Boot application with servlet filter, that intended to set response status code:

@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);
    }

Everything goes as expected (status is 201) if controller returns a String. But if controller returns a ResponseEntity, then after calling doFilter() status code is 200 instead of 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
    }
}

Why filter doesn't change status code when using ResponseEntity?

--

Project on Github: https://github.com/timofeev-denis/set-status-code

As the documentation states the ResponseEntity#ok returns a HTTP OK ( https://httpstatuses.com/200 ) status code. The filter is running earlier so the status is later overwritten.

You should create the ResponseEntity using some other means described in https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html .

With the /string endpoint, you're not modifying the status code. With the /entity endpoint, you explicitly are (you're setting it to 200 by virtue of returning an ok).

Your filter implementation changes the response status code and then proceeds to run the rest of the filter chain -- and then the servlet . And we just established the servlet (your controller) is setting the response status code to something else.

So you need to change the response code after the servlet/controller has done its thing.

Your first thought might be to reimplement your filter like this:

@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);
        }
    }
}

But unfortunately, that won't work either! According to the documentation :

Note that postHandle is less useful with @ResponseBody and ResponseEntity methods for which the response is written and committed within the HandlerAdapter and before postHandle. That means it is too late to make any changes to the response, such as adding an extra header. For such scenarios, you can implement ResponseBodyAdvice and either declare it as an Controller Advice bean or configure it directly on RequestMappingHandlerAdapter.

This might have the desired effect you're looking for (note I set to "CHECKPOINT" just to demonstrate the point!):

@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;
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM