openresty - lua API(2) - 子请求、(内部)重定向

2018-08-01 17:34:56

内部子请求

子请求在内部执行,非常高效。子请求会缓冲所有的响应体,如果响应体很大,建议用 cosockets 来操作。

子请求默认情况下会继承所有父请求的请求头。

ngx.location.capture

syntax: res = ngx.location.capture(uri, options?)

基本用法

res = ngx.location.capture(uri)

res是一个表格,包含4个成员

res.status 子请求响应的状态码
res.header 表格的形式存放子请求的响应头,如果有同名多个响应头,那么值也为表格,比如假设响应头有3个 Set-Cookie。

Set-Cookie: a=3
Set-Cookie: foo=bar
Set-Cookie: baz=blah

那么 res.header["Set-Cookie"] 里的值为

{"a=3", "foo=bar", "baz=blah"}

res.body 存放响应体内容,必须通过检查 res.truncated 返回值来确认响应体是否完整,如果为 true,那么证明响应体被裁剪过了不完整,可能是由于一些错误导致的。

查询的参数也可以结合到uri比如

res = ngx.location.capture('/foo/bar?a=3&b=4')

这里不支持命名 location 比如 @foo,如果想使用只允许内部子请求的location,可以在 location 块里加上 internal 指令。

第二个可选参数表格:

method 指定请求方法比如 ngx.HTTP_POST。
body 指定的请求体,只支持字符串。
args 指定查询字符串,支持表格。
ctx 指定子请求里的 ngx.ctx 表格,如果当前请求里传入 ngx.ctx ,那么可以实现父子请求共用上下文。
vars 表格形式指定传递给子请求的变量,在子请求可以用 ngx.var.name 获取。
copy_all_vars 布尔值,是否复制父级所有nginx变量到子请求,那么在子请求改变变量值不会影响到福请求。
share_all_vars 布尔值,是否共享当前所有变量到子请求。
always_forward_body 布尔值,是否总是发送当前请求体。如果开启,那么如果没有指定 body 参数,会把当前通过 ngx.req.read_body() 收到的请求体直接发送给子请求而无需拷贝请求体数据,不管请求体数据在内存里还是在临时文件里。默认为false,如果没有指定 body 参数,只有子请求方法为 PUT 或者 POST 时才放松当前请求体。

--POST请求
res = ngx.location.capture(
     '/foo/bar',
     { method = ngx.HTTP_POST, body = 'hello, world' }
 )
--下面3个等同
ngx.location.capture('/foo?a=1',
     { args = { b = 3, c = ':' } }
 )

ngx.location.capture('/foo?a=1&b=3&c=%3a')

ngx.location.capture('/foo?a=1',
     { args = 'b=3&c=%3a' } }
 )

共享参数可能会造成一些负面影响,最好用 args、vars、copy_all_vars 代替。

location /other {
     set $dog "$dog world";
     echo "$uri dog: $dog";
 }

 location /lua {
     set $dog 'hello';
     content_by_lua_block {
         res = ngx.location.capture("/other",
             { share_all_vars = true });

         ngx.print(res.body)
         ngx.say(ngx.var.uri, ": ", ngx.var.dog)
     }
 }
[root@192 ~]# curl localhost/lua
/other dog: hello world
/lua: hello world

如果同时指定,copy_all_vars 和 share_all_vars,那么 share_all_vars 优先级更高。

ctx 选项

location /sub {
     content_by_lua_block {
         ngx.ctx.foo = "bar";
     }
 }
 location /lua {
     content_by_lua_block {
         local ctx = {}
         res = ngx.location.capture("/sub", { ctx = ctx })

         ngx.say(ctx.foo);
         ngx.say(ngx.ctx.foo);
     }
 }
bar
nil

当然也可以通过 ctx 选项来共享请求上下文。

location /sub {
     content_by_lua_block {
         ngx.ctx.foo = "bar";
     }
 }
 location /lua {
     content_by_lua_block {
         res = ngx.location.capture("/sub", { ctx = ngx.ctx })
         ngx.say(ngx.ctx.foo);
     }
 }
bar

ngx.location.capture_multi

同上,只是它支持多个子请求平行运行,按相同的顺序返回,所花费的时间为多个请求里时间最长的那个。

res1, res2, res3 = ngx.location.capture_multi{
     { "/foo", { args = "a=3&b=4" } },
     { "/bar" },
     { "/baz", { method = ngx.HTTP_POST, body = "hello" } },
 }

 if res1.status == ngx.HTTP_OK then
     ...
 end

 if res2.body == "BLAH" then
     ...
 end

上面两个指令的关系类似于下面

ngx.location.capture =
     function (uri, args)
         return ngx.location.capture_multi({ {uri, args} })
     end


内部重定向 - ngx.exec

syntax: ngx.exec(uri, args?)

执行内部重定向到 uri,并附加上 args 参数。

ngx.exec('/some-location')
ngx.exec('/some-location', 'a=3&b=5&c=6')
ngx.exec('/some-location?a=3&b=5', 'c=6')

ngx.exec("/foo", "a=3&b=hello+world")

--表格
ngx.exec("/foo", { a = 3, b = "hello world" })

通过表格的形式传递参数,类似于 ngx.encode_args 方法。

ngx.exec 支持命名 location,但是 args 参数将会被忽略,不过可以通过 uri 传递。

location /foo {
     content_by_lua_block {
         return ngx.exec("@bar", "a=goodbye");
     }
 }

 location @bar {
     content_by_lua_block {
         local args = ngx.req.get_uri_args()
         for key, val in pairs(args) do
             if key == "a" then
                 ngx.say(val)
             end
         end
     }
 }
[root@192 ~]# curl localhost/foo?a=hello
hello

注意:重定向前必须没有任何输出,也就是必须在 ngx.send_headers 或者是 ngx.print、ngx.say之前调用。建议配合 return 来强调重定向后会终止请求。


外部重定向 - ngx.redirect

syntax: ngx.redirect(uri, status?)

支持的状态码有:301、302(默认)、303、307、308。

return ngx.redirect("/foo")
--等价
return ngx.redirect("/foo", ngx.HTTP_MOVED_TEMPORARILY)
return ngx.redirect("http://www.google.com")

return ngx.redirect("/foo", 301)

--可以指定参数
return ngx.redirect('/foo?a=3&b=4')

该重定向类似于 ngx_http_rewrite_module 里,rewrite 指令配合 redirect 修饰符。

rewrite ^ /foo? redirect; #nginx 配置

等同于

return ngx.redirect('/foo');  -- Lua code

下面也等同

rewrite ^ /foo? permanent; #nginx 配置
return ngx.redirect('/foo', ngx.HTTP_MOVED_PERMANENTLY) -- lua代码

注意:重定向前必须没有任何输出,也就是必须在 ngx.send_headers 或者是 ngx.print、ngx.say之前调用。建议配合 return 来强调重定向后会终止请求。 

location /foo {
     content_by_lua_block {
         return ngx.redirect("/bar");
     }
 }

 location /bar {
     content_by_lua_block {
         ngx.print("bar")
     }
 }
[root@192 ~]# curl localhost/foo -v
...
> 
< HTTP/1.1 302 Moved Temporarily
< Server: openresty/1.13.6.2
< Date: Wed, 01 Aug 2018 09:30:45 GMT
< Content-Type: text/html
< Content-Length: 167
< Connection: keep-alive
< Location: /bar


备注

1.测试环境centos7 64位,openresty 版本为 1.13.6.2。
2..原文地址http://www.freecls.com/a/2712/f0


©著作权归作者所有
收藏
推荐阅读
简介
天降大任于斯人也,必先苦其心志。