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_lua
handler内部终止当前请求,需要使用大于等于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 ^ /bar
last
会发起一个内部的重定向。如果使用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_uri
APIsyntax: 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_args
API,比如:
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_args
APIsyntax: 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
。