如何通过jQuery的ajaxPreFilter设置Ajax请求头?
问题分析
你的核心问题是异步操作的时序冲突:check_token_expiry里的Token刷新请求是异步执行的,但原Ajax请求不会等待这个异步操作完成就直接发送,导致successCallback里的jqXHR.setRequestHeader执行时,请求已经发出,自然无法修改请求头。另外你的currentRequests变量没有正确存储请求实例,调用currentRequests[options.url].abort()会直接报错。
修复方案
我们需要暂停原请求,等待Token刷新完成后,再带着新Token重新发起请求,同时优化请求管理避免重复刷新:
// 标记是否正在刷新Token,避免重复发起刷新请求 let isRefreshingToken = false; // 存储等待Token刷新完成后需要重新发起的请求队列 let requestQueue = []; $.ajaxPrefilter(function(options, originalOptions, jqXHR) { // 跳过刷新Token和登录请求的预处理 const skipUrls = ['API_URL/api/auth/refresh_token', 'API_URL/api/auth/login']; if (skipUrls.includes(options.url)) { return; } const currentToken = localStorage.getItem("api_token"); const tokenExpiry = localStorage.getItem("api_token_expiry"); const timeNow = Math.floor(Date.now() / 1000); // Token未过期,直接设置请求头 if (tokenExpiry && tokenExpiry > timeNow) { jqXHR.setRequestHeader("X-Api-Token", currentToken); return; } // Token已过期,触发刷新流程 if (!isRefreshingToken) { isRefreshingToken = true; $.ajax({ type: 'POST', url: "API_URL/api/auth/refresh_token", data: "refresh_token=" + localStorage.getItem("refresh_token") }).done(function(data) { if (data.status == 'success') { // 更新本地Token存储 localStorage.setItem("api_token", data.token); localStorage.setItem("refresh_token", data.refresh_token); localStorage.setItem("api_token_expiry", data.token_expiry); localStorage.setItem("refresh_token_expiry", data.refresh_token_expiry); // 给当前请求设置新Token并发送 jqXHR.setRequestHeader("X-Api-Token", data.token); // 批量处理队列中等待的请求 requestQueue.forEach(({ options, resolve }) => { options.headers = options.headers || {}; options.headers["X-Api-Token"] = data.token; resolve($.ajax(options)); }); } }).fail(function() { // 刷新失败,跳转登录页并拒绝所有等待请求 window.location.href = "login.html"; requestQueue.forEach(({ reject }) => reject(new Error('Token刷新失败'))); }).always(function() { // 重置刷新状态,清空队列 isRefreshingToken = false; requestQueue = []; }); } // 若正在刷新Token,将当前请求加入队列等待处理 return new Promise((resolve, reject) => { requestQueue.push({ options: originalOptions, resolve, reject }); }); });
关键改进点
- 异步请求队列:用
isRefreshingToken避免重复刷新,requestQueue存储等待请求,刷新完成后统一处理,避免并发冲突。 - 时序控制:通过Promise让原请求等待Token刷新完成,确保新Token能正确附加到请求头。
- 错误处理:刷新失败时统一跳转登录页,同时拒绝所有等待的请求,避免无效请求堆积。
- 直接设置请求头:重新发起请求时直接修改
options.headers,确保请求发送时携带最新Token。
内容的提问来源于stack exchange,提问作者user1530205
相关产品推荐
相关产品推荐

