Postman - REST Client(Chrome 扩展)技术文档
一个可以直接在浏览器标签页中运行的 REST API 调试工具,基于原生 JavaScript + jQuery 实现的单页 Chrome 扩展。项目无构建步骤,源码即产物。
- 当前版本:
0.9.0(Manifest V3) - 历史版本:
0.8.4.22(Manifest V2) - 扩展 ID(由
manifest.json中的key决定,升级后保持不变):coohjcphdfgbiolnekdpbcijmhambjff - 数据全部保存在本地(IndexedDB + localStorage),不依赖后端服务
- google extension 开发文档 developer.chrome.google.cn/docs/extens...
开源项目代码: gitcode.com/ai-cm/post_...

1. 快速开始
1.1 加载扩展
- 打开
chrome://extensions/ - 右上角开启「开发者模式」
- 点击「加载已解压的扩展程序」,选择本项目根目录(包含
manifest.json的目录) - 点击工具栏中的扩展图标,会在新标签页打开
index.html
修改代码后,在
chrome://extensions/点击扩展卡片上的「重新加载」;页面本身刷新即可生效。 因为manifest.json保留了原扩展的key,扩展 ID 固定,升级到 MV3 后原有的 IndexedDB/localStorage 数据仍可读取。
扩展程序增加:下载git代码即可。
bash
# 下载代码,然后加载即可
git clone https://gitcode.com/ai-cm/post_man_original.git

