Как установить код состояния ответа в фильтре, когда контроллер возвращает ResponseEntity? - PullRequest
0 голосов
/ 29 марта 2019

Я разрабатываю простое приложение 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

Ответы [ 2 ]

0 голосов
/ 29 марта 2019

С конечной точкой /string вы не изменяете код состояния. С конечной точкой /entity вы явно являетесь (вы устанавливаете ее на 200 благодаря возвращению ok).

Ваша реализация фильтра изменяет код состояния ответа, и , затем продолжает выполнение остальной части цепочки фильтров - и затем сервлет . И мы только что установили, что сервлет (ваш контроллер) устанавливает код состояния ответа на что-то другое.

Так что вам нужно изменить код ответа после того, как сервлет / контроллер выполнил свою задачу.

Ваша первая мысль может заключаться в переопределении вашего фильтра следующим образом:

@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, для которых ответ написан и передан в адаптере Handler и перед postHandle. Это означает, что это слишком поздно вносить какие-либо изменения в ответ, например добавлять дополнительные заголовок. Для таких сценариев вы можете реализовать ResponseBodyAdvice и либо объявить его как компонент Controller Advice, либо настроить его непосредственно в 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;
    }

}
0 голосов
/ 29 марта 2019

Как указано в документации, ResponseEntity#ok возвращает HTTP-код состояния OK (https://httpstatuses.com/200)). Фильтр работает раньше, поэтому состояние перезаписывается позже.

Вы должны создать ResponseEntity, используя другие средства, описанные в https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html.

...