经典场景配方
把 concepts 与 scripting 组合成真实业务打法。每个场景给出:目标 → 流程 → 可直接照搬到界面的脚本 → 预期结果。脚本逻辑均经真实应用端到端验证。
示例基于本地测试服务端(
/oauth2/token、/protected、/echo、/json等)。连你自己的接口时换掉路径与前置 URL 即可。
场景1:登录取 Token、全组自动携带
目标:调登录接口拿 Token 存起来,之后所有接口自动带上它,无需手工复制。
请求 A —— 登录(后置脚本存 Token)
POST /oauth2/token,body 用表单:grant_type=password&username=qruser&password=qrpass&client_id=qr-client&client_secret=qr-secret
后置加一条脚本:
async function onResponse(context, url, response) {
var j = response.body.parseJSON();
if (j && j.access_token) {
sdk.env.set('authToken', j.access_token); // 存进运行期黑板
sdk.env.set('tokenType', j.token_type || 'Bearer');
}
return response;
}
请求 B —— 携带 Token(同一段脚本可复制到所有需鉴权接口)
GET /protected,前置加一条脚本:
async function onRequest(context, url, request) {
var tok = sdk.env.get('authToken'); // 从黑板取
var ty = sdk.env.get('tokenType') || 'Bearer';
if (tok) { request.headers.set('Authorization', ty + ' ' + tok); }
return request;
}
预期结果
| 操作 | 结果 |
|---|---|
| 先发 A(登录) | 200,响应含 access_token,Token 被存下 |
| 再发 B(携带) | 200,响应 {authorized:true, ...} |
| 关掉脚本再发 B | 401(前置不跑→没注入 Token,反证正是脚本带上的) |
关键:A、B 要在同一分组、绑同一环境(运行期变量按环境隔离),路径片段靠环境前置 URL 补全。
场景2:请求体 AES 加密、响应解密(多脚本链)
目标:发出 前对 body 做 AES 加密(线上只传密文),拿到响应后解密还原;顺带演示多条脚本串联、脚本间共享数据。
为什么能拆多条? 同一条链里各脚本共享
globalThis:脚本①把密钥/明文暂存起来,脚本②③直接取用。每条职责单一、可单独开关。
前置链
脚本①(准备上下文与密钥):
async function onRequest(context, url, request) {
globalThis.__traceId = $uuid.v4();
globalThis.__aesKey = '0123456789abcdef'; // 16 字节 → AES-128
globalThis.__aesIv = 'abcdef0123456789';
globalThis.__plain = request.body.parseText();
request.headers.set('X-Trace-Id', globalThis.__traceId);
return request;
}
脚本②(加密 body):
async function onRequest(context, url, request) {
var cipher = $crypto.encryptAES(globalThis.__plain, globalThis.__aesKey, globalThis.__aesIv);
request.body = cipher; // 线上只传密文
request.headers.set('X-Encrypt', 'AES-128-CBC');
return request;
}
(可选)脚本③叠加场景1 的 Token 携带。
后置链(解密还原 + 改写响应)
async function onResponse(context, url, response) {
var j = response.body.parseJSON();
var cipher = j && j.body && j.body.utf8; // /echo 把请求体回显在 body.utf8
var recovered = cipher ? $crypto.decryptAES(cipher, globalThis.__aesKey, globalThis.__aesIv) : '';
var obj = null; try { obj = JSON.parse(recovered); } catch (e) {}
response.body = JSON.stringify({
traceId: globalThis.__traceId, decrypted: recovered,
roundtripOk: recovered === globalThis.__plain, jsonValid: !!obj,
});
return response;
}
请求 body(加密前):{"order":123,"note":"secret-payload","amount":99.5}
预期结果
发送后返回:
{"traceId":"...","decrypted":"{\"order\":123,\"note\":\"secret-payload\",\"amount\":99.5}","roundtripOk":true,"jsonValid":true}
roundtripOk:true:解密结果与原文明细逐字节一致- 改写后的响应体在结果里可见(证实后置改写生效 )
⚠️
$crypto参数是位置参数字符串;失败只返回以Error:开头的错误串、不会抛异常/中断(实测:非法密文→Invalid ciphertext,错 key/iv→CryptoKit error)。中文/非 ASCII 明文实测可正常加→解密(旧版“非 ASCII 会中断”已证伪)。若需把解密结果写回 HTTP 头,请确保值为 ASCII(头值不容非 ASCII,否则抛TypeError);UTF-8 内容放 body/查询参数即可。
场景3:提取 · 断言 · 动态变量 · 签名
一组轻量自动化,多数无需写 JS,用声明式提取/断言步骤即可。
3.1 把响应字段传给下一个请求(提取)
- 请求A 后置加一条提取:变量
author、来源响应体JSON、表达式$.slideshow.author。 - 请求B 前置加一条脚本:
var v=sdk.env.get('author'); if(v){ request.headers.set('X-Extracted', v); } return request;
实测:请求B 的 /echo 回显 X-Extracted 与请求A 响应里的作者一致。
3.2 断言
在请求后置加断言步骤:状态码 = 200、$.slideshow.title 存在。结果逐条显示(通过/失败、期望 vs 实际)。
3.3 动态变量
参数/头/URL 里直接写:
GET /echo?ts={{$timestamp}}&r={{$randomInt}}&u={{$uuid}}
实测每次发送回显 ts 为当前时间戳、r 为不同随机整数、u 为合法 UUID。全集见 concepts 动态变量。
3.4 签名(MD5,标准可互操作)
很多开放 API 用 MD5(参数拼接 + secret) 签名。用已验证为标准的 $hash.md5:
async function onRequest(context, url, request) {
var appId = sdk.env.get('appId');
var secret = sdk.env.get('appSecret'); // 敏感值放环境变量
var ts = String(Math.floor(Date.now() / 1000));
var sign = $hash.md5(appId + ts + secret); // 拼接顺序按后端约定
request.queries.set('appId', appId);
request.queries.set('ts', ts);
request.queries.set('sign', sign);
return request;
}
实测 $hash.md5('abc') = 900150983cd24fb0d6963f7d28e17f72,与后端一致。
✅ 原生
$hmac($hmac.sha256(message, key)等)经逐向量核对与后端 标准 RFC HMAC 一致(注意参数序:数据在前、密钥在后),可直接用于对外签名;$hash系列同样标准。只有$jwt对对象 payload 会退化成[object Object](需先JSON.stringify,或改服务端签)。
场景4:用库脚本与 require() 复用公共逻辑
目标:把"注入 Token""统一加签""日志埋点"抽成可复用单元,多处引用、集中维护。
两种复用方式:
- 库脚本步骤:自带
onRequest/onResponse的完整脚本——作为步骤添加后按顺序执行、与其它步骤共享globalThis。 require():把纯函数存成脚本库并用module.exports导出,在任意脚本中加载——适合加签工具、编解码器等公共工具函数:
// 脚本库 "sign-lib"