Rhai 脚本
Avalon 支持使用 Rhai 脚本进行高级请求处理和 URL 重写。Rhai 是一种简单、安全的脚本语言,专为嵌入 Rust 应用程序设计。
基本用法
[servers.routes.handle]
type = "script"
script = '''
// 访问请求信息
let path = request.path;
let method = request.method;
let host = request.host;
let client_ip = request.client_ip;
// 返回响应
response(200, "Hello, World!", #{})
'''
请求对象
| 属性 | 类型 | 说明 |
request.path | string | 请求路径 |
request.method | string | HTTP 方法 |
request.host | string | 请求主机名 |
request.client_ip | string | 客户端 IP |
request.query | string | 查询字符串 |
request.headers | map | 请求头 (键小写) |
内置函数
响应函数
| 函数 | 说明 | 示例 |
response(status, body, headers) | 返回自定义响应 | response(200, "OK", #{}) |
json_response(data) | 返回 JSON 响应 | json_response(#{ key: "value" }) |
redirect(url) | 302 重定向 | redirect("https://example.com") |
redirect_with_code(url, code) | 指定状态码重定向 | redirect_with_code("/new", 301) |
字符串函数
| 函数 | 说明 | 示例 |
url_encode(str) | URL 编码 | url_encode("hello world") |
url_decode(str) | URL 解码 | url_decode("hello%20world") |
base64_encode(str) | Base64 编码 | base64_encode("hello") |
base64_decode(str) | Base64 解码 | base64_decode("aGVsbG8=") |
regex_match(pattern, text) | 正则匹配 | regex_match("^/api/", path) |
regex_replace(pattern, repl, text) | 正则替换 | regex_replace("old", "new", text) |
JSON 函数
| 函数 | 说明 | 示例 |
json_parse(str) | 解析 JSON | json_parse('{"a":1}') |
json_stringify(obj) | 转为 JSON 字符串 | json_stringify(#{ a: 1 }) |
工具函数
| 函数 | 说明 | 示例 |
query_param(name) | 获取查询参数 | query_param("page") |
hash_md5(str) | MD5 哈希 | hash_md5("password") |
hash_sha256(str) | SHA256 哈希 | hash_sha256("data") |
time_now() | 当前 Unix 时间戳 | time_now() |
示例
API 网关路由
[servers.routes.handle]
type = "script"
script = '''
let path = request.path;
let api_key = request.headers["x-api-key"];
// 验证 API Key
if api_key != "secret-key-123" {
response(401, json_stringify(#{ error: "Invalid API key" }), #{
"Content-Type": "application/json"
})
} else if path.starts_with("/v1/users") {
// 转发到用户服务
#{ proxy: "user-service:8080" }
} else if path.starts_with("/v1/orders") {
// 转发到订单服务
#{ proxy: "order-service:8080" }
} else {
json_response(#{ error: "Not Found", path: path })
}
'''
A/B 测试
[servers.routes.handle]
type = "script"
script = '''
// 基于客户端 IP 的简单 A/B 测试
let ip_hash = hash_md5(request.client_ip);
let variant = if ip_hash.starts_with("0") || ip_hash.starts_with("1") {
"A"
} else {
"B"
};
response(200, "", #{
"X-AB-Variant": variant,
"Set-Cookie": "ab_variant=" + variant + "; Path=/; Max-Age=86400"
})
'''
健康检查端点
[servers.routes.handle]
type = "script"
script = '''
if request.path == "/health" {
json_response(#{
status: "ok",
timestamp: time_now(),
version: "1.0.0"
})
} else {
response(404, "Not Found", #{})
}
'''
请求日志
[servers.routes.handle]
type = "script"
script = '''
// 记录请求详情
let log_entry = #{
path: request.path,
method: request.method,
client_ip: request.client_ip,
timestamp: time_now()
};
// 继续代理
#{ proxy: "backend:8080" }
'''
速率限制响应
[servers.routes.handle]
type = "script"
script = '''
// 检查速率限制头
let remaining = request.headers["x-ratelimit-remaining"];
if remaining == "0" {
response(429, json_stringify(#{
error: "Too Many Requests",
retry_after: 60
}), #{
"Content-Type": "application/json",
"Retry-After": "60"
})
} else {
#{ proxy: "backend:8080" }
}
'''