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
相关产品推荐
相关产品推荐

