rewrite_by_lua指令syntax: rewrite_by_luacontext: http, server, location, location if phase: rewrite tail WARNING Since the v0.9.17 release, use of this directive is discouraged; use the new rewrite_by_lua_block directive instead.
扮演着rewrite阶段的handler的角色,每次请求的时候,rewrite_by_lua把字符串<lua-script-str>当做Lua代码执行。在Lua代码中可以执行API调用,并且Lua代码是在独立的全局环境中执行的(也就说,是在一个沙箱中执行的)。
注意:默认情况下,这个指令运行在标准的ngx_http_rewrite_module指令集之后,因此下面的配置符合我们的预期:
location /foo {
set $a 12; # create and initialize $a
set $b ""; # create and initialize $b
rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1';
echo "res = $b";
}
因为set $a 12和set $b ""运行在rewrite_by_lua之前。
另外一方面,下面的配置不符合我们的预期:
location /foo {
set $a 12; # create and initialize $a
set $b ''; # create and initialize $b
rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1';
if ($b = '13') {
rewrite ^ /bar redirect;
break;
}
echo "res = $b";
}
即使在配置文件中,if被放在了rewrite_by_lua的后面,但是它在rewrite_by_lua之前运行。
正确的配置方式,如下所示:
location /foo {
set $a 12; # create and initialize $a
set $b ''; # create and initialize $b
rewrite_by_lua '
ngx.var.b = tonumber(ngx.var.a) + 1
if tonumber(ngx.var.b) == 13 then
return ngx.redirect("/bar");
end
';
echo "res = $b";
}
可以使用rewrite_by_lua在模仿ngx_eval模块,比如:
location / {
eval $res {
proxy_pass http://foo.com/check-spam;
}
if ($res = 'spam') {
rewrite ^ /terms-of-use.html redirect;
}
fastcgi_pass ...;
}
对应的ngx_lua实现是:
location = /check-spam {
internal;
proxy_pass http://foo.com/check-spam;
}
location / {
rewrite_by_lua '
local res = ngx.location.capture("/check-spam")
if res.body == "spam" then
return ngx.redirect("/terms-of-use.html")
end
';
fastcgi_pass ...;
}
正如任何其它的rewrite阶段的handler一样,rewrite_by_lua也运行在子请求中。
注意:当在一个rewrite_by_lua handler中调用ngx.exit(ngx.OK)的时候,Nginx请求处理控制流仍然会继续,直到Content handler。为了在一个rewrite_by_luahandler内部终止当前请求,需要使用大于等于200(ngx.HTTP_OK)并且小于300(ngx.HTTP_SPECIAL_RESPONSE)的status code调用ngx.exit,来表示成功的退出;需要使用500(ngx.HTTP_INTERNAL_SERVER_ERROR)调用ngx.exit,来表示失败的退出。
如果ngx_http_rewrite_module的rewrite指令,改变了原始的URI,并且重新搜索匹配改变后的URI的location,那么任何当前location中的rewrite_by_lua或rewrite_by_lua_file代码序列都不会被执行。比如:
location /foo {
rewrite ^ /bar;
rewrite_by_lua 'ngx.exit(503)';
}
location /bar {
...
}
上面这个例子中,ngx.exit(503)永远也不会被执行,因为rewrite ^ /barlast会发起一个内部的重定向。如果使用break标记(rewrite ^ /bar break),那么就不会发生重定向,因此rewrite_by_lua代码会被执行。
默认情况下,rewrite_by_lua代码总是在rewrite请求处理阶段的末尾执行。可以通过打开rewrite_by_lua_no_postpone指令,来改变这个默认行为。
rewrite_by_lua_block指令
syntax: rewrite_by_lua_block { lua-script }
context: http, server, location, location if
phase: rewrite tail
与rewrite_by_lua指令类似,除了:rewrite_by_lua_block指令把Lua代码放在一对花括号的内部,而不是放在一个Nginx字符串中(它需要对特殊字符进行转义)。
下面是一个例子:
rewrite_by_lua_block {
do_something("hello, world!\nhiya\n")
}
rewrite_by_lua_file指令syntax: rewrite_by_lua_filecontext: http, server, location, location if phase: rewrite tail
与rewrite_by_lua等价,除了:Lua代码包含在<path-to-lua-script-file>所指定的文件中(从v0.5.0rc32版本开始,文件的内容也可以是Lua/LuaJIT字节码)。
为了提高伸缩性,<path-to-lua-script-file>字符串中也可以包含Nginx变量,然而这样做可能带来一些风险,因此不推荐。
当指定一个相对地址(比如:foo/bar.lua)的时候,它会被转换成相对server prefix路径的绝对地址,server prefix的值是在启动Nginx的时候,通过-p PATH命令行选项指定的。
当开启Lua代码缓存的时候,用户代码在第一个请求到来时加载一次,然后缓存。每次修改了Lua源代码文件之后,必须重载Nginx配置文件。在开发期间,可以通过将Nginx配置文件中的lua_code_cache指令切换成off,来临时的关闭Lua代码缓存。
默认情况下,rewrite_by_lua_file代码总是在rewrite请求处理阶段的末尾执行。可以通过打开rewrite_by_lua_no_postpone指令,来改变这个默认行为。
ngx.exec APIsyntax: ngx.exec(uri, args?) context: rewrite_by_lua*, access_by_lua*, content_by_lua*
发起一个到uri的内部重定向,与echo-nginx-module的echo_exec指令类似。
ngx.exec('/some-location');
ngx.exec('/some-location', 'a=3&b=5&c=6');
ngx.exec('/some-location?a=3&b=5', 'c=6');
第二个参数args是可选的,它用来指定额外的URI查询参数,比如:
ngx.exec("/foo", "a=3&b=hello%20world")
同时也可以给args参数传递一个Lua表,ngx_lua负责URI转义和字符串连接。
ngx.exec("/foo", { a = 3, b = "hello world" })
的结果其实与前面的例子相同。
当给args参数传递一个Lua表的时候,其实是调用ngx.encode_args,它会按照URI编码规则,把Lua表编码成一个查询字符串。
比如:
ngx.encode_args({foo = 3, ["b r"] = "hello world"})
会生成
foo=3&b%20r=hello%20worldfoo=3&b%20r=hello%20world
表的键必须是Lua字符串。
也支持多值查询参数,只需要使用Lua表作为参数的值,比如:
ngx.encode_args({baz = {32, "hello"}})
会生成:
baz=32&baz=hello
如果参数的值为空表,效果等价于nil值。
如果参数的值为false,效果等价于nil值。
如果参数的值为true,比如:
ngx.encode_args({a = true, b = 1})
会生成:
a&b=1
下面回到ngx.exec。
ngx.exec支持命名location,但是在提供第二个参数的时候,它会被忽略。重定向之后的查询字符串会从重定向之前的location继承。
在下面的例子中,GET /foo/file.php?a=hello会返回hello,而不是goodbye:
location /foo {
content_by_lua '
ngx.exec("@bar", "a=goodbye");
';
}
location @bar {
content_by_lua '
local args = ngx.req.get_uri_args()
for key, val in pairs(args) do
if key == "a" then
ngx.say(val)
end
end
';
}
ngx.exec与ngx.redirect不同,它仅仅只是一个内部重定向,并没有新的外部的HTTP调用。
这个方法会终止当前请求的处理,因此它必须在ngx.send_headers之前或通过ngx.print或ngx.say显式的输出响应体之前,被调用。
推荐:在这个函数调用之前加上return语句。当在除了header_filter_by_lua之外的上下文中,调用ngx.exec的时候,可以采用return ngx.exec(...)来强调请求处理正在终止。
ngx.redirect APIsyntax: ngx.redirect(uri, status?) context: rewrite_by_lua*, access_by_lua*, content_by_lua*
发起一个到URI的HTTP 301或HTTP 302重定向。
可选的status参数用来指定使用301还是302,默认是302(ngx.HTTP_MOVED_TEMPORARILY)。
下面是一个例子,假设当前的server name是localhost,监听1984端口:
return ngx.redirect("/foo")
等价于:
return ngx.redirect("/foo", ngx.HTTP_MOVED_TEMPORARILY)
ngx.redirect也支持重定向到任意外部的URL,比如:
return ngx.redirect("http://www.google.com")
可以使用数字形式的status code作为status参数:
return ngx.redirect("/foo", 301)
ngx.redirect方法与带有redirect修饰符的rewrite指令类似,比如:
rewrite ^ /foo? redirect; # nginx config
等价于:
return ngx.redirect('/foo'); -- Lua code
而
rewrite ^ /foo? permanent; # nginx config
等价于:
return ngx.redirect('/foo', ngx.HTTP_MOVED_PERMANENTLY) -- Lua code
也可以指定URI参数,比如:
return ngx.redirect('/foo?a=3&b=4')
这个方法会终止当前请求的处理,因此它必须在ngx.send_headers之前或通过ngx.print或ngx.say显式的输出响应体之前,被调用。
推荐:在这个函数调用之前加上return语句。当在除了header_filter_by_lua之外的上下文中,调用ngx.exec的时候,可以采用return ngx.exec(...)来强调请求处理正在终止。
ngx.req.set_uriAPIsyntax: ngx.req.set_uri(uri, jump?) context: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*
使用uri参数重写当前请求的URI。uri参数必须是一个Lua字符串,并且长度不能为0,否则会抛出一个Lua异常。
jump参数是可选的,它是一个布尔值,用于触发location的重新匹配。当jump是true的时候(默认值是false),这个函数不会返回,它会告诉Nginx在之后的post-rewrite阶段,使用新的URI值重新搜索location,然后跳转到新的location。
默认情况下,仅当改变了当前请求的URI,才会触发跳转。当jump参数是false或没传递jump参数的时候,ngx.req.set_uri会返回,并且没有返回值。比如:
rewrite ^ /foo last;
等价于:
ngx.req.set_uri("/foo", true)
同理,
rewrite ^ /foo break;
等价于:
ngx.req.set_uri("/foo", false)
或者是等价于:
ngx.req.set_uri("/foo")
在rewrite_by_lua和rewrite_by_lua_file中,jump参数只能被设置为true。在其它上下文中使用jump是禁止的,会抛出一个Lua异常。
下面是一个使用了正则表达式的例子:
location /test {
rewrite_by_lua '
local uri = ngx.re.sub(ngx.var.uri, "^/test/(.*)", "/$1", "o")
ngx.req.set_uri(uri)
';
proxy_pass http://my_backend;
}
它的功能等价于:
location /test {
rewrite ^/test/(.*) /$1 break;
proxy_pass http://my_backend;
}
无法使用ngx.req.set_uri重写URI参数。如果想要重写URI参数,需要使用ngx.req.set_uri_argsAPI,比如:
Nginx配置文件:
rewrite ^ /foo?a=3? last;
等价于:
ngx.req.set_uri_args("a=3")
ngx.req.set_uri("/foo", true)
或:
ngx.req.set_uri_args({a = 3})
ngx.req.set_uri("/foo", true)
ngx.req.set_uri_argsAPIsyntax: ngx.req.set_uri_args(args) context: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*
使用args参数重写当前请求的URI查询参数。args参数可以是一个Lua字符串,比如:
ngx.req.set_uri_args("a=3&b=hello%20world")
也可以是一个包含查询参数的key-value对的Lua表,比如:
ngx.req.set_uri_args({ a = 3, b = "hello world" })
当args参数是一个Lua表的时候,ngx.req.set_uri_args方法会根据URI转义规则,转义key和value。
ngx.req.set_uri_args也支持多值参数,比如:
ngx.req.set_uri_args({ a = 3, b = {5, 6} })
它会产生一个这样的查询字符串:a=3&b=5&b=6。