当前位置: 首页 > news >正文

黄聪:如何扩展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.inspectedWindow,devtools.network,devtools.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扩展遵循一种优秀的设计原则,
那就是在设计系统的时候,应该想办法让扩展对用户而言,与原生功能平权
这种对称性,会拉近原生与扩展之间的距离,从而让系统架构从一开始就建立在灵活的基础之上。



作者:何幻
链接:https://www.jianshu.com/p/4ce7f58b8c84
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

转载于:https://www.cnblogs.com/huangcong/p/9414479.html

相关文章:

  • 来,搞个侧栏导航
  • Oracle RAC/Clusterware 多种心跳heartbeat机制介绍 RAC超时机制分析
  • 手机点击输入框时,当键盘弹起,界面上弹,键盘收起,界面下来
  • Python 偏函数 partial function
  • php课程 12-41 多态是什么
  • 设计模式快速学习(三)单例模式
  • hadoop实操篇:hadoop基础教程汇总
  • python2.7用socks和socket设置代理
  • 关于VS2017编译UE4项目,输出窗口有乱码的解决方案
  • Java全栈程序员之01:做个Linux下的程序猿
  • 如何靠谱地查到Tomcat的版本
  • Maven根据pom文件中的Profile标签动态配置编译选项
  • openlayers+geoserver+wms实现空间查询,属性查询
  • 关于 HandlerMethodArgumentResolver 类 以及 WebArgumentResolver 类 自定义解析参数
  • vue.js有什么用,是用来做什么的(整理)
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • C# 免费离线人脸识别 2.0 Demo
  • create-react-app项目添加less配置
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • java中的hashCode
  • js 实现textarea输入字数提示
  • Mysql优化
  • Redux 中间件分析
  • 分类模型——Logistics Regression
  • 关于List、List?、ListObject的区别
  • 湖南卫视:中国白领因网络偷菜成当代最寂寞的人?
  • 基于Mobx的多页面小程序的全局共享状态管理实践
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 两列自适应布局方案整理
  • 码农张的Bug人生 - 初来乍到
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 最近的计划
  • 如何正确理解,内页权重高于首页?
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (转)Linq学习笔记
  • (转)Oracle存储过程编写经验和优化措施
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .bat批处理出现中文乱码的情况
  • .NET Micro Framework初体验(二)
  • .NET 设计模式—适配器模式(Adapter Pattern)
  • .NET/C# 项目如何优雅地设置条件编译符号?
  • .Net组件程序设计之线程、并发管理(一)
  • .vimrc php,修改home目录下的.vimrc文件,vim配置php高亮显示
  • .考试倒计时43天!来提分啦!
  • @Async注解的坑,小心
  • @transactional 方法执行完再commit_当@Transactional遇到@CacheEvict,你的代码是不是有bug!...
  • [ vulhub漏洞复现篇 ] Grafana任意文件读取漏洞CVE-2021-43798
  • [20190416]完善shared latch测试脚本2.txt
  • [AR]Vumark(下一代条形码)
  • [AutoSar]BSW_OS 01 priority ceiling protocol(PCP)
  • [AX]AX2012 R2 出差申请和支出报告