<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    Nginx 變量漫談(二)

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    關于 Nginx 變量的另一個常見誤區是認為變量容器的生命期,是與 location 配置塊綁定的。其實不然。我們來看一個涉及“內部跳轉”的例子:

        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進行初始化。

     

        從上面這個例子我們看到,一個請求在其處理過程中,即使經歷多個不同的 location 配置塊,它使用的還是同一套 Nginx 變量的副本。這里,我們也首次涉及到了“內部跳轉”這個概念。值得一提的是,標準ngx_rewrite 模塊的 rewrite 配置指令其實也可以發起“內部跳轉”,例如上面那個例子用 rewrite 配置指令可以改寫成下面這樣的形式:

        server {
            listen 8080;
     
            location /foo {
                set $a hello;
                rewrite ^ /bar;
            }
     
            location /bar {
                echo "a = [$a]";
            }
        }

    其效果和使用 echo_exec 是完全相同的。后面我們還會專門介紹這個 rewrite 指令的更多用法,比如發起 301和 302 這樣的“外部跳轉”。

     

        從上面這個例子我們看到,Nginx 變量值容器的生命期是與當前正在處理的請求綁定的,而與 location 無關。

     

        前面我們接觸到的都是通過 set 指令隱式創建的 Nginx 變量。這些變量我們一般稱為“用戶自定義變量”,或者更簡單一些,“用戶變量”。既然有“用戶自定義變量”,自然也就有由 Nginx 核心和各個 Nginx 模塊提供的“預定義變量”,或者說“內建變量”(builtin variables)。

     

        Nginx 內建變量最常見的用途就是獲取關于請求或響應的各種信息。例如由 ngx_http_core 模塊提供的內建變量 $uri,可以用來獲取當前請求的 URI(經過解碼,并且不含請求參數),而 $request_uri 則用來獲取請求最原始的 URI (未經解碼,并且包含請求參數)。請看下面這個例子:

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

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全