如何根据 Chrome 扩展中的用户选项修改 http 请求 headers?

How to modify http request headers based on user options in a Chrome extension?

我正在为 Google Chrome 开发我的第一个扩展。我想根据用户可以在我的扩展选项页面上 select 的选项向(所有)http(s) 请求添加自定义 header。

我成功管理了以下内容:

  1. 手动将自定义 header 添加到所有 http 请求
  2. 创建选项页面
  3. chrome.storage.sync
  4. 中的保存和恢复选项

现在我正在努力将以上内容结合起来。这是我尝试过的:

chrome.webRequest.onBeforeSendHeaders.addListener(function(details){
    var demoOptions = new Array();
    var demoHeader = '';
    var bkg = chrome.extension.getBackgroundPage(); // for debugging
    chrome.storage.sync.get({
    demoOption: true,
    }, function(items) {
        if(items.demoOption){
            demoOptions.push('demo-header-1');
        }
        demoHeader = demoOptions.join();
        details.requestHeaders.push({
            name: 'X-Demo-Header',
            value: demoHeader //this is not showing up in the http headers
        });
        bkg.console.log('demoHeader:' + demoHeader); //this is putting the correct value to the debugging console
        bkg.console.log(details.requestHeaders); //this is putting the headers to the debugging console, incl. x-demo-header1 and x-demo-header2 (see below)
    });

    details.requestHeaders.push({
        name: 'X-Demo-Header2',
        value: 'Demo-Header-2' //this is correctly added to the headers and included in the request
    });
    
    return { requestHeaders: details.requestHeaders };
},
{urls: [ "*://*/*" ]},['requestHeaders','blocking']);

观察:

  1. X-Demo-Header2 已正确插入(在 Chrome header 检查和 debigging 控制台中可见)
  2. x-demo-header1 是 不是 发送到站点的 header 的一部分,但在调试输出中可见
  3. x-demo-header2在header之前X-Demo-Header1在header控制台中的数组(见屏幕截图)

假设:获取我的选项的函数(项目)是异步执行的,并且在插入我的选项中的 header 之前返回 requestHeaders。

我可以避免这种情况吗?是否有更好的选择根据选项添加headers?

截图:

TL;DR

它目前无法在 Chrome(2021 年 2 月 15 日)

长答案

JavaScript 严重依赖于异步代码执行的思想。在您的代码中,您 return { requestHeaders: details.requestHeaders }chrome.storage.sync.get(key, listener) 中注册的事件侦听器完成之前。因此,demo-header-1添加得太晚了。

这就是您需要使用 Promise 的原因。 docs 中有一个如何做到这一点的示例。在那个页面向下滚动到它说的地方:“这段代码与前面的例子完全一样,除了侦听器是异步的,returning 一个用新的 [=47= 解析的 Promise ]".

在您的情况下,您的代码的修改版本将如下所示:

chrome.webRequest.onBeforeSendHeaders.addListener(
  function (details) {
    return new Promise((resolve, reject) => {
      chrome.storage.sync.get({ demoOption: true }, function (items) {
        if (items.demoOption) {
          details.requestHeaders.push({
            name: 'X-Demo-Header',
            value: 'demo-header-1',
          });
        }
        resolve({ requestHeaders: details.requestHeaders });
      });
    });
  },
  { urls: ['*://*/*'] },
  ['blocking', 'requestHeaders']
);

如您所见,我们现在 return Promise 而不是 object 文字。在这个 Promise 中,我们等待 chrome.storage.sync.get 的读取操作完成。完成后,我们解决 Promise。

这应该适用于支持它的浏览器。

在 Chrome 中这不起作用,因为:

There is no support in Chrome for returning a Promise from webRequest.onBeforeSendHeaders since 2018

除非你能找到一种方法以同步方式从 chrome.storage.sync.get 加载数据,否则恐怕你就不走运了。

作为最后的手段,您可以尝试 post 投诉 Chromium issue