请求重写
请求重写在转发路线上加一层可编排的改写:请求发出前、响应回来前,按规则改请求头、请求体或响应内容。
只处理非流式 JSON
重写只作用于非流式 JSON请求。流式(SSE)与非 JSON 内容不参与重写。
两个阶段
| 阶段 | 时机 | 作用对象 | 当前状态 |
|---|---|---|---|
| 请求 | 请求发往上游之前 | 请求头、请求体 | 可用 |
| 响应 | 上游返回之后、回给客户端之前 | 响应头、响应体 | 已关闭 |
响应阶段当前关闭
流式(SSE)响应无法在事件级改写,而它占绝大多数流量——一条只在非流式响应上生效的规则,实际几乎不会触发。因此响应阶段整段关闭:编辑器不再提供这个阶段,保存或试跑带响应阶段的规则会被拒绝。能力本身已实现,补齐流式改写后再开放。
一条规则
一条规则由「匹配条件 + 操作」组成:
- 匹配:命中哪些请求才应用(客户端协议与上游协议)。
- 操作:
set:设置一个字段。append:追加一个值(仅请求头)。remove:移除一个字段。replace:替换字段中匹配的部分(支持正则,仅 Body)。
每个操作指定目标(请求头 / 请求体字段 / 脚本)与值。
内置模板
「新建规则」是一个下拉,除空白规则外还提供几个动作已填好的草稿,套用后可直接点「运行测试」看到效果:
| 模板 | 做什么 |
|---|---|
| 修改 User-Agent | 覆盖请求头 User-Agent,默认值 OSW/<应用版本号>。 |
| 移除请求头 | 删掉指定的请求头,例如本地会话 Cookie。 |
| 设置请求字段 | 给 JSON 请求体写入固定值,路径不存在时自动创建。 |
| 按脚本条件改写 | 先读请求内容再决定改不改,演示脚本动作。 |
模板只填草稿,不落库、不生效,保存后与手写规则完全等价。
脚本动作
结构化动作表达不了「读完内容再决定怎么改」这类逻辑(例如「只有正文里出现过某个标记,才把 temperature 改成 0」)。脚本动作用一小段自己的代码补上这块:它读当前阶段报文,交回改写结果。
脚本以函数体形式书写——不写 function 包装,直接写语句、直接 return:
// 只有请求里出现 "apply-strict" 时,才把 temperature 压到 0;
// 没命中就不 return,这条动作什么都不改。
if (JSON.stringify(body || {}).includes('apply-strict')) {
return { body: { ...body, temperature: 0 } }
}脚本能看到这些名字(没有 require / process / 定时器 / 网络 / 文件系统):
| 名字 | 含义 |
|---|---|
body | 当前阶段报文的解析结果;不是合法 JSON 时为 null |
headers | 当前阶段报文的 Header(只读副本) |
protocol | { stage, clientProtocol, upstreamProtocol },只读 |
get(path) | 按点号路径取值,[*] 表示数组投影(如 messages[*].role);取不到返回 undefined。不写 $. 前缀(写成 $.a 会取不到),这点与上面的结构化 Body 动作不同 |
console.log/warn/error(...) | 写入「运行测试」的日志区,最多 50 行 |
要交回什么
return { body, headers }:交回谁就整体替换谁——脚本没写的字段 / 键即视为删除。- 不
return,或返回的对象里两个键都没出现:这条动作不改动报文。 - 返回数字、字符串、数组等其它类型:规则失败。
正因为交回即整体替换,删掉一个字段才写得出来——交回完整 body,但不写那个键:
// 交回完整 body,但把 metadata 这个键丢掉 —— 整体替换之下它就被删了。
const { metadata, ...rest } = body || {}
return { body: rest, headers: { ...headers } }报文不是合法 JSON 时不直接报错,而是把 null 交给脚本,由脚本自己决定要不要 throw——「是不是 JSON」本身可能就是脚本要判断的事。
能做与不能做
- 受保护的 Header 改不了:
Authorization、Host、Content-Length、Connection、Transfer-Encoding。按改动前后取值是否不同判定,改了就规则失败,原样带回去不算违规。 - 投递形态字段改不了:
stream,同样按改动前后取值判定。 - 单次执行有超时:默认 1000 毫秒,最大 5000 毫秒,死循环会被中断而不是挂住代理。
- 只对完整 JSON 报文生效:与结构化动作一致,流式响应不参与。
- 沙箱里没有
eval与new Function。它防的是手滑与死循环,不是抵御恶意代码。
起手与调试
切到脚本时,编辑器会填入一段能直接跑通的基线脚本;动作行里的插入示例会按当前阶段列出可选示例,选了就整段替换编辑器内容。切换报文阶段时,若编辑器里仍是原阶段的基线(你没动过),它会跟着换成新阶段对应的基线。
在「运行测试」里加一条用例(选阶段、传输形态,填一段报文),点运行即可看到改写后的 Header 与 Body;脚本里的 console.* 输出会显示在结果区。没有生效的规则会逐条给出原因(禁用 / 已删除 / 本阶段没有动作 / 协议不匹配 / 该形态不支持),让「为什么没生效」当场可答。
调试
规则列表按顺序生效。每条的开关可以单独关掉,用于二分定位是哪条改坏了请求。改动保存后立即生效,新请求即可看到效果。
需要经由自己的网络出口发往上游时,看上游代理。