SpringBoot 2 - OncePerRequestFilter - изменить заголовки ответа после обработки контроллера - PullRequest
0 голосов
/ 02 февраля 2019

Здравствуйте! Я хочу изменить некоторые из заголовков ответа моего API после того, как я завершил обработку (выполнил логику) и завершил работу с кодом состояния HTTP.

Например, если ответ 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;
  }

Я уже пытался добавить HttpServletResponseWrapper, как указано в этих сообщениях здесь и здесь на CacheControlFilter, но это не похожеработать.Я также видел подобный поток SO здесь .

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 ответ уже содержит заголовки default, заполненные фильтром (CacheControlFilter).В итоге получается немного странно, я добавляю дополнительную логику, чтобы изменить контрольный заголовок, и в итоге получаю ответ с одинаковым заголовком 2 раза (1 со значением, установленным CacheControlFilter, а затем любое специальное значение IЯ пытаюсь переопределить ControllerAdvice

Любые советы или помощь, спасибо! Я использую Spring Boot 2.1.2 с Undertow в качестве основного контейнера сервлета.

Ответы [ 3 ]

0 голосов
/ 03 февраля 2019

Ссылка , которую вы упомянули, говорит, что не может получить код состояния или изменить заголовки в ResponseBodyAdvice, неверно.Если вы разыгрываете ServerHttpResponse на ServletServerHttpResponse, вы можете сделать оба из них.Так что просто реализуйте 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");
        }       
    }
}
0 голосов
/ 03 февраля 2019

Существует дюжина способов изменить HttpServletResponse перед возвратом клиенту в Spring и добавить ответ в метод обработчика или использовать 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 голосов
/ 03 февраля 2019

Я предположил, что вы используете 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");
    }
}
...