关于 Nginx 变量的另外一个常见误区是认为变量容器的生命期,是与 location 配置块绑定的。其实否则。咱们来看一个涉及“内部跳转”的例子:html
server { listen 8080; location /foo { set $a hello; echo_exec /bar; } location /bar { echo "a = [$a]"; } }
这里咱们在 location /foo 中,使用第三方模块 ngx_echo 提供的 echo_exec 配置指令,发起到 location /bar 的“内部跳转”。所谓“内部跳转”,就是在处理请求的过程当中,于服务器内部,从一个 location 跳转到另外一个 location 的过程。这不一样于利用 HTTP 状态码 301 和 302 所进行的“外部跳转”,由于后者是由 HTTP 客户端配合进行跳转的,并且在客户端,用户能够经过浏览器地址栏这样的界面,看到请求的 URL 地址发生了变化。内部跳转和 Bourne Shell(或 Bash)中的 exec 命令很像,都是“有去无回”。另外一个相近的例子是 C 语言中的 goto 语句。数组
既然是内部跳转,当前正在处理的请求就仍是原来那个,只是当前的 location 发生了变化,因此仍是原来的那一套 Nginx 变量的容器副本。对应到上例,若是咱们请求的是 /foo 这个接口,那么整个工做流程是这样的:先在 location /foo 中经过 set 指令将 $a 变量的值赋为字符串 hello,而后经过 echo_exec 指令发起内部跳转,又进入到 location /bar 中,再输出 $a 变量的值。由于 $a 仍是原来的 $a,因此咱们能够指望获得 hello 这行输出。测试证明了这一点:浏览器
$ curl localhost:8080/foo
a = [hello]服务器
但若是咱们从客户端直接访问 /bar 接口,就会获得空的 $a 变量的值,由于它依赖于 location /foo 来对 $a 进行初始化。cookie
从上面这个例子咱们看到,一个请求在其处理过程当中,即便经历多个不一样的 location 配置块,它使用的仍是同一套 Nginx 变量的副本。这里,咱们也首次涉及到了“内部跳转”这个概念。值得一提的是,标准 ngx_rewrite 模块的 rewrite 配置指令其实也能够发起“内部跳转”,例如上面那个例子用 rewrite 配置指令能够改写成下面这样的形式:curl
server { listen 8080; location /foo { set $a hello; rewrite ^ /bar; } location /bar { echo "a = [$a]"; } }
其效果和使用 echo_exec 是彻底相同的。后面咱们还会专门介绍这个 rewrite 指令的更多用法,好比发起 301 和 302 这样的“外部跳转”。测试
从上面这个例子咱们看到,Nginx 变量值容器的生命期是与当前正在处理的请求绑定的,而与 location 无关。ui
前面咱们接触到的都是经过 set 指令隐式建立的 Nginx 变量。这些变量咱们通常称为“用户自定义变量”,或者更简单一些,“用户变量”。既然有“用户自定义变量”,天然也就有由 Nginx 核心和各个 Nginx 模块提供的“预约义变量”,或者说“内建变量”(builtin variables)。编码
Nginx 内建变量最多见的用途就是获取关于请求或响应的各类信息。例如由 ngx_http_core 模块提供的内建变量 $uri,能够用来获取当前请求的 URI(通过解码,而且不含请求参数),而 $request_uri 则用来获取请求最原始的 URI (未经解码,而且包含请求参数)。请看下面这个例子:url
location /test { echo "uri = $uri"; echo "request_uri = $request_uri"; }
这里为了简单起见,连 server 配置块也省略了,和前面全部示例同样,咱们监听的依然是 8080 端口。在这个例子里,咱们把 $uri 和 $request_uri 的值输出到响应体中去。下面咱们用不一样的请求来测试一下这个 /test 接口:
$ curl 'http://localhost:8080/test'
uri = /test
request_uri = /test
$ curl 'http://localhost:8080/test?a=3&b=4'
uri = /test
request_uri = /test?a=3&b=4
$ curl 'http://localhost:8080/test/hello%20world?a=3&b=4'
uri = /test/hello world
request_uri = /test/hello%20world?a=3&b=4
另外一个特别经常使用的内建变量其实并非单独一个变量,而是有无限多变种的一群变量,即名字以 arg_ 开头的全部变量,咱们估且称之为 $arg_XXX 变量群。一个例子是 $arg_name,这个变量的值是当前请求名为 name 的 URI 参数的值,并且仍是未解码的原始形式的值。咱们来看一个比较完整的示例:
location /test { echo "name: $arg_name"; echo "class: $arg_class"; }
而后在命令行上使用各类参数组合去请求这个 /test 接口:
$ curl 'http://localhost:8080/test'
name:
class:
$ curl 'http://localhost:8080/test?name=Tom&class=3'
name: Tom
class: 3
$ curl 'http://localhost:8080/test?name=hello%20world&class=9'
name: hello%20world
class: 9
其实 $arg_name 不只能够匹配 name 参数,也能够匹配 NAME 参数,抑或是 Name,等等:
$ curl 'http://localhost:8080/test?NAME=Marry'
name: Marry
class:
$ curl 'http://localhost:8080/test?Name=Jimmy'
name: Jimmy
class:
Nginx 会在匹配参数名以前,自动把原始请求中的参数名调整为所有小写的形式。
若是你想对 URI 参数值中的 %XX 这样的编码序列进行解码,可使用第三方 ngx_set_misc 模块提供的 set_unescape_uri 配置指令:
location /test { set_unescape_uri $name $arg_name; set_unescape_uri $class $arg_class; echo "name: $name"; echo "class: $class"; }
如今咱们再看一下效果:
$ curl 'http://localhost:8080/test?name=hello%20world&class=9'
name: hello world
class: 9
空格果真被解码出来了!
从这个例子咱们同时能够看到,这个 set_unescape_uri 指令也像 set 指令那样,拥有自动建立 Nginx 变量的功能。后面咱们还会专门介绍到 ngx_set_misc 模块。
像 $arg_XXX 这种类型的变量拥有无穷无尽种可能的名字,因此它们并不对应任何存放值的容器。并且这种变量在 Nginx 核心中是通过特别处理的,第三方 Nginx 模块是不能提供这样充满魔法的内建变量的。
相似 $arg_XXX 的内建变量还有很多,好比用来取 cookie 值的 $cookie_XXX 变量群,用来取请求头的 $http_XXX 变量群,以及用来取响应头的 $sent_http_XXX 变量群。这里就不一一介绍了,感兴趣的读者能够参考 ngx_http_core 模块的官方文档。
须要指出的是,许多内建变量都是只读的,好比咱们刚才介绍的 $uri 和 $request_uri. 对只读变量进行赋值是应当绝对避免的,由于会有意想不到的后果,好比:
location /bad { set $uri /blah; echo $uri; }
这个有问题的配置会让 Nginx 在启动的时候报出一条使人匪夷所思的错误:
[emerg] the duplicate "uri" variable in ...
若是你尝试改写另一些只读的内建变量,好比 $arg_XXX 变量,在某些 Nginx 的版本中甚至可能致使进程崩溃。