[FE] 如何扩展Chrome DevTools来获取页面请求

1. Chrome DevTools Extension

熟悉React的同学,可能对React Developer Tools并不陌生,

刚看到的时候,我也觉得很神奇,
因为React Developer Tools和其他Chrome Extension不同,
它居然出现在了Chrome开发者工具栏中,和原生的DevTools一样强大。
例如,可以审查元素,查看元素的属性,等等。

后来才知道,像这种出现在Chrome开发者工具栏中的扩展,称为Chrome DevTools Extension

比起普通的Chrome Extension,Chrome DevTools Extension可以访问更多API,例如,
(1)devtools.inspectedWindow
(2)devtools.network
(3)devtools.panels
其中包括了,与当前审查窗口相关的,与网络请求相关的,以及与开发者工具栏相关的API。

2. 背景 & 基本概念

在某一具体项目中,有一个这样的需求,
我们需要选择页面中发起的http请求,然后将它们保存到数据库中。

由于页面发起的请求可能会发往不同的服务器,所以在服务器端解决这个问题就显得比较麻烦,
而编写一个Chrome DevTools Extension会更简单直接。

下文我将这个功能的核心抽离出来,作为一个例子,来还原Chrome DevTools Extension的编写方法。
为此,我们先熟悉几个基本的概念。

(1)tab页

Chrome浏览器是由tab页组成的,一个浏览器实例中可以打开多个tab页。


(2)DevTools Window

每个tab页,都可以打开自己的开发者工具窗口,称为DevTools Window。


注意,每个tab页都有自己独立的DevTools Window,
只是切换tab页的时候,只会显示当前tab页的DevTools Window。

(3)DevTools Page 和 Panel

下面我们来创建一个Chrome DevTools Extension项目,目录结构如下,

chrome-devtools-extension-example
├── devtools.html    // DevTool Page
├── devtools.js      // DevTool Page中引用的js
├── manifest.json    // 入口
├── panel.html       // 开发者工具栏选项卡页面
└── panel.js         // 选项卡页面中引用的js

其中manifest.json是入口,它会声明一个对用户不可见的DevTools Page。
在本例中为devtools.html

{
  "name": "PageRecorder",
  "version": "1.1.0",
  "minimum_chrome_version": "10.0",
  "description": "Record all http requests in a page.",
  "devtools_page": "devtools.html",
  "manifest_version": 2
}

DevTools Page引入的js,具有访问DevTools API的能力,
包括上文提到的那些API,devtools.inspectedWindowdevtools.networkdevtools.panels

DevTools Page对用户是不可见的,如果需要在开发者工具栏中创建新的DevTool选项卡,
还需要在DevTools Page使用一下方法来创建,DevTool选项卡,官方称为Panel。
原生的Panel包括,Elements,Console,Network,等等。

// 创建一个Panel
chrome.devtools.panels.create(

  // title
  'ChromeDevToolsExtensionExample',

  // iconPath
  null,

  // pagePath
  'panel.html'
);

以上,我们在DevTool Page中创建了一个新的Panel,名字为ChromeDevToolsExtensionExample

其中,panel.html,我们只是简单的写了一个Hello World!
值得注意的是,每个Panel都可以加载自己的html,js和css,且具有和DevTools Page一样的权限。

(4)Panel的生命周期

Panel只有在第一次被激活的时候,才进行实例化,
同一个DevTools Window中的不同Panel切换时,不会重新加载。
当前tab页刷新时,Panel也不会重新加载。

DevTools Window关闭后,Panel将被销毁。

因此,我们要想使用Chrome DevTools Extension,就必须先打开开发者工具窗口,
然后再激活我们新建的DevTools Panel。

3. 监听请求

上文我们提到了,Chrome DevTools Extension可以访问devtools.network API,
现在我们来展示使用chrome.devtools.network.onRequestFinished.addListener来获取请求。
为此,我们新建了一个panel.js文件,并在panel.html中引用它。

// Chrome DevTools Extension中不能使用console.log
const log = (...args) => chrome.devtools.inspectedWindow.eval(`
    console.log(...${JSON.stringify(args)});
`);

// 注册回调,每一个http请求响应后,都触发该回调
chrome.devtools.network.onRequestFinished.addListener(async (...args) => {
    try {
        const [{
            // 请求的类型,查询参数,以及url
            request: { method, queryString, url },

            // 该方法可用于获取响应体
            getContent,
        }] = args;

        log(method, queryString, url);

        // 将callback转为await promise
        // warn: content在getContent回调函数中,而不是getContent的返回值
        const content = await new Promise((res, rej) => getContent(res));
        log(content);
    } catch (err) {
        log(err.stack || err.toString());
    }
});

以上就是panel.js的完整内容了,我们还需要做以下几点说明,

(1)Chrome DevTools Extension中,不能直接使用console.log
所以本例中使用了devtools.inspectedWindow API中的chrome.devtools.inspectedWindow.eval方法,
在当前审查的窗口中直接求值一段js代码,从而间接实现打印日志的功能。

(2)与获取http请求的methodqueryStringurl不同的是,
我们需要调用getContent方法来获取http响应体,
并且,getContent是一个高阶的异步函数

所谓高阶函数,指的是,它接受一个回调函数作为参数getContent(content=>{ })
这个回调函数的参数content,才是对应http请求的响应体。

所谓异步,指的是,当回调函数还没触发的时候,getContent就已经返回了。
这也导致了事件监听函数,也不得不具有异步性。

(3)由于事件监听函数是异步的,
所以,有可能在上一个onRequestFinished事件还未处理完的情况下,
下一个onRequestFinished的监听函数就又被触发了。

这就导致了,以上例子中,log(method, queryString, url);log(content);
可能是乱序打印的。

这个问题我们曾经讨论过,可参考:怎样按触发顺序执行异步任务

总结

到此为止,我们最简版的Chrome DevTools Extension示例已经介绍完了,
以下是可以运行的源码地址:github: chrome-extension-example
(注:不是master分支,而是simply分支。

Chrome扩展遵循一种优秀的设计原则,
那就是在设计系统的时候,应该想办法让扩展对用户而言,与原生功能平权
这种对称性,会拉近原生与扩展之间的距离,从而让系统架构从一开始就建立在灵活的基础之上。


参考

Chrome Extension
Extending DevTools
devtools.network#onRequestFinished
github: chrome-extension-example
简书:怎样按触发顺序执行异步任务

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容