路由选项
在 APISIX 中,有三个 HTTP 路由选项:
-
radixtree_host_uri按主机和 URI 路径路由请求,在匹配期间优先考虑主机名而不是 URI 路径。这是默认设置,行为与 NGINX 相同。 -
radixtree_uri按主机和 URI 路径路由请求,在匹配期间优先考虑 URI 路径而不是主机名。 -
radixtree_uri_with_parameter支持在路径匹配中使用参数。
这些路由选项可以在 conf/config.yaml 中的 apisix.router.http 下配置。
radixtree_host_uri
这是默认的路由设置,在路由匹配时优先考虑主机名而不是 URI 路径。如果你想显式配置该选项,请将以下块添加到你的配置文件中:
apisix:
router:
http: radixtree_host_uri
重新加载 APISIX 以使更改生效。
配置两个具有相同 URI 但匹配主机不同的路由对于多租户 SaaS 平台等场景很有用,其中每个租户都通过自定义子域提供服务。例如,两个租户都可以访问相同的端点,但根据主机将请求路由到不同的上游服务。
为此,你可以配置两个具有相同匹配 URI 但匹配主机和上游服务不同的路由:
curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "httpbin-host-test1",
"uri": "/get",
"host": "test1.com",
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "postman-host-test2",
"uri": "/get",
"host": "test2.com",
"upstream": {
"type": "roundrobin",
"nodes": {
"postman-echo.com:80": 1
}
}
}'
发送匹配第一个路由主机的请求:
curl "http://127.0.0.1:9080/get" -H 'host: test1.com'
你应该看到来自 httpbin.org 的响应:
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "test1.com",
"User-Agent": "curl/8.6.0",
"X-Amzn-Trace-Id": "Root=1-6746c0bf-653fac896be8818275f1e8da",
"X-Forwarded-Host": "test1.com"
},
"origin": "192.168.65.1, 43.252.208.90",
"url": "http://test1.com/get"
}
发送匹配第二个路由主机的请求:
curl "http://127.0.0.1:9080/get" -H 'host: test2.com'
你应该看到来自 postman-echo.com 的响应:
{
"args": {},
"headers": {
"host": "test2.com",
"x-request-start": "t=1732834286",
"connection": "close",
"x-forwarded-proto": "http",
"x-forwarded-port": "80",
"x-amzn-trace-id": "Root=1-6746c0ca-2b0b323902e784f512051ef6",
"x-forwarded-host": "test2.com",
"user-agent": "curl/8.6.0",
"accept": "*/*"
},
"url": "http://test2.com/get"
}
为了进一步了解该行为,创建第三个匹配所有请求的路由:
curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "mock-api7-all",
"uri": "/*",
"upstream": {
"type": "roundrobin",
"nodes": {
"mock.api7.ai:443": 1
},
"scheme": "https",
"pass_host": "node"
}
}'
发送带有随机主机名的请求:
curl "http://127.0.0.1:9080/get" -H 'host: random.com'
你应该看到请求被转发到第三个路由:
API7.ai, the creator of Apache APISIX, delivers a cloud-native API Gateway solution for the Enterprise, to help you maximize the value of APIs.
发送另一个匹配第一个路由主机的请求:
curl "http://127.0.0.1:9080/get" -H 'host: test1.com'
你应该看到请求被转发到 httpbin.org 上游。这演示了当路由器设置为 radixtree_host_uri 时,如何在路由中优先考虑主机名。
radixtree_uri
radixtree_uri 在路由匹配时优先考虑 URI 路径而不是主机名。要使用 radixtree_uri 作为 HTTP 路由器设置,请将以下块添加到你的配置文件中:
apisix:
router:
http: radixtree_uri
重新加载 APISIX 以使更改生效。
为了了解该行为,创建一个匹配 test1.com 主机和所有 URI 路径的路由:
curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "httpbin-host-test1",
"uri": "/*",
"host": "test1.com",
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
创建另一个匹配 /get 请求的路由:
curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "mock-api7-all",
"uri": "/get",
"upstream": {
"type": "roundrobin",
"nodes": {
"mock.api7.ai:443": 1
},
"scheme": "https",
"pass_host": "node"
}
}'
使用 test1.com 主机向 /get 发送请求:
curl "http://127.0.0.1:9080/get" -H 'host: test1.com'
你应该看到请求被转发到 mock.api7.ai
API7.ai, the creator of Apache APISIX, delivers a cloud-native API Gateway solution for the Enterprise, to help you maximize the value of APIs.
这表明当路由器以 radixtree_uri 模式运行时,主机名未被优先考虑,全路径匹配具有 优先级。
使用 test1.com 主机向 /anything 发送另一个请求:
curl "http://127.0.0.1:9080/anything" -H 'host: test1.com'
你应该看到请求被转发到 httpbin.org 上游。
radixtree_uri_with_parameter
radixtree_uri_with_parameter 支持在路由匹配时使用参数。要使用 radixtree_uri_with_parameter 作为 HTTP 路由器设置,请将以下块添加到你的配置文件中:
apisix:
router:
http: radixtree_uri_with_parameter
重新加载 APISIX 以使更改生效。
一个常见的用例是提取 URI 路径的一部分并使用它来构建发送到上游服务的请求。这可用于重写 URL 路径或设置自定义标头,从而实现基于客户端输入的灵活动态路由和请求自定义。可以使用 uri_param_<parameter_name> 轻松提取匹配的 URI 路径。
例如,你可以匹配像 /user/123/profile 这样的 URI 路径,提取用户 ID 123,并将该值转发到新标头中的上游服务。
为此,请创建如下路由:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "httpbin",
"uri": "/anything/user/:user_id/profile",
"plugins":{
"proxy-rewrite": {
"headers": {
"set": {
"X-User-ID": "$uri_param_user_id"
}
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
❶ 匹配请求 /anything/user/:user_id/profile,其中 user_id 是一个参数。
❷ 将 user_id 参值分配给新标头 X-User-ID。
要进行验证,请向路由发送请求:
curl "http://127.0.0.1:9080/anything/user/123/profile"
你应该看到以下响应:
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Host": "127.0.0.1",
"User-Agent": "curl/8.6.0",
"X-Amzn-Trace-Id": "Root=1-68873cf5-7248f64d19d607ea50aa9735",
"X-Forwarded-Host": "127.0.0.1",
"X-User-Id": "123"
},
...
}
路由参数还可以接受 URL 编码的字符串。例如,如果你发送如下请求:
curl -i "http://127.0.0.1:9080/anything/user/123%20456/profile"
用户 ID 将被提取为 123 456:
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Host": "127.0.0.1",
"User-Agent": "curl/8.6.0",
"X-Amzn-Trace-Id": "Root=1-68873d37-7634825b20d05dee3a852cb9",
"X-Forwarded-Host": "127.0.0.1",
"X-User-Id": "123 456"
},
...
}
在参数中匹配编码斜杠
NGINX 通常会在路由匹配前解码编码斜杠(%2F 或 %2f)。解码后的斜杠会成为路径分隔符,因此 /files/a%2Fb 无法匹配 /files/:name 这样的参数路由。
启用 match_uri_encoded_slash,可在匹配期间将编码斜杠保留为路径参数的一部分:
apisix:
match_uri_encoded_slash: true
router:
http: radixtree_uri_with_parameter
重新加载 APISIX 使全局设置生效。启用该选项后,对现有示例路由发起请求会在捕获的参数中保留斜杠:
curl "http://127.0.0.1:9080/anything/user/123%2F456/profile"
proxy-rewrite 插件接收到的 uri_param_user_id 为 123%2F456,所以上游响应应包含:
{
"headers": {
"X-User-Id": "123%2F456"
}
}
编码形式仅在路由匹配和参数捕获期间存在。插件继续从 ctx.var.uri 读取已标准化、解码的 URI,而 APISIX 会将原始请求行转发到上游,并保留 %2F。
APISIX 会将客户端的小写 %2f 标准化为大写 %2F,用于匹配和参数捕获。路由定义按字节逐一比较,因此应在路由 URI 中使用大写 %2F 表示编码斜杠。
此选项是全局性的,可能改变精确路由兼容性。例如,启用该选项后,请求 /files/a%2Fb 不再匹配精确路由 /files/a/b。
只有当完全解码原始路径后得到的标准化 URI 与 NGINX 已计算的 URI 相同时,APISIX 才会保持 %2F 编码。还需要点段解析、斜杠合并、绝对形式标准化或其他标准化步骤的请求,会回退到普通解码 URI。若 delete_uri_tail_slash 或 normalize_uri_like_servlet 改变 URI,也以该标准化 URI 为准,不应用编码斜杠匹配。
匹配请求体字段
路由可使用 post_arg.* 变量匹配 JSON、URL 编码表单或 multipart 请求体中的字段。对于 JSON 和 multipart 请求体,APISIX 会限制路由匹配期间读取的请求体数据量。apisix.max_post_args_readable_size 以 MiB 为单位,默认值为 64,可设为 0 以禁用限制:
apisix:
max_post_args_readable_size: 64
该限制用于在路由匹配时保护 Worker 内存。禁用它会允许在选择路由前读取任意大的 JSON 或 multipart 请求体。
如果请求体超过限制,post_arg.* 变量没有值,该路由条件不会匹配。APISIX 不会仅因超过该匹配限制而拒绝请求;其他路由仍可以匹配。
例如,配置更小的限制以便验证,重新加载 APISIX,然后创建按请求体匹配的路由:
apisix:
max_post_args_readable_size: 1
curl "http://127.0.0.1:9180/apisix/admin/routes/body-match" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/anything/body-match",
"methods": ["POST"],
"vars": [
["post_arg.model", "==", "gpt-4"]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
较小的匹配请求体会到达上游:
curl -i "http://127.0.0.1:9080/anything/body-match" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4"}'
你应收到 HTTP/1.1 200 OK 响应。创建一个大于已配置 1 MiB 限制的 JSON 请 求体,并将其发送到同一 URI:
{
printf '{"model":"gpt-4","pad":"'
head -c 2097152 /dev/zero | tr '\0' a
printf '"}'
} > oversized-body.json
curl -i "http://127.0.0.1:9080/anything/body-match" \
-H "Content-Type: application/json" \
--data-binary @oversized-body.json
若没有回退路由,你应收到 404 Route Not Found。错误日志会记录请求体超过可读大小;本例产生 404 的是路由条件未匹配,而不是请求体被拒绝。
了解路由匹配
APISIX 利用 luaradixtree 库进行路由,这是一个在 Lua 中为 OpenResty 实现的自适应基数树。它利用外部函数接口 (FFI) 与 rax 集成,确保高效和高性能的路由。
有几种路由匹配方式。
全路径
假设路由 URI 为:
/anything/foo
该路由将仅匹配对 /anything/foo 的请求。
通配符
假设路由 URI 为:
/anything/*
该路由将匹配对 /anything 的子路径的任何请求,例如 /anything/foo 和 /anything/bar。请注意,它将不匹配对 /anything 的请求。
通配符不需要在 URI 的末尾。你还可以有如下路由 URI:
/*/*/test
这将匹配类似 /anything/foo/test 的 URI 请求。
匹配优先级
全路径匹配的优先级高于通配符匹配。假设你有两个路由,URI 分别为 /anything/foo 和 /anything/*。对 /anything/foo 的请求将由 /anything/foo 路由匹配,而不是 /anything/* 路由。