<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[静怡家园]]></title> 
<link>http://www.zhanghaijun.com/index.php</link> 
<description><![CDATA[书山有路勤为径，学海无涯苦作舟！]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[静怡家园]]></copyright>
<item>
<link>http://www.zhanghaijun.com/post//</link>
<title><![CDATA[nginx变量使用方法详解-6]]></title> 
<author>碟舞飞扬 &lt;webmaster@zhanghaijun.com&gt;</author>
<category><![CDATA[Linux技术]]></category>
<pubDate>Thu, 26 Feb 2009 06:16:35 +0000</pubDate> 
<guid>http://www.zhanghaijun.com/post//</guid> 
<description>
<![CDATA[ 
	Nginx 内建变量用在“子请求”的上下文中时，其行为也会变得有些微妙。<br/>前面在 （三） 中我们已经知道，许多内建变量都不是简单的“存放值的容器”，它们一般会通过注册“存取处理程序”来表现得与众不同，而它们即使有存放值的容器，也只是用于缓存“存取处理程序”的计算结果。我们之前讨论过的 $args 变量正是通过它的“取处理程序”来返回当前请求的 URL 参数串。因为当前请求也可以是“子请求”，所以在“子请求”中读取 $args，其“取处理程序”会很自然地返回当前“子请求”的参数串。我们来看这样的一个例子：<br/><div class="code"><br/>&nbsp;&nbsp;&nbsp;&nbsp;location /main &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;main args: $args&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo_location /sub &quot;a=1&amp;b=2&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/> <br/>&nbsp;&nbsp;&nbsp;&nbsp;location /sub &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;sub args: $args&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/></div><br/>这里在 /main 接口中，先用 echo 指令输出当前请求的 $args 变量的值，接着再用 echo_location 指令发起子请求 /sub. 这里值得注意的是，我们在 echo_location 语句中除了通过第一个参数指定“子请求”的 URI 之外，还提供了第二个参数，用以指定该“子请求”的 URL 参数串（即 a=1&amp;b=2）。最后我们定义了 /sub 接口，在里面输出了一下 $args 的值。请求 /main 接口的结果如下：<br/>$ curl &#039;http://localhost:8080/main?c=3&#039;<br/>main args: c=3<br/>sub args: a=1&amp;b=2<br/>显然，当 $args 用在“主请求” /main 中时，输出的就是“主请求”的 URL 参数串，c=3；而当用在“子请求” /sub 中时，输出的则是“子请求”的参数串，a=1&amp;b=2。这种行为正符合我们的直觉。<br/>与 $args 类似，内建变量 $uri 用在“子请求”中时，其“取处理程序”也会正确返回当前“子请求”解析过的 URI:<br/><div class="code"><br/>&nbsp;&nbsp;&nbsp;&nbsp;location /main &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;main uri: $uri&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo_location /sub;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/> <br/>&nbsp;&nbsp;&nbsp;&nbsp;location /sub &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;sub uri: $uri&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/></div><br/>请求 /main 的结果是<br/><div class="code"><br/>&nbsp;&nbsp;&nbsp;&nbsp;$ curl &#039;http://localhost:8080/main&#039;<br/>&nbsp;&nbsp;&nbsp;&nbsp;main uri: /main<br/>&nbsp;&nbsp;&nbsp;&nbsp;sub uri: /sub<br/></div><br/>这依然是我们所期望的。<br/>但不幸的是，并非所有的内建变量都作用于当前请求。少数内建变量只作用于“主请求”，比如由标准模块 ngx_http_core 提供的内建变量 $request_method.<br/>变量 $request_method 在读取时，总是会得到“主请求”的请求方法，比如 GET、POST 之类。我们来测试一下：<br/><div class="code"><br/>&nbsp;&nbsp;&nbsp;&nbsp;location /main &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;main method: $request_method&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo_location /sub;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/> <br/>&nbsp;&nbsp;&nbsp;&nbsp;location /sub &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;sub method: $request_method&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/></div><br/>在这个例子里，/main 和 /sub 接口都会分别输出 $request_method 的值。同时，我们在 /main 接口里利用 echo_location 指令发起一个到 /sub 接口的 GET “子请求”。我们现在利用 curl 命令行工具来发起一个到 /main 接口的 POST 请求：<br/><div class="code"><br/>&nbsp;&nbsp;&nbsp;&nbsp;$ curl --data hello &#039;http://localhost:8080/main&#039;<br/>&nbsp;&nbsp;&nbsp;&nbsp;main method: POST<br/>&nbsp;&nbsp;&nbsp;&nbsp;sub method: POST<br/></div><br/>这里我们利用 curl 程序的 --data 选项，指定 hello 作为我们的请求体数据，同时 --data 选项会自动让发送的请求使用 POST 请求方法。测试结果证明了我们先前的预言，$request_method 变量即使在 GET “子请求” /sub 中使用，得到的值依然是“主请求” /main 的请求方法，POST.<br/>有的读者可能觉得我们在这里下的结论有些草率，因为上例是先在“主请求”里读取（并输出）$request_method 变量，然后才发“子请求”的，所以这些读者可能认为这并不能排除 $request_method 在进入子请求之前就已经把第一次读到的值给缓存住，从而影响到后续子请求中的输出结果。不过，这样的顾虑是多余的，因为我们前面在 （五） 中也特别提到过，缓存所依赖的变量的值容器，是与当前请求绑定的，而由 ngx_echo 模块发起的“子请求”都禁用了父子请求之间的变量共享，所以在上例中，$request_method 内建变量即使真的使用了值容器作为缓存（事实上它也没有），它也不可能影响到 /sub 子请求。<br/>为了进一步消除这部分读者的疑虑，我们不妨稍微修改一下刚才那个例子，将 /main 接口输出 $request_method 变量的时间推迟到“子请求”执行完毕之后：<br/><div class="code"><br/>&nbsp;&nbsp;&nbsp;&nbsp;location /main &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo_location /sub;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;main method: $request_method&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/> <br/>&nbsp;&nbsp;&nbsp;&nbsp;location /sub &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;sub method: $request_method&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/></div><br/>让我们重新测试一下：<br/><div class="code"><br/>&nbsp;&nbsp;&nbsp;&nbsp;$ curl --data hello &#039;http://localhost:8080/main&#039;<br/>&nbsp;&nbsp;&nbsp;&nbsp;sub method: POST<br/>&nbsp;&nbsp;&nbsp;&nbsp;main method: POST<br/></div><br/>可以看到，再次以 POST 方法请求 /main 接口的结果与原先那个例子完全一致，除了父子请求的输出顺序颠倒了过来（因为我们在本例中交换了 /main 接口中那两条输出配置指令的先后次序）。<br/>由此可见，我们并不能通过标准的 $request_method 变量取得“子请求”的请求方法。为了达到我们最初的目的，我们需要求助于第三方模块 ngx_echo 提供的内建变量 $echo_request_method：<br/><div class="code"><br/>&nbsp;&nbsp;&nbsp;&nbsp;location /main &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;main method: $echo_request_method&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo_location /sub;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/> <br/>&nbsp;&nbsp;&nbsp;&nbsp;location /sub &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;sub method: $echo_request_method&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/></div><br/>此时的输出终于是我们想要的了：<br/><div class="code"><br/>&nbsp;&nbsp;&nbsp;&nbsp;$ curl --data hello &#039;http://localhost:8080/main&#039;<br/>&nbsp;&nbsp;&nbsp;&nbsp;main method: POST<br/>&nbsp;&nbsp;&nbsp;&nbsp;sub method: GET<br/></div><br/>我们看到，父子请求分别输出了它们各自不同的请求方法，POST 和 GET.<br/>类似 $request_method，内建变量 $request_uri 一般也返回的是“主请求”未经解析过的 URL，毕竟“子请求”都是在 Nginx 内部发起的，并不存在所谓的“未解析的”原始形式。<br/>如果真如前面那部分读者所担心的，内建变量的值缓存在共享变量的父子请求之间起了作用，这无疑是灾难性的。我们前面在 （五） 中已经看到 ngx_auth_request 模块发起的“子请求”是与其“父请求”共享一套变量的。下面是一个这样的可怕例子：<br/><div class="code"><br/>&nbsp;&nbsp;&nbsp;&nbsp;map $uri $tag &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;default&nbsp;&nbsp;&nbsp;&nbsp; 0;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/main&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/sub&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/> <br/>&nbsp;&nbsp;&nbsp;&nbsp;server &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;listen 8080;<br/> <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;location /main &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;auth_request /sub;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;main tag: $tag&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/> <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;location /sub &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;sub tag: $tag&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/></div><br/>这里我们使用久违了的 map 指令来把内建变量 $uri 的值映射到用户变量 $tag 上。当 $uri 的值为 /main 时，则赋予 $tag 值 1，当 $uri 取值 /sub 时，则赋予 $tag 值 2，其他情况都赋 0. 接着，我们在 /main 接口中先用 ngx_auth_request 模块的 auth_request 指令发起到 /sub 接口的子请求，然后再输出变量 $tag 的值。而在 /sub 接口中，我们直接输出变量 $tag. 猜猜看，如果我们访问接口 /main，将会得到什么样的输出呢？<br/><div class="code"><br/>&nbsp;&nbsp;&nbsp;&nbsp;$ curl &#039;http://localhost:8080/main&#039;<br/>&nbsp;&nbsp;&nbsp;&nbsp;main tag: 2<br/></div><br/>咦？我们不是分明把 /main 这个值映射到 1 上的么？为什么实际输出的是 /sub 映射的结果 2 呢？<br/>其实道理很简单，因为我们的 $tag 变量在“子请求” /sub 中首先被读取，于是在那里计算出了值 2（因为 $uri 在那里取值 /sub，而根据 map 映射规则，$tag 应当取值 2），从此就被 $tag 的值容器给缓存住了。而 auth_request 发起的“子请求”又是与“父请求”共享一套变量的，于是当 Nginx 的执行流回到“父请求”输出 $tag 变量的值时，Nginx 就直接返回缓存住的结果 2 了。这样的结果确实太意外了。<br/>从这个例子我们再次看到，父子请求间的变量共享，实在不是一个好主意。<br/>Tags - <a href="http://www.zhanghaijun.com/tags/nginx/" rel="tag">nginx</a>
]]>
</description>
</item><item>
<link>http://www.zhanghaijun.com/post//#blogcomment</link>
<title><![CDATA[[评论] nginx变量使用方法详解-6]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>http://www.zhanghaijun.com/post//#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>