Безопасный способ узнать, находимся ли мы в запросе с привязкой к потоку, не проверяя исключение - PullRequest
2 голосов
/ 25 апреля 2019

Задача

Итак, у нас есть перехватчик запросов (Feign), который проверяет autowired HttpServletRequest на заголовки, а затем распространяет / копирует их в исходящий запрос. Работа нашего перехватчика состоит в том, чтобы распространять заголовки от микросервиса к микросервису так, чтобы даже последний микросервис на графике имел информацию о том, кто инициировал запрос (например, арендатор).

Иногда мы вызываем feign в результате потока HTTP-запроса, а иногда мы вызываем его при запуске или из запланированного потока.

В случае запланированного потока, мы хотели бы иметь возможность определить, существует ли запрос, без необходимости делать попытку / перехват. Это тот случай, когда мы являемся инициатором, и нам не нужно ничего копировать.

Я ожидал, что сработает следующее, но мы получаем прокси-объект, который выдает исключение:

Следующая проверка не пройдена, поскольку this.request не имеет значение null:

this.request!=null && this.request.getHeader("X-Application")

Со следующей ошибкой:

No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.

Я понимаю ошибку. Я бы хотел избежать очевидного обходного пути, которое могло бы выглядеть примерно так:

Текущее решение - неуклюжие и плохие

//TODO: Review this
        boolean requestExists = true;
        try{
            request.getHeader(APPLICATION_HEADER);
        }catch (IllegalStateException e ){
            requestExists = false;
        }

Текущий код, вызывающий проблему

   public class ServiceNameFeignInterceptor implements RequestInterceptor {
        private static final Logger log = LoggerFactory.getLogger(ServiceNameFeignInterceptor.class);
        final TenantIdResolver tenantResolver;
        final ApplicationNameResolver appResolver;
        private final String APPLICATION_HEADER = "X-Application";
        private final String TENANT_ID = "X-Tenant-Id";
        ...
        @Autowired
        HttpServletRequest request;

        public void apply(RequestTemplate requestTemplate) {
...

if (this.request!=null && this.request.getHeader("X-Application") != null) {
                log.info("Application header found in the request !!!");
                requestTemplate.header("X-Application", new String[]{this.request.getHeader("X-Application")});
                requestTemplate.header("X-Tenant-Id", new String[]{this.request.getHeader("X-Tenant-Id")});

            } else {
                log.info("Setting {} as {} for URL {}  ", new Object[]{"X-Application", appName, requestTemplate.url()});
                requestTemplate.header("X-Application", new String[]{appName});
                requestTemplate.header("X-Tenant-Id", new String[]{appName});               
            }

}

Текущие параметры

Пожалуйста, исправьте меня в следующих пунктах или предложите лучшие варианты, если это возможно.

В данный момент у меня есть три варианта:

  1. Используйте решение "попробовать / поймать" (наименее предпочтительное)

  2. Проверка локальных переменных потока на наличие запроса

  3. Передайте нашу собственную локальную переменную потока, которая будет флагом (что мы не в контексте запроса).

Проблемы

Мне не нравится 1, потому что перехват исключений стоит дорого, и потому, что они могут маскировать любые реальные ошибки.

Мне не нравится 2, потому что, если реализация пружины изменилась, возможно, изменились бы детали реализации (например, ключ), и наша реализация в нашем стартере сломалась бы. Но в любом случае при обновлении пружинной загрузки необходимо исправить различные мелкие или важные вещи.

Вариант 3 Мне нравится, потому что это сознательное действие, чтобы установить флаг перед вызовом нашего симулированного клиента. Таким образом, нет риска, что ошибки останутся незамеченными.

Мнения, варианты, решения?

Обновление

Один из членов команды предлагает использовать: new NamedThreadLocal("Request attributes");

Они предлагают это из-за реализации по адресу:

https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/context/request/RequestContextHolder.java#L50

https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/context/request/RequestContextHolder.java#L107

Так что мы будем использовать что-то вроде:

ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");
        RequestAttributes attributes = (RequestAttributes)requestAttributesHolder.get();
        boolean requestExists = attributes != null;

Но это очень зависит от внутренних элементов пружины, и они продолжают использовать «атрибуты запроса».

...