请求重写

请求重写在转发路线上加一层可编排的改写:请求发出前、响应回来前,按规则改请求头、请求体或响应内容。

只处理非流式 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.* 输出会显示在结果区。没有生效的规则会逐条给出原因(禁用 / 已删除 / 本阶段没有动作 / 协议不匹配 / 该形态不支持),让「为什么没生效」当场可答。

调试

规则列表按顺序生效。每条的开关可以单独关掉,用于二分定位是哪条改坏了请求。改动保存后立即生效,新请求即可看到效果。


需要经由自己的网络出口发往上游时,看上游代理。