通过插件 · Native脚本
native 暴露小程序与 mmtls / ilink 层之间的交互回调。典型场景包括:
- 云网关调用
- 云托管
- 云容器
- 其它封装在 mmtls/ilink 之上的服务端能力
这些请求的初始化或部分步骤并不走 HTTP,所以不能只靠 Network 页观察。native 在 mmtls/ilink 的 JSON 边界提供请求/响应回调,插件可以只读观察,也可以在请求发出前、响应交给业务前改写。
挂在 wadLogicLayer.native 与 wadPanelLayer.native 上;渲染层的 native 注册类同步抛错、查询类 Promise.reject。宿主不解析业务信封 —— 插件拿到的是完整原始 JSON 对象,自己判断业务形态。
本页你将学到
- intercept(改写)与 observe(只读)的区别与层限制;
- 请求/响应信封的结构;
- 面板 observe 大信封截断的回查方式;
- 云网关捕获的纯插件实现思路。
API 总表
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
intercept.request(fn) / intercept.response(fn) | ✅ 改写 | ✋ 同步抛 E_LAYER_ONLY | ✋ 同步抛 E_LAYER_ONLY |
observe.request(fn) / observe.response(fn) | 👁 只读 | ✋ 同步抛 | 👁 只读 |
records(n) / get(id) / clear() | ✅ | ❌ reject | ✅ |
事件信封
请求与响应事件是完整原始信封:
// 请求事件(发出前)
{
id: '17', // 请求序号(只读)
appid: 'wx1234...', // 小程序 appid(只读)
data: { /* 完整 mmtls/ilink 业务信封 JSON 对象 */ }
}
// 响应事件
{
id: '17',
appid: 'wx1234...',
req: { id: '17', appid: '...', data: {} }, // 最终发出的请求(只读)
data: { /* 完整响应信封 JSON 对象 */ }
}id / appid 只读;data 可改(仅 intercept)。注意:回调事件本身没有 phase 字段;phase 只出现在 records() 的归一化条目里。
intercept.request(fn)
逻辑层专用。在请求发出前改写。返回值语义:
| 返回 | 含义 |
|---|---|
undefined / null / '' | 不修改 |
| 对象 | 采纳其 data(返回完整事件对象即可) |
| 其他类型 | 记插件日志,不修改 |
| 抛异常 | 该请求不发出,小程序收到错误 JSON;错误写进插件日志 |
// <script type="text/wad-logic">
wadLogicLayer.native.intercept.request(function (req) {
const root = req.data;
if (!root) return; // 不认识的形态直接放行
// 宿主不拆信封 —— 自己按业务形态判断;这里只演示“给某个 mmtls/ilink 调用加标记”
if (root.api_name !== 'your_cloud_api') return;
root.trace = 'plugin';
return req; // 返回对象 = 采纳修改
});注意点:返回解绑函数;多个插件、多个 hook 按注册顺序链式(前一个的输出是后一个的输入)。
intercept.response(fn)
逻辑层专用。在响应交给小程序前改写。
wadLogicLayer.native.intercept.response(function (resp) {
if (resp.data && resp.data.ret === -1) {
resp.data.ret = 0; // 改写响应
return resp;
}
});注意点:响应 hook 抛异常时,小程序收到 {"data":"{\"error\":{...}}"},statusCode 语义保留,错误写插件日志。
observe.request(fn) / observe.response(fn)
只读观察,不能修改。逻辑层与面板层都可用。
// 逻辑层
wadLogicLayer.native.observe.response(function (resp) {
wadLogicLayer.log('[native] id=' + resp.id + ' keys=' + Object.keys(resp.data || {}));
});
// 面板:订阅实时事件(大信封 >180KB 会被截断;records/get 单条 body >96KB 也会截断并标记)
wadPanelLayer.native.observe.response(function (resp) {
row(resp.id, JSON.stringify(resp.data).slice(0, 120));
});records(n) / get(id) / clear()
逻辑层与面板层可用;渲染层 Promise.reject。
逻辑层与面板层返回同一形状的归一化数组:
(async () => {
// 逻辑层 / 面板层均可
const items = await wadPanelLayer.native.records(100);
// items = [{ id:'17', phase:'request'|'response', data:{...完整信封...}, at:1699... }, ...]
const hit = await wadPanelLayer.native.get('17'); // 找不到返回 null
await wadPanelLayer.native.clear(); // Promise<{ok:boolean}>
})();实战:云网关 Key 捕获 + WASM 解密
云网关的 get_service_info 响应会下发 key。插件只观察响应并保存 key,然后用 gw.wasm 解密捕获到的请求/响应;不需要改写请求,也不依赖服务端明文下发。
(async () => {
const wasm = await wadLogicLayer.assets.wasm('assets/gw.wasm');
const gwWasm = wasm.exports;
// 1) 观察 get_service_info 响应:提取 key/token/domain/过期时间
wadLogicLayer.native.observe.response(function (resp) {
const text = typeof resp.data === 'string' ? resp.data : JSON.stringify(resp.data || {});
if (text.indexOf('get_service_info') < 0) return;
const cred = gwWasm.wad_extract_key(text); // 插件自带 wasm 解析
if (!cred || !cred.key) return;
const item = {
key: cred.key, token: cred.token || '', rid: cred.rid || '',
domain: cred.domain || '', expireAt: Date.now() + 30 * 60 * 1000
};
wadLogicLayer.sharedStorage.global.set('gwCreds:' + item.domain, item);
wadLogicLayer.sharedStorage.global.set('gwCreds:last', item);
});
// 2) 按 domain 取 key,用 wasm 解密捕获到的请求体
const recs = await wadLogicLayer.capture.records(120);
for (const r of recs) {
const host = (r.url || '').replace(/^https?:\/\//, '').split('/')[0];
const saved = await wadLogicLayer.sharedStorage.global.get('gwCreds:' + host);
if (!saved || saved.expireAt < Date.now()) continue;
if (r.requestBodyB64 !== true || !r.requestBody) continue;
const plain = gwWasm.wad_v3_decode(saved.key, String(r.requestBody), 0);
wadLogicLayer.log('[gateway]', host, String(plain).slice(0, 200));
}
})();完整可安装实现见插件市场的「云网关捕获」(gateway-capture 1.0.0)。
限制
- 覆盖云网关、云托管、云容器等 mmtls/ilink 交互;普通 HTTP 请求请用 Network 页,原生 UI 弹窗/本地 API 不在此面
- Native hook 有时间预算(单插件 260ms、总 900/600ms):hook 里别做重活,复杂计算放 wasm/异步
- 面板推送 >180KB 截断;
records/get单条 body >96KB 也会截断并带truncated:true。需要完整原始体时请用 Network 抓包或分片获取
下一步
- 网络抓包与改写 capture:普通 wx.request 流量走这里
- 通过插件 · 网络重写:不改代码就能改流量的声明式方式