Service Worker的fetch事件中无法读取Authorization请求头如何解决
问题背景
客户端使用封装的getJSON方法发起带Authorization令牌的GET请求到Node.js服务,函数实现如下:
// Do a secured GET API request and return response object async function getJSON(url) { try { // Send request const res = await fetch(url, { headers: { Authorization: localStorage.getItem('token') } }); // If something went wrong if (!res.ok) { // If token is invalid if (res.status === 401) { // Logout localStorage.removeItem('token'); location.replace(`/?msg=${dict.get('expired-session')}`); } // Return an object with error message and status code return { error: res.statusText, status: res.status }; } // Else return response object else return await res.json(); } catch (err) { // Return an object with error message return { error: err.message }; } }
接入Service Worker做静态资源缓存时,为了避免缓存带鉴权的API请求,原逻辑计划通过判断请求是否携带Authorization头识别API请求,跳过缓存流程,初始Service Worker代码如下:
// Fetch event self.addEventListener('fetch', e => e.respondWith(respond(e))); async function fetchAndCache(req, cache_name) { const { url, headers } = req; console.log({ url, headers }); // Fetch request const fetch_res = await fetch(req); const is_get = req.method === 'GET'; const is_api = req.headers.Authorization; const is_cahing_domain = cache_domains.some(domain => req.url.includes(domain)); if (is_cahing_domain && is_get && !is_api) { // Open cache and save a cloned result const cache = await caches.open(cache_name); cache.put(req, fetch_res.clone()); } return fetch_res; } async function respond(e) { if (!use_cache) return fetch(e.request); // Try to get response from cache const cached_res = await caches.match(e.request); // If response is found, return it if (cached_res) return cached_res; // If request is not found, try to fetch it return await fetchAndCache(e.request, 'main'); }
故障表现
运行时控制台打印的headers对象为空,Authorization头判断逻辑始终为false,导致带令牌的API请求被错误写入缓存;但后端实际可以正常收到请求携带的Authorization令牌,缓存条目中也能看到对应请求携带了该头。
根因分析
问题出在Request对象头的读取方式错误:
- Fetch API规范中,
Request.headers是Headers类实例,不是普通JavaScript对象,不能直接通过点语法(req.headers.Authorization)读取头字段值 - 直接console.log打印Headers实例时,控制台不会枚举展示其内部存储的键值对,因此看起来是空对象,但头数据实际是存在的,所以后端能正常收到令牌
修复方案
使用Headers实例标准的get()方法读取头字段,修正fetchAndCache函数的头读取逻辑即可:
async function fetchAndCache(req, cache_name) { const { url } = req; // 标准方式读取Authorization头,头名大小写不敏感,不存在时返回null const authToken = req.headers.get('Authorization'); console.log('Request url:', url, 'Has auth token:', !!authToken); // Fetch request const fetch_res = await fetch(req); const is_get = req.method === 'GET'; // 存在Authorization头则判定为API请求,不缓存 const is_api = !!authToken; // 修正原代码拼写错误:is_cahing_domain -> is_caching_domain const is_caching_domain = cache_domains.some(domain => req.url.includes(domain)); if (is_caching_domain && is_get && !is_api) { // Open cache and save a cloned result const cache = await caches.open(cache_name); cache.put(req, fetch_res.clone()); } return fetch_res; }
优化建议
- 可以提前在
respond函数中判断API请求,直接走网络逻辑,避免不必要的缓存查询,进一步提升性能 - 除了判断Authorization头,建议同时结合API路径前缀(比如所有接口都走
/api/路径)做过滤,避免跨域场景下受CORS头访问限制导致判断失效
内容的提问来源于stack exchange,提问作者Iconejey
相关产品推荐
相关产品推荐