1.2 使用
- 在 URL 输入框填写地址,选择方法后点击 Send(或在输入框按回车)发送请求
- 左侧边栏切换 History / Collections
- 顶部环境选择器用于切换变量环境,
{{variable}}会在发送前被替换
2. 功能特性
| 分类 | 能力 |
|---|---|
| 请求构建 | 方法(GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS/...)、URL 参数键值编辑、Headers 键值编辑、Body 三种模式(form-data / x-www-form-urlencoded / raw) |
| 认证 Helper | Basic Auth、Digest Auth、OAuth 1.0(本地签名) |
| 变量系统 | Environment(多环境)与 Globals,语法 {{var}},分隔符可在设置中调整 |
| Collections | 新建/重命名/删除集合、保存当前请求到集合、导出为 JSON 文件、从文件或 URL 导入、保存示例响应 |
| History | 自动记录请求历史,可配置保留条数(默认 100),支持清空 |
| Header Presets | 常用请求头预设,可在 Headers 编辑器中自动补全 |
| 响应查看 | Body(Pretty / Raw / Preview)、Cookies、Headers、状态码/耗时/大小、响应另存为示例 |
| 其它 | 请求代理模式、快捷键、导出响应/集合到本地文件、代码格式化(JSON/XML/HTML) |
快捷键
| 快捷键 | 功能 |
|---|---|
Enter(输入框内) |
发送请求 |
Alt + N |
新建请求 |
Alt + C |
清空历史 |
Alt + P |
切换响应 Preview |
Backspace(非输入态) |
聚焦 URL 输入框 |
Q |
显示/隐藏环境快速查看面板 |
Esc |
失焦当前输入框 |
实现位置:js/requester.js 中的 pm.keymap(第 3337 行起)。
3. 技术栈
- UI / 框架:jQuery 1.x、jQuery UI(自动补全)、Bootstrap 2.x、Bootstrap Modal
- 模板 :Handlebars(运行时加载的是预编译产物
js/templates.js) - 编辑器:CodeMirror 2.x(请求体与响应体高亮、折叠、格式化)
- 工具库:Underscore、noty(消息提示)、jScrollPane、hotkeys、keyvalueeditor(自研键值编辑器插件)
- 加密 / 签名:crypto-js rollup(MD5 等)、jsSHA1 + OAuth.js(OAuth 1.0 签名)
- 存储:IndexedDB(业务数据)+ localStorage(设置与少量状态)
- 网络 :
XMLHttpRequest - 扩展平台:Chrome Extension Manifest V3(Service Worker 后台)
js/下的angular.min.js、backbone-min.js、jquery.tmpl.min.js、iscroll/等文件当前未被index.html引用,属于历史遗留依赖(js/modules/同理,见 4.2)。
4. 目录结构
text
post_man/
├── manifest.json # 扩展清单(Manifest V3)
├── index.html # 单页应用入口(请求区 / 响应区 / 侧边栏 / 各类弹窗)
├── _locales/en/messages.json # 国际化文案(name、description 等)
├── icon*.png # 扩展图标(16/32/48/128)
├── css/ # Bootstrap、jQuery UI、业务样式
├── img/ # 图片资源
├── codemirror/ # CodeMirror 编辑器及语言模式
└── js/
├── background.js # MV3 Service Worker:点击图标打开 index.html
├── requester.js # ★ 运行时主逻辑(约 224KB,所有 pm.* 模块的合并产物)
├── modules/ # 按模块拆分的源码(未被 index.html 加载)
├── templates.js # Handlebars 预编译模板
├── templates/*.handlebars # 模板源文件
├── jquery.keyvalueeditor.js # 键值对编辑器插件
├── httpstatuscodes.js # HTTP 状态码文案
├── httpheaders.js # 常用请求头常量
├── util.js # 通用工具函数
├── vkbeautify.js # JSON/XML 格式化
├── jsonlint.js # JSON 校验
├── dropbox/ # 早期云同步依赖(当前未加载)
├── noty/ # 消息提示
└── 第三方库(jquery / underscore / bootstrap / handlebars / codemirror ...)
4.1 关键文件说明
index.html:所有 UI 静态结构(请求构建区、响应区、History/Collections 侧边栏、Settings、About 等弹窗),末尾按顺序引入 30+ 个脚本,无内联脚本、无内联事件处理器,满足 MV3 CSP。js/requester.js:实际运行的业务代码,使用全局命名空间pm,包含下文所有模块;文件头注明 Apache License 2.0。js/background.js:MV3 后台 Service Worker,仅注册chrome.action.onClicked。js/modules/*.js:早期拆分版源码(如request.js、indexed_db.js),index.html并未引用 ;其内容与requester.js可能不同步,修改功能请以requester.js为准。js/requesterç的副本.js(文件名含乱码):打包前的备份副本,未被引用,可忽略。
5. 架构说明
5.1 运行模型
text
点击扩展图标
└─ Service Worker (js/background.js: chrome.action.onClicked)
└─ chrome.tabs.create(chrome.runtime.getURL('index.html'))
└─ 扩展页面 index.html
├─ 加载第三方库 + 预编译模板
├─ 加载 js/requester.js(定义 pm.* 全部模块)
└─ $(document).ready → pm.init()
pm.init()(js/requester.js 第 154 行)的初始化顺序:
js
pm.history.init(); // 历史
pm.collections.init(); // 集合
pm.settings.init(); // 设置(localStorage)
pm.layout.init(); // 界面布局与弹窗
pm.editor.init(); // CodeMirror 编辑器
pm.jsonlint.init(); // JSON 校验
pm.request.init(); // 请求构建与发送
pm.urlCache.refreshAutoComplete();
pm.helpers.init(); // Basic/Digest/OAuth1 Helper
pm.keymap.init(); // 快捷键
pm.envManager.init(); // 环境变量
pm.filesystem.init(); // 本地临时文件系统(导出/预览)
pm.indexedDB.open(); // 打开数据库并加载历史/环境/预设
pm.broadcasts.init(); // 公告(远程拉取已注释,始终离线)
5.2 模块一览(均在 js/requester.js)
| 模块 | 行号 | 职责 |
|---|---|---|
pm.broadcasts |
182 | 公告展示(远程接口已注释) |
pm.collections |
296 | 集合与集合请求的增删改查、导入导出、示例响应 |
pm.editor |
1107 | CodeMirror 初始化与换行等设置 |
pm.envManager |
1181 | 环境/Globals 管理与变量替换 |
pm.filesystem |
1631 | 通过 FileSystem API 写临时文件并新标签页打开 |
pm.headerPresets |
1765 | 请求头预设 |
pm.helpers |
1917 | Basic / Digest / OAuth1 认证 Helper |
pm.history |
2346 | 请求历史读写与条数裁剪 |
pm.indexedDB |
2514 | 数据库打开、迁移与各表 CRUD |
pm.jsonlint |
3328 | JSON 语法检查 |
pm.keymap |
3337 | 快捷键绑定 |
pm.layout |
3477 | 页面布局、弹窗、侧边栏、设置界面等 |
pm.request |
4121 | ★ 当前请求模型、发送、响应解析与渲染 |
pm.settings |
6308 | 设置项默认值、读写 localStorage |
pm.urlCache |
6470 | URL 自动补全缓存 |
6. 数据存储
6.1 IndexedDB
- 数据库名:
postman,版本:11(pm.indexedDB.open_latest) - Chrome < 23 时走旧版
open_v21(setVersion),现代浏览器不会进入该分支
| Object Store | keyPath | 索引 | 说明 |
|---|---|---|---|
requests |
id |
timestamp |
历史请求 |
collections |
id |
timestamp |
集合 |
collection_requests |
id |
timestamp、collectionId |
集合内的请求 |
environments |
id |
timestamp、id |
环境(含变量键值对) |
header_presets |
id |
timestamp |
请求头预设 |
6.2 localStorage
| Key | 说明 |
|---|---|
historyCount |
历史保留条数(默认 100) |
autoSaveRequest |
发送后是否自动保存历史 |
selectedEnvironmentId |
当前环境 ID |
lineWrapping / previewType / disableIframePreview |
编辑器与响应预览设置 |
retainLinkHeaders / sendNoCacheHeader |
请求头相关设置 |
usePostmanProxy / proxyURL |
代理模式与代理地址 |
variableDelimiter |
变量分隔符,默认 {{...}} |
languageDetection / haveDonated / v3BannerDismissed |
其它界面设置 |
lastRequest |
上次编辑中的请求(页面卸载时保存) |
globals |
全局变量(JSON) |
broadcasts / broadcast_last_update_time |
公告状态 |
设置项的默认值集中定义在
pm.settings.createSettings()(第 6314 行)。注意create()仅在 key 不存在时写入,get()始终从 localStorage 读取。
7. 核心流程
7.1 发送请求
入口:pm.request.send()(第 6160 行),主要步骤:
prepareForSending():收集 URL、方法、Headers、Body- 变量替换:
pm.envManager.getCurrentValue()按{{var}}替换 new URL(url)解析目标主机,调用chrome.permissions.request({ origins: ['http://host/', 'https://host/'] })动态申请可选主机权限 (Manifest 中声明为optional_host_permissions)- 创建
XMLHttpRequest,设置responseType、请求头与 Body 后send() onreadystatechange→pm.request.response.load():解析响应头、Cookies、正文- 若开启
autoSaveRequest,调用pm.history.addRequest()落库
补充:
- 带 Body 的方法集合:
POST / PUT / PATCH / DELETE / LINK / UNLINK - Body 模式:
form-data:构造FormData(可包含文件字段)urlencoded:application/x-www-form-urlencodedraw:CodeMirror 原始文本
- 不可设置的请求头 (
pm.bannedHeaders,第 84 行):cookie、host、user-agent、origin、content-length等浏览器禁止 XHR 修改的头。开启代理模式时,这些头会被加上Postman-前缀发送,响应中的Postman-Location会被还原为Location。
7.2 环境变量
- 环境数据存于 IndexedDB
environments,当前选中项记录在 localStorage - Globals 存于 localStorage
globals pm.envManager.containsVariable()/processString()按{{...}}模板替换;getCurrentValue()供 URL、Headers、Body 统一调用- 支持环境导入/导出(JSON 文件)
7.3 响应处理
loadHeaders():拆解响应头并排序renderCookies():从Set-Cookie解析 Cookie 列表setFormat():根据内容类型选择 JSON/XML/HTML 高亮与格式化- 三种查看模式:
parsed(Pretty)/raw/preview preview模式通过 FileSystem API 将响应写入临时文件,再以filesystem:URL 嵌入 iframe;二进制响应(如 PDF)同样走该路径- 可将响应保存为集合中的「示例响应」
7.4 导入 / 导出
- 集合导出:
pm.collections.saveCollection()→ JSON 文件 - 集合导入:本地文件或 URL(
importCollections/importCollectionFromUrl) - 集合上传:
uploadCollection()会 POST 到https://www.getpostman.com/collections(该远端接口已失效,见第 10 节)
8. 扩展配置与安全
8.1 Manifest 权限
json
{
"permissions": [],
"optional_host_permissions": ["http://*/", "https://*/"]
}
- 安装时不申请任何主机权限;首次向某主机发送请求时按需弹窗申请,符合 MV3 最小权限原则
- 由于需要读取任意 URL 的响应,
connect-src在 CSP 中放开为*
8.2 Content Security Policy(MV3)
json
"content_security_policy": {
"extension_pages": "default-src 'self'; script-src 'self'; object-src 'self'; connect-src *; style-src * 'unsafe-inline' blob: data:; img-src * data: blob:; frame-src * blob: data: filesystem:; font-src * data: blob:; media-src *;"
}
- 所有可执行脚本均在扩展包内(
script-src 'self'),无远程代码、无eval index.html无内联<script>、无onclick等内联事件处理器- 页面使用大量内联
style属性与动态样式,因此需要style-src 'unsafe-inline' - 响应预览/导出依赖
blob:、data:、filesystem:,iframe 预览依赖frame-src
8.3 数据与隐私
- 请求、集合、环境等全部保存在本地浏览器中,不上传
- Google Analytics 脚本在
index.html中已注释,未启用 - 早期 Dropbox 同步代码(
js/dropbox/)未接入
9. Manifest V2 → V3 升级说明
本次升级严格按 Chrome 官方扩展文档(Manifest V3)完成,改动如下:
| 项目 | 升级前(MV2) | 升级后(MV3) |
|---|---|---|
| 清单版本 | "manifest_version": 2 |
"manifest_version": 3 |
| 工具栏入口 | "browser_action" |
"action"(字段结构不变) |
| 后台脚本 | "background": { "scripts": ["js/background.js"] } |
"background": { "service_worker": "js/background.js" } |
| 可选权限 | "optional_permissions": ["http://*/", "https://*/"] |
"optional_host_permissions": ["http://*/", "https://*/"] |
| CSP | 字符串形式的 content_security_policy |
对象形式 { "extension_pages": "..." } |
| 离线标记 | "offline_enabled": true |
删除(MV3 不再支持该键) |
| 后台 API | chrome.browserAction.onClicked |
chrome.action.onClicked |
| 打开页面 | chrome.tabs.create({ url: 'index.html' }) |
chrome.tabs.create({ url: chrome.runtime.getURL('index.html') })(Service Worker 无页面基准地址) |
| 描述 | 无 | 增加 "description": "__MSG_description__" |
| 版本 | 0.8.4.22 |
0.9.0 |
9.1 为什么要用 chrome.runtime.getURL
Service Worker 中没有当前页面,chrome.tabs.create 的相对 URL 不再有可靠基准;显式使用 chrome.runtime.getURL('index.html') 可确保始终打开扩展内的页面。
9.2 MV3 兼容性核对
- 远程代码:无。所有 JS 均为包内文件,符合 MV3「所有逻辑必须随包发布」的要求
- Service Worker :
js/background.js只使用chrome.action/chrome.tabs/chrome.runtime,不依赖 DOM、window、localStorage,可直接作为 Service Worker 运行 - CSP :
script-src/object-src均为'self',未使用unsafe-eval、unsafe-inline(脚本层面) - 用户手势 :
chrome.permissions.request必须在用户手势中调用;send()由 Send 按钮或回车触发,满足要求。若后续改为定时/脚本自动发送,需要注意该限制 - 页面内联脚本 :
index.html已确认无内联脚本与内联事件处理器,无需改造 - 已知降级 :
pm.layout中的社交分享按钮(Twitter/Facebook/Google+)通过注入远程<script>实现,在 MV3 CSP 下会被拦截。该功能属于废弃的分享入口,不影响核心功能;如需保留,应改为普通外链<a>或移除
10. 已知限制
- 集合上传接口失效 :
pm.collections.uploadCollection()指向https://www.getpostman.com/collections,该服务已停止,上传会失败;本地导出/导入不受影响 - 云同步未接入 :
js/dropbox/未被index.html加载,无云端同步能力 - 发布到商店需调整 :
manifest.json保留了原扩展的key与update_url。若以新主体发布到 Chrome Web Store,应移除update_url(商店不接受该键)并确认key的使用符合要求;更换key会导致扩展 ID 变化、本地数据隔离 - 模块源码不同步 :
js/modules/*.js是早期拆分源码,运行时实际加载的是js/requester.js,两者可能存在差异 - 老版本依赖:jQuery 1.x、Bootstrap 2.x、Handlebars 1.x 等版本较旧,仅满足本地工具场景
- FileSystem API :导出/预览依赖
webkitRequestFileSystem(当前 Chrome 仍提供),若未来移除该 API,导出能力需要改为 Blob 下载方案 - 欢迎/公告数据 :
pm.broadcasts.fetch()已被注释,公告区域始终为空
11. 调试与验证
11.1 日常调试
- 在扩展页面按
F12打开 DevTools,可查看pm全局对象、IndexedDB、请求日志 - 在
chrome://extensions/中点击 Service Worker 的「检查」链接查看后台日志 - 修改
index.html/js/requester.js后刷新页面即可;修改manifest.json/background.js后需要在扩展管理页重新加载
11.2 打包
bash
google-chrome --pack-extension=/path/to/post_man --no-message-box
会生成 post_man.crx 与 post_man.pem(.pem 为私钥,请妥善保管,勿提交到仓库)。
11.3 本次升级的验证结果
在 Chrome 155 Headless 环境下通过 CDP Extensions.loadUnpacked 加载本项目,结果:
- 扩展安装成功,Service Worker
js/background.js正常注册 index.html初始化完成:pm对象存在、pm.request.send可用、IndexedDBpostman打开成功- 页面无
Runtime.exceptionThrown、无 console error/warning、无 CSP 拦截报错 - Service Worker 内
chrome.action存在且已注册onClicked,chrome.runtime.getURL('index.html')返回正确的扩展页地址
12. 许可证
js/requester.js 文件头包含 Apache License 2.0 声明(ASF 模板)。该代码源自 Postman 早期开源版本,如需再分发或商用,请自行确认原项目的授权条款与商标使用限制。