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

Chrome扩展如何获取失败预检请求的HTTP状态码及可行方案?

解决Chrome扩展中预检请求失败无法获取状态码及错误区分问题

1. 能否获取失败预检请求的HTTP状态码?

内容脚本的fetch无法直接获取,因为浏览器CORS安全机制会在请求到达业务代码前拦截并抛出错误。但可通过扩展后台脚本的chrome.webRequest API实现:

  • 在manifest.json中添加webRequest、webRequestBlocking权限,以及目标站点的主机权限;
  • 监听chrome.webRequest.onCompleted事件,筛选OPTIONS方法的请求,即可拿到完整响应状态码:
    // 后台脚本 background.js
    chrome.webRequest.onCompleted.addListener(
      (details) => {
        if (details.method === 'OPTIONS') {
          console.log('预检请求状态码:', details.statusCode);
          // 可通过chrome.runtime.sendMessage将状态码传递给内容脚本
        }
      },
      { urls: ['https://目标站点域名/*'] },
      ['responseHeaders']
    );
    
  • 若预检请求因网络错误完全失败(如ERR_FAILED),需监听chrome.webRequest.onErrorOccurred事件获取错误信息。

2. 能否直接禁用特定站点的预检请求?

不行。预检请求是浏览器根据请求特性自动触发的安全机制,当请求属于「非简单请求」(如使用PUT/DELETE方法、自定义请求头、Content-Type为application/json等)时,浏览器必须发送预检请求,扩展没有权限强制禁用这一行为。

3. 可行的解决办法

方案一:用后台脚本作为请求代理(推荐)

扩展后台脚本不受前端CORS限制,可直接发起请求并获取完整响应信息,能彻底规避CORS相关问题:

  1. 内容脚本将请求参数发送给后台脚本:
    // 内容脚本 content.js
    async function fetchViaBackground(url, options) {
      const response = await chrome.runtime.sendMessage({
        type: 'PROXY_REQUEST',
        url,
        options
      });
      return response;
    }
    
    // 使用示例
    fetchViaBackground('https://目标站点/api/xxx', { method: 'POST', body: JSON.stringify(data) })
      .then(res => {
        if (res.status === 404) {
          alert('产品ID错误,请检查');
        } else if (res.status === 500) {
          // 静默重试逻辑
          setTimeout(() => fetchViaBackground(url, options), 2000);
        }
      });
    
  2. 后台脚本处理请求并返回结果:
    // 后台脚本 background.js
    chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
      if (request.type === 'PROXY_REQUEST') {
        try {
          const res = await fetch(request.url, request.options);
          const responseData = {
            status: res.status,
            headers: Object.fromEntries(res.headers.entries()),
            body: await res.text()
          };
          sendResponse(responseData);
        } catch (err) {
          sendResponse({ error: err.message });
        }
      }
      return true; // 标记为异步响应
    });
    
  3. 在manifest.json中配置对应的主机权限和后台脚本路径。

方案二:优化declarativeNetRequest规则处理预检请求

针对预检请求返回错误状态码的情况,可通过规则修改响应状态码为200并添加CORS头,让浏览器放行主请求,从而获取主请求的真实状态码:

  • 在declarative_net_request/rules.json中添加规则:
    [
      {
        "id": 1,
        "priority": 1,
        "action": {
          "type": "modifyHeaders",
          "responseHeaders": [
            { "header": "Access-Control-Allow-Origin", "operation": "set", "value": "*" },
            { "header": "Access-Control-Allow-Methods", "operation": "set", "value": "GET, POST, PUT, DELETE, OPTIONS" },
            { "header": "Access-Control-Allow-Headers", "operation": "set", "value": "*" }
          ]
        },
        "condition": {
          "urlFilter": "https://目标站点域名/*",
          "resourceTypes": ["xmlhttprequest"],
          "requestMethod": "OPTIONS"
        }
      },
      {
        "id": 2,
        "priority": 2,
        "action": {
          "type": "modifyHeaders",
          "responseHeaders": [
            { "header": "Access-Control-Allow-Origin", "operation": "set", "value": "*" }
          ],
          "statusCode": 200
        },
        "condition": {
          "urlFilter": "https://目标站点域名/*",
          "resourceTypes": ["xmlhttprequest"],
          "requestMethod": "OPTIONS",
          "responseStatusCodes": [400, 404, 500]
        }
      },
      {
        "id": 3,
        "priority": 1,
        "action": {
          "type": "modifyHeaders",
          "responseHeaders": [
            { "header": "Access-Control-Allow-Origin", "operation": "set", "value": "*" }
          ]
        },
        "condition": {
          "urlFilter": "https://目标站点域名/*",
          "resourceTypes": ["xmlhttprequest"],
          "excludeRequestMethods": ["OPTIONS"]
        }
      }
    ]
    
  • 此方案通过修改预检请求的状态码为200,让浏览器允许后续主请求,从而在内容脚本中获取主请求的真实状态码来区分错误类型。

方案三:结合webRequest和内容脚本关联请求状态

通过webRequest监听预检请求的状态,将状态与请求标识关联,内容脚本在请求失败时查询对应状态:

  1. 后台脚本记录每个预检请求的状态,用请求的requestId作为标识:
    // 后台脚本 background.js
    const preflightStatus = new Map();
    
    chrome.webRequest.onCompleted.addListener(
      (details) => {
        if (details.method === 'OPTIONS') {
          preflightStatus.set(details.requestId, details.statusCode);
          // 定时清理过期记录
          setTimeout(() => preflightStatus.delete(details.requestId), 5000);
        }
      },
      { urls: ['https://目标站点域名/*'] }
    );
    
    chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
      if (request.type === 'GET_PREFLIGHT_STATUS') {
        sendResponse(preflightStatus.get(request.requestId));
      }
    });
    
  2. 内容脚本在发起请求前,通过后台脚本获取请求ID,失败时查询对应预检状态,再执行相应逻辑。

内容的提问来源于stack exchange,提问作者Petr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 22:05:37