ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

在 Scalar API Client 中执行 Pre-Request 与 Post-Response 脚本:基于 Postman Sandbox 的 OpenAPI 脚本插件实战

在 Scalar API Client 中执行 Pre-Request 与 Post-Response 脚本:基于 Postman Sandbox 的 OpenAPI 脚本插件实战 在 Scalar API Client 中执行 Pre-Request 与 Post-Response 脚本基于 Postman Sandbox 的 OpenAPI 脚本插件实战【免费下载链接】scalarScalar is an open-source API platform: Modern REST API Client Beautiful API References ✨ 1st-Class OpenAPI/Swagger Support项目地址: https://gitcode.com/GitHub_Trending/sc/scalar本文围绕scalar/pre-post-request-scripts这一开源包展开讲解如何在 Scalar API Client桌面端与 Web 端中以插件方式为 OpenAPI 文档中的请求注入发送前脚本pre-request script与响应后脚本post-response script。读完本文你将掌握该包的安装接入方式、Postman 语义的脚本运行时原理、内置示例脚本的用法以及底层 iframe 沙箱、变量回写与测试结果展示的实现机制并能在自己的 Scalar 集成中直接编写和调试这类脚本。一、包定位为 API Client 补充脚本能力Scalar 是一个开源的 API 平台提供现代 REST API 客户端、美观的 API 文档以及一流的 OpenAPI/Swagger 支持。scalar/pre-post-request-scripts是其中负责脚本执行能力的独立包它接收 OpenAPI 文档中通过x-pre-request与x-post-response扩展字段声明的脚本在请求发出前与响应返回后执行并像 Postman 一样把pm.test断言结果展示出来。从包的导出入口 packages/pre-post-request-scripts/src/index.ts 可以看到它对外暴露的能力非常聚焦export { getScript } from ./helpers/get-script export { executePostResponseScript, executePreRequestScript } from ./libs/execute-scripts即一个脚本合并工具函数getScript以及两个执行器executePreRequestScript/executePostResponseScript。这个包本身不强制绑定 UI官方定位是作为scalar/api-client的插件使用README 中明确说明 This is intended to be used as a plugin with scalar/api-client。二、安装包以标准 npm 包形式发布需要 Node.js 22 及以上版本见 packages/pre-post-request-scripts/package.json 的engines字段。在项目中安装npm install scalar/pre-post-request-scripts安装后该包会同时引入postman-sandboxPostman 官方沙箱运行时、postman-collectionPostman Collection 数据结构以及 Vue 相关的依赖vue、scalar/components等这些依赖共同支撑起脚本的解析与执行。三、快速接入将插件挂载到 API Client在 Web 端通过scalar/api-client提供的createApiClientWeb初始化客户端并将脚本插件加入plugins数组即可import { createApiClientWeb } from scalar/api-client/layouts/Web import { postResponseScriptsPlugin } from scalar/pre-post-request-scripts import scalar/api-client/style.css createApiClientWeb( document.getElementById(app), { proxyUrl: https://proxy.scalar.com, // Load the plugin plugins: [ postResponseScriptsPlugin() ], }, )从源码结构看真正负责统一脚本插件实现的是 packages/pre-post-request-scripts/src/plugins/request-scripts/request-scripts-plugin.ts 中定义的requestScriptsPluginREADME 用法示例中对外暴露的插件函数为postResponseScriptsPlugin。该插件注册了两类 UI 组件与三个生命周期钩子组件request.componentScriptsSection一个折叠面板内含Pre-request与Post-response两个脚本编辑器response.componentTestResults用于展示响应后脚本的断言结果。钩子onRequestMount当某个 operation 挂载时检查它是否带有脚本若有则预热沙箱prewarmSandboxFrame避免首次执行时等待加载sandbox.html与postman-sandbox包产生明显冷启动延迟beforeRequest清空上一轮测试结果执行 pre-request 脚本responseReceived先保存本轮 pre-request 的结果快照再执行 post-response 脚本并把两部分结果合并展示。其中ScriptsSection组件的实现位于 packages/pre-post-request-scripts/src/plugins/request-scripts/components/ScriptsSection.vue它通过operation[x-pre-request]与operation[x-post-response]两个字段读写脚本内容并在任一脚本非空时显示一个绿色圆点指示器同时内置示例脚本面板方便快速插入。四、脚本运行时Postman 官方沙箱语义这是该包最关键的设计决策脚本不是用自定义解释器执行的而是跑在 Postman 官方开源的postman-sandbox运行时中。因此脚本内可用的pmAPI 行为与 Postman 完全一致pm.test(name, fn)定义一个测试用例pm.expect(actual)断言入口语法遵循 Postman 内置的断言风格.to.have.status、.to.include、.to.be.an、.to.have.jsonSchema等pm.response当前响应对象提供pm.response.status、pm.response.code、pm.response.headers、pm.response.text()、pm.response.json()等pm.request当前请求对象Postman Collection 形态而非浏览器 Fetch API 的 Request。脚本作为 Postman 的test脚本运行post-response 场景pre-request 场景则对应prerequest监听类型见 packages/pre-post-request-scripts/src/libs/execute-scripts/postman-adapter/sandbox-adapter.ts 中listen: type pre-request ? prerequest : test的取值。执行产生的测试结果通过插件实时回传并渲染在响应的 Tests 面板中。4.1 执行链路两个执行器 execute-pre-request-script.ts 与 execute-post-response-script.ts 的职责非常对称executePreRequestScript(script, { requestBuilder, variablesStore, onTestResultsUpdate })构造pm.request由RequestFactory构建执行脚本并把脚本对请求头、请求方法的修改合并回原请求executePostResponseScript(script, { requestBuilder, response, variablesStore, onTestResultsUpdate })额外接收一个浏览器Response对象序列化后作为pm.response提供给沙箱。两者都先判断脚本为空则直接返回再统一走executeInPostmanSandbox完成真正的执行。4.2 响应数据的字节级传递为了让pm.response在沙箱内所见即所得宿主端会把Response序列化为 Postman 定义的响应结构code、status、header、stream。这里有一个容易被忽略的工程细节toPostmanResponse使用response.arrayBuffer()而不是response.text()读取正文——因为 gzip 压缩的 JSON、图片、PDF、protobuf 等二进制负载经过postMessage时需要逐字节保留若先text()解码再TextEncoder编码任何非 UTF-8 序列都会因替换字符UFFFD而被破坏。源码注释明确记录了这一点。4.3 沙箱隔离与安全postman-sandbox依赖eval执行脚本。为了让主应用保持不含unsafe-eval的严格 CSP脚本执行被委托给一个同源 iframe该 iframe 加载一个独立的sandbox.html拥有自己的宽松 CSP宿主与 iframe 之间通过postMessage通信。这带来几层保障宿主端sandbox-adapter.ts绝不 importpostman-sandbox只做序列化、消息转发与结果回写iframe 在 Web 端锚定到站点根路径/sandbox.html在 Electron 端则与文档同目录解析sandboxFrameUrl()通过isElectron()区分两种解析策略所有收发的消息都校验event.origin与event.source必须来自自己的 iframe防止同源环境中的其他窗口伪造done/test-results消息注入恶意变量或请求两套超时兜底iframe 就绪握手最长等待 30 秒防止文档加载成功但脚本从未广播ready时 promise 永久悬挂单次脚本执行最长等待 5 分钟作为 iframe 崩溃时的熔断器postman-sandbox内部另有自己的脚本级超时。五、脚本从哪来多层级合并规则OpenAPI 文档可以在两个层级声明脚本插件会将其拼接合并后执行。合并逻辑在 packages/pre-post-request-scripts/src/helpers/get-script.tsexport const getScript (...args: (string | undefined | null)[]): string { return args .map((script) script?.trim()) .filter((script) typeof script string script.length 0) .join(\n) }插件在beforeRequest与responseReceived钩子中分别这样取脚本// pre-request文档级 操作级 const script getScript(document[x-pre-request], operation[x-pre-request]) // post-response文档级 操作级 const script getScript(document[x-post-response], operation[x-post-response])也就是说你可以在document 根级作用于整份文档的所有请求和operation 级仅作用于单个接口同时定义脚本两者会按传入顺序拼接执行空值与 undefined 会被自动剔除。这提供了一种全局公共脚本 接口私有脚本的组织方式。六、编写脚本内置示例与常用断言为了让使用者快速上手包内置了一批可直接复用的示例脚本定义在 packages/pre-post-request-scripts/src/consts/example-scripts.ts。以下是其中的核心范例检查状态码pm.test(Status code is 200, () { pm.response.to.have.status(200) })校验 JSON 响应pm.test(Response is valid JSON, () { const responseData pm.response.json() pm.expect(responseData).to.be.an(object) })校验响应头pm.test(Content-Type header is present, () { pm.response.to.have.header(Content-Type) })校验 JSON Schemapm.test(Response matches schema, () { const schema { type: object, required: [id, name], properties: { id: { type: number }, name: { type: string } } } pm.response.to.have.jsonSchema(schema) })校验响应正文内容pm.test(Response body contains string, () { pm.expect(pm.response.text()).to.include(success) })断言状态码属于某一集合pm.test(Successful POST request, () { pm.expect(pm.response.code).to.be.oneOf([201, 202]) })校验响应头取值pm.test(Content-Type is JSON, () { pm.expect(pm.response.headers.get(Content-Type)).to.include(application/json) })断言字段数据类型pm.test(Response data types are correct, () { const data pm.response.json() pm.expect(data).to.be.an(object) pm.expect(data.name).to.be.a(string) pm.expect(data.age).to.be.a(number) pm.expect(data.hobbies).to.be.an(array) })断言对象包含pm.test(Response contains expected object, () { const expected { created: true, errors: [] } pm.expect(pm.response.json()).to.deep.include(expected) })这些示例与 UI 中的Example Scripts面板一一对应每个示例还附带 mock 响应数据status、body、headers方便在编辑器里立即试跑。七、结果回写变量与请求修改脚本运行结束后沙箱会把两类副作用回写到宿主变量更新若脚本修改了pm.variables/pm.collectionVariables/pm.globals/pm.environment沙箱在done消息中携带各作用域的变量条目宿主端通过variablesStore.setLocalVariables、setCollectionVariables、setGlobals、setEnvironment依次写回对应 variables-store.ts 的转换逻辑。请求修改pre-request 脚本对请求头、请求方法的修改通过syncPlainPostmanRequestToRequestFactory合并回RequestFactory从而影响即将发出的真实请求。八、测试结果面板与状态机执行pm.test产生的结果会被汇总为TestResult数组其结构定义在 execute-post-response-script.tsexport type TestResult { title: string passed: boolean duration: number error?: string status: pending | passed | failed }渲染组件 TestResults.vue 按passed/pending/failed三类过滤统计并计算整体状态全部通过则为passed存在未决项则为pending否则为failed同时累加所有用例的duration得到总耗时与逐条结果一起展示在响应区域的 Tests 面板中。九、源码级验证测试用例说明了什么包内附带的测试 execute-post-response-script.test.ts 验证了脚本执行的四个关键行为pm.test(Status code is 200)对 200 响应通过、对 500 响应失败结果对象中的passed与status字段随之翻转pm.response.to.have.header(Content-Type)形式的响应头断言可以正常工作脚本在pm.test之外抛出异常如throw new Error(kaboom)时会被包装成一条标题为 Script Execution、状态为failed、携带error信息的测试结果而不会让整个请求流程崩溃。测试通过registerInProcessSandbox()注册进程内沙箱传输层使 Node 环境的单测无需 DOM 也能运行真实的postman-sandbox执行逻辑——这与生产环境默认的 iframe 传输层可通过setSandboxTransport替换相互独立也再次印证了宿主不直接依赖 postman-sandbox的架构边界。十、适用前提与限制该包面向scalar/api-client的插件体系设计独立使用需要自行搭建插件挂载环境脚本运行环境是 Postman 官方沙箱因此pmAPI 的可用范围以 Postman 语义为准浏览器原生 API如fetch在沙箱内不可直接使用沙箱执行依赖 iframe 与postMessage在纯 SSR 环境中不可用Electron 与 Web 场景的沙箱页面解析策略不同由isElectron()自动区分执行时长与就绪时长分别受 5 分钟与 30 秒的超时保护长时间阻塞脚本会触发熔断并被报告为执行错误。总体而言scalar/pre-post-request-scripts通过OpenAPI 扩展字段声明脚本 Postman 官方沙箱执行 插件钩子驱动的三层设计把 Postman 生态中成熟的脚本能力无缝引入了 Scalar API Client你既可以在 OpenAPI 文档中声明可移植的请求前/响应后逻辑又能享受到pm.test断言带来的自动化校验体验而底层 iframe 隔离与消息校验则保证了脚本执行的安全边界。【免费下载链接】scalarScalar is an open-source API platform: Modern REST API Client Beautiful API References ✨ 1st-Class OpenAPI/Swagger Support项目地址: https://gitcode.com/GitHub_Trending/sc/scalar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进