Nginx 變量漫談(二)

  關於 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 的版本中甚至可能致使進程崩潰。

 

轉載連接:http://blog.sina.com.cn/s/blog_6d579ff40100wk2j.html

相關文章
相關標籤/搜索