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



>问题

因此,我们有一个请求拦截器(Feign),它检查autowiredHttpServletRequest中的标头,然后将其传播/复制到传出请求。我们的拦截器的工作是将标头从一个微服务传播到另一个微服务,以便即使是图中的最后一个微服务也包含有关谁发起请求的信息(例如租户)。

有时我们调用 feign 作为 HTTP 请求线程的结果,有时我们在启动时或从计划线程调用它。

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

我期待以下内容可以工作,但我们得到一个引发异常的代理对象:

以下检查失败,因为 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. 使用尝试/捕获解决方案(最不受欢迎)

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

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

问题

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

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

选项 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;

但这完全取决于 spring 的内部结构,并且它们继续使用"请求属性"。

我有一个类似的问题,我使用RequestContextHolder来检查请求是否绑定到线程。根据文档,如果没有绑定到线程的 RequestAttributes,getRequestAttributes返回 null。

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

最新更新