You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apollo Client中Token获取/刷新API未在GraphQL请求前优先执行的问题

Apollo Client中Token获取/刷新API未在GraphQL请求前优先执行的问题

看起来你遇到的核心问题是:当多个GraphQL请求同时触发时,每个请求都会独立调用Token获取/刷新API,导致Token请求和GraphQL请求并行执行,没有等待Token返回后再发起GraphQL请求。结合你的代码,我们可以从两个方面调整来解决这个问题:


1. 简化setContext的异步写法

你当前手动包裹了Promise,但Apollo Client的setContext本身支持直接返回async函数,这样代码更简洁,也能避免手动处理Promise可能带来的潜在问题:

const authLink = setContext(async (_, { headers }) => {
  // 等待commonAuthHeaders完成后再返回headers
  const updatedHeaders = await commonAuthHeaders(headers);
  return { headers: updatedHeaders };
});

2. 对Token请求做全局缓存,避免并行调用

问题的关键在于:当多个GraphQL请求同时发起时,每个请求都会触发commonAuthHeaders里的Token API调用,导致这些请求同时发送。我们可以通过一个全局Promise来缓存Token请求的结果,让所有后续请求都等待第一个Token请求完成,这样就能保证Token获取完成后才发起GraphQL请求。

修改你的commonAuthHeaders函数,加入全局Promise缓存逻辑:

// 全局变量,用于缓存正在进行的Token请求Promise
let tokenFetchPromise = null;

export async function commonAuthHeaders(headers) {
    const supportedLoginModes = ["autodesk", "google"]

    const logInMode = getCookie("__login_mode");
    let access_token = getCookie("__bauhub_token");
    const refresh_token = getCookie("__bauhub_refresh_token")
    const lang = getCookie("__bauhub_lang");
    const currentOrgId = getCookie("__current_organization");
    const pluginType = getCookie("pluginType")

    const newHeaders = {
        ...headers,
        "Apollo-Require-Preflight": true,
    };

    if (lang) newHeaders["Accept-Language"] = lang;

    if (supportedLoginModes.includes(logInMode)) {

        // 处理Autodesk Token刷新逻辑,加入缓存
        if (logInMode === "autodesk" && !access_token && refresh_token) {
            // 如果没有正在进行的Token请求,发起新请求;否则等待已有请求
            if (!tokenFetchPromise) {
                tokenFetchPromise = refreshAutodeskAccessToken().finally(() => {
                    // 请求完成后清空缓存,下次需要时重新发起
                    tokenFetchPromise = null;
                });
            }
            access_token = await tokenFetchPromise;
            // 更新cookie,避免下次请求重复触发刷新
            document.cookie = `__bauhub_token=${access_token}; path=/;`; // 根据你的实际Cookie配置补充参数,比如过期时间、domain等
        }

        // 处理Google Token获取逻辑,加入缓存
        if (logInMode === "google" && !access_token) {
            if (!tokenFetchPromise) {
                tokenFetchPromise = getFirebaseToken().finally(() => {
                    tokenFetchPromise = null;
                });
            }
            access_token = await tokenFetchPromise;
            // 更新cookie
            document.cookie = `__bauhub_token=${access_token}; path=/;`;
        }

        if (access_token && currentOrgId) newHeaders.organizationId = currentOrgId;
        if (logInMode && access_token) newHeaders.logInMode = logInMode;
        if (pluginType) newHeaders.pluginType = pluginType;

        newHeaders.authorization = access_token ? `Bearer ${access_token}` : "";
        return newHeaders;
    } else {
        return headers;
    }
}

为什么这样能解决问题?

  • 通过全局的tokenFetchPromise,我们确保同一时间只有一个Token请求在执行,所有后续的GraphQL请求都会等待这个Promise完成后再拿到Token、组装Headers并发起请求。
  • 更新Cookie的操作能让后续请求直接从Cookie获取Token,不需要重复调用Token API。
  • 简化后的setContext写法更符合Apollo Client的异步处理规范,减少了手动Promise管理的出错概率。

备注:内容来源于stack exchange,提问作者Nischal Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 10:08:10