Skip to main content

脚本能力(开发者视角)

前置/后置脚本是快速请求的"瑞士军刀":动态签名、加解密、条件断言、跨请求传值都靠它。脚本用 JavaScript 编写,运行在内置 QuickJS 引擎里。

本篇只讲你在界面里能加什么、脚本能干什么;完整的 JavaScript API 签名见脚本 API 参考。


一个请求有两个"操作链"​

在请求编辑页,你可以往前置和后置各加若干条步骤,按顺序执行。每个步骤是这几种之一:

步骤类型界面里做什么典型用途
脚本写一段 JavaScript改请求头/body、加签、解密、条件逻辑
库脚本引用一个保存好的公共脚本多处复用"注入 Token""统一埋点"
提取从响应里取一个值存成变量把登录返回的 token 传给下一个请求
断言对响应提一条校验状态码=200、某字段存在
等待停若干毫秒限流、轮询间隔

脚本怎么写:两个入口​

脚本引擎在你的脚本里查找约定好的入口函数:

// 前置:能改"将要发出的请求"
async function onRequest(context, url, request) {
// 例如:注入一个头
request.headers.set('X-Trace-Id', $uuid.v4());
return request; // 必须把 request 返回
}

// 后置:能读、能改"拿到的响应"
async function onResponse(context, url, response) {
// 例如:把密文响应改写回明文供人看
return response;
}
  • 前置链里写 onRequest,后置链里写 onResponse。
  • 函数名必须是这两个,否则被判"无入口、跳过"。
  • 支持 await。

脚本里能改什么​

改请求(onRequest 里的 request)​

你想怎么写
设置/覆盖请求头request.headers.set('Authorization', 'Bearer ' + tok)
加/改查询参数request.queries.set('ts', String(Date.now()))
读原始 bodyvar text = request.body.parseText() 或 request.body.parseJSON()
整体改写 bodyrequest.body = 新的字符串(赋值即可)

改/读响应(onResponse 里的 response)​

你想怎么写
读状态码response.statusCode
读响应头response.headers.get('Set-Cookie')
读 bodyresponse.body.parseJSON() / .parseText()
整体改写响应体response.body = 新的字符串——界面/返回里显示的就是改写后的内容(实测)

现成的能力,不用自己造轮子​

脚本里有一批内置工具(sdk.*)和以 $ 开头的原生能力,加解密、摘要、编码、随机、JSONPath 都齐:

能力用什么
读/写变量(跨请求传值)sdk.env.get(name) / sdk.env.set(name, value)
声明测试sdk.test('状态应为200', function(){ sdk.expect(response.statusCode).toBe(200) })
JSONPath 取值sdk.jsonPath('$.data.id')
AES 加解密$crypto.encryptAES(明文, key[, iv]) / $crypto.decryptAES(密文, key[, iv])
MD5/SHA 摘要(签名)$hash.md5(...) / $hash.sha256(...)(返回 hex,标准可互操作)
UUID$uuid.v4()
字符串 base64sdk.utils.base64Encode(str) / base64Decode(str)

⚠️ 引擎是 QuickJS,没有浏览器的 crypto.subtle,且 crypto.getRandomValues 在本引擎不生效(不填充入参)——要 AES 就用 $crypto,要随机数用 crypto.randomUUID/$uuid,别去写 WebCrypto。完整签名与注意事项见脚本 API 参考。


require 引用脚本库​

公共逻辑除了作为步骤添加,还可以用 CommonJS 风格的 require() 加载。引用键固定为 @libs/<键>,按以下顺序解析:

  1. 脚本库 identifier —— 精确匹配,重命名不受影响(推荐)
  2. 脚本库 name —— 可读,但重命名库会断链(重名歧义会跳过)
  3. 内置库 —— 随应用内置分发

内置库​

键导出说明
@libs/base64encode(str) / decode(text)UTF-8 安全的 Base64(中文、emoji 安全);decode 忽略空白,非法序列按 Latin-1 保留
@libs/hexencode(str) / decode(text)UTF-8 安全的小写十六进制;decode 忽略非 hex 字符,奇数长度抛错

两者同时暴露全局别名(base64.encode(...) / hex.encode(...))——require 只是提供了显式导入风格。

const { encode } = require('@libs/base64');
encode('hello 中文'); // 'aGVsbG8g5Lit5aSp'

你自己的脚本库​

把可复用函数存成脚本库(用 module.exports 导出,无需入口函数),在任意前置/后置脚本中引用:

// 脚本库 "sign-lib"
function sign(method, path, body, secret) {
return $hmac.sha256(method + path + body, secret);
}
module.exports = { sign };
// 任意请求脚本
async function onRequest(context, url, request) {
const { sign } = require('@libs/sign-lib');
request.headers.set('X-Sign', sign('POST', request.path, request.body.parseText(), sdk.env.get('secret')));
return request;
}
  • 在库管理列表用复制 require 语句获取稳定的 identifier 形态。
  • 库内可以再 require 其他库——整个依赖闭包自动打包(循环引用按 Node 语义返回部分 exports,不会死循环)。
  • 模块缓存在每次执行时重建:库不会跨请求残留状态。
  • 想复用 npm 包:先用 esbuild --format=iife(或 Browserify/Webpack)打成单文件,导入为脚本库后 require('@libs/<名称>')。选择纯 JS、零 Node 依赖(不得用 Buffer/fs/crypto/process)、不依赖 DOM 的浏览器单文件构建。
  • 引用键未命中会抛 Error: require: 未找到库 [...]——前置失败中止发送,后置失败保留原始响应。注释里的 require 不会参与解析。

完整 require 语义见脚本 API 参考。


多条脚本怎么配合​

一个请求里放多条脚本时,它们共享同一份 globalThis(实测)。于是可以:

好处:每条脚本职责单一、可单独开关。"库脚本"也是完整脚本(自带 onRequest/onResponse)——作为步骤添加可整段运行;也可以在里面只放纯函数并用 module.exports 导出,脚本中 require('@libs/<键>') 加载(见 require 引用脚本库)。

具体串联示例(登录取 Token、AES 加解密)见 recipes。