在不检查异常的情况下查找我们是否在线程绑定请求中的安全方法

Safe way to find if we are within a thread bound request without checking for an Exception

问题

所以我们有一个请求拦截器 (Feign),它检查 autowired HttpServletRequest 是否有 headers,然后 propagates/copies 这些到传出请求。我们的拦截器的工作是将 headers 从一个微服务传播到另一个微服务,这样即使图中的最后一个 micro-service 也有关于谁发起请求的信息(例如租户)。

有时我们调用 feign 作为 HTTP 请求线程的结果,有时我们在 start-up 或调度线程中调用它。

在调度线程的情况下,我们希望能够检测请求是否存在而无需执行 try/catch。这是我们是发起方,不需要复制任何东西的情况。

我原以为下面的方法会起作用,但我们得到一个抛出异常的代理 object:

以下检查失败,因为 this.request 不为空:

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. 使用try/catch解决方案(最不受欢迎)

  2. 检查线程局部变量是否存在请求

  3. 传递我们自己的附加线程局部变量,这将是一个标志(我们不在请求上下文中)。

问题

我不喜欢 1,因为捕获异常非常昂贵,而且它们可能会掩盖任何真正的错误。

我不喜欢 2,因为如果 spring 实现发生变化,实现细节可能会发生变化(例如密钥),并且我们在 starter 中的实现会中断。但无论如何,每当升级 spring 启动时,各种次要或主要的事情都需要修复。

我喜欢选项 3,因为在调用我们的假客户端之前设置标志是一种有意识的行为。因此不存在错误 un-noticed.

的风险

意见、选项、解决方案?

更新

一位团队成员建议我们使用: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;

但这完全取决于 spring 的内部结构,并且他们继续使用 "Request attributes"。

我有类似的问题,我使用RequestContextHolder检查请求是否绑定到线程。根据 docgetRequestAttributes returns null if no RequestAttributes bound to the thread.

RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
if (attrs == null) {
    return;
}