写一个Kong前端URI通配符传递插件

场景

如果Kong前端uris配置正则表达式,虽然Kong能够捕获正则表达式的值,但是它不会用该值去替换上游upstream_url里面预设变量,例如:
前端uris配置

1
/api/testmap/(?<id>\d+)

上游upstream_url配置类似这样

1
http://192.168.1.3:10000/api/testmap/{id}

前端发起请求

1
2
3
curl http://10.0.0.111/api/testmap/1/
curl http://10.0.0.111/api/testmap/2/
curl http://10.0.0.111/api/testmap/3/

上游upstream_url的值都是

1
http://192.168.1.3:10000/api/testmap/{id}
解决思路

既然Kong能够捕获正则表达式的值,只需要用Kong捕获的值,对upstream_url进行正则替换即可。

获取

如果,Kong前端uris配置为

1
/api/testmap/(?<id>\d+)

那么,Kong将捕获的值,以table类型存储在变量ngx.ctx.router_matches.uri_captures中。
如果,前端发起的请求为

1
curl http://10.0.0.111/api/testmap/2/

那么ngx.ctx.router_matches.uri_captures变量的值为

1
{"2", id = "2"}

需要注意的是,如果uris里面没有正则表达式,那么变量ngx.ctx.router_matches.uri_captures的值为nil,而不是一个table

替换

定义upstream_url里面的变量时候,需要有一定的特征。
例如,uris定义为

1
/api/testmap/(?<id>\d+)

那么,upstream_url定义的时候,就将id用特需符号标识,比如用{}来标识这是一个待替换的变量

1
http://192.168.1.3:10000/api/testmap/{id}

做一个table的循环,k是待替换的变量,v是最后替换的值。

1
2
3
4
5
6
for k, v in pairs(uri_captures) do
if type(k) == "string" then
local tag_string = string.format("{%s}", k)
upstream_uri = string.gsub(upstream_uri, tag_string, v)
end
end
重置

变量替换好后,需要重置upstream_uri的值。有个坑就是,Kong版本大于等于0.11.0后,不支持用ngx.req.set_uri()重置upstream_uri的值,需要用ngx.var.upstream_uri重置upstream_uri
官方更新日志是这么说的:

The upstream URI is now determined via the Nginx $upstream_uri variable. Custom plugins using the ngx.req.set_uri() API will not be taken into consideration anymore. One must now set the ngx.var.upstream_uri variable from the Lua land.

代码

插件的核心代码handler.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
local BasePlugin = require "kong.plugins.base_plugin"

local UpstreamUrlPenetrateHandler = BasePlugin:extend()

function UpstreamUrlPenetrateHandler:new()
UpstreamUrlPenetrateHandler.super.new(self, "upstream-url-penetrate")
end

function UpstreamUrlPenetrateHandler:access()
UpstreamUrlPenetrateHandler.super.access(self)

local uri_captures = ngx.ctx.router_matches.uri_captures
local upstream_uri = ngx.var.upstream_uri

if type(uri_captures) == "table" then
for k, v in pairs(uri_captures) do
if type(k) == "string" then
local tag_string = string.format("{%s}", k)
upstream_uri = string.gsub(upstream_uri, tag_string, v)
end
end
ngx.var.upstream_uri = upstream_uri
end

end

UpstreamUrlPenetrateHandler.PRIORITY = 100

return UpstreamUrlPenetrateHandler

该插件也适用于uris包含多个正则表达式的场景。
插件GitHub开源地址kong-plugin-upstream-url-penetrate

----------------本文结束 感谢阅读----------------