<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 變量的值只有一種類型,那就是字符串,但是變量也有可能壓根就不存在有意義的值。沒有值的變量也有兩種特殊的值:一種是“不合法”(invalid),另一種是“沒找到”(not found)。

     

        舉例說來,當 Nginx 用戶變量 $foo 創建了卻未被賦值時,$foo 的值便是“不合法”;而如果當前請求的 URL 參數串中并沒有提及 XXX 這個參數,則 $arg_XXX 內建變量的值便是“沒找到”。

     

        無論是“不合法”也好,還是“沒找到”也罷,這兩種 Nginx 變量所擁有的特殊值,和空字符串("")這種取值是完全不同的,比如 JavaScript 語言中也有專門的 undefined 和 null 這兩種特殊值,而 Lua 語言中也有專門的 nil 值: 它們既不等同于空字符串,也不等同于數字 0,更不是布爾值 false. 其實 SQL 語言中的NULL 也是類似的一種東西。

     

        雖然前面在 (一) 中我們看到,由 set 指令創建的變量未初始化就用在“變量插值”中時,效果等同于空字符串,但那是因為 set 指令為它創建的變量自動注冊了一個“取處理程序”,將“不合法”的變量值轉換為空字符串。為了驗證這一點,我們再重新看一下 (一) 中討論過的那個例子:

        location /foo {
            echo "foo = [$foo]";
        }
     
        location /bar {
            set $foo 32;
            echo "foo = [$foo]";
        }

    這里為了簡單起見,省略了原先寫出的外圍 server 配置塊。在這個例子里,我們在 /bar 接口中用 set 指令隱式地創建了 $foo 變量這個名字,然后我們在 /foo 接口中不對 $foo 進行初始化就直接使用 echo 指令輸出。我們當時測試 /foo 接口的結果是

        $ curl 'http://localhost:8080/foo'
        foo = []

    從輸出上看,未初始化的 $foo 變量確實和空字符串的效果等同。但細心的讀者當時應該就已經注意到,對于上面這個請求,Nginx 的錯誤日志文件(一般文件名叫做 error.log)中多出一行類似下面這樣的警告:

        [warn] 5765#0: *1 using uninitialized "foo" variable, ...

    這一行警告是誰輸出的呢?答案是 set 指令為 $foo 注冊的“取處理程序”。當 /foo 接口中的  進行“變量插值”計算。于是,參數串中的 $foo 變量會被讀取,而 Nginx 會首先檢查其值容器里的取值,結果它看到了“不合法”這個特殊值,于是它這才決定繼續調用$foo 變量的“取處理程序”。于是 $foo 變量的“取處理程序”開始運行,它向 Nginx 的錯誤日志打印出上面那條警告消息,然后返回一個空字符串作為 $foo 的值,并從此緩存在 $foo 的值容器中。

     

        細心的讀者會注意到剛剛描述的這個過程其實就是那些支持值緩存的內建變量的工作原理,只不過 set 指令在這里借用了這套機制來處理未正確初始化的 Nginx 變量。值得一提的是,只有“不合法”這個特殊值才會觸發 Nginx 調用變量的“取處理程序”,而特殊值“沒找到”卻不會。

     

        上面這樣的警告一般會指示出我們的 Nginx 配置中存在變量名拼寫錯誤,抑或是在錯誤的場合使用了尚未初始化的變量。因為值緩存的存在,這條警告在一個請求的生命期中也不會打印多次。當然,ngx_rewrite 模塊專門提供了一條 uninitialized_variable_warn 配置指令可用于禁止這條警告日志。

     

        剛才提到,內建變量 $arg_XXX 在請求 URL 參數 XXX 并不存在時會返回特殊值“找不到”,但遺憾的是在 Nginx 原生配置語言(我們估且這么稱呼它)中是不能很方便地把它和空字符串區分開來的,比如:

        location /test {
            echo "name: [$arg_name]";
        }

    這里我們輸出 $arg_name 變量的值同時故意在請求中不提供 URL 參數 name:

        $ curl 'http://localhost:8080/test'
        name: []

    我們看到,輸出特殊值“找不到”的效果和空字符串是相同的。因為這一回是 Nginx 的“變量插值”引擎自動把“找不到”給忽略了。

     

        那么我們究竟應當如何捕捉到“找不到”這種特殊值的蹤影呢?換句話說,我們應當如何把它和空字符串給區分開來呢?顯然,下面這個請求中,URL 參數 name 是有值的,而且其值應當是空字符串:

        $ curl 'http://localhost:8080/test?name='
        name: []

    但我們卻無法將之和前面完全不提供 name 參數的情況給區分開。

     

        幸運的是,通過第三方模塊 ngx_lua,我們可以輕松地在 Lua 代碼中做到這一點。請看下面這個例子:

        location /test {
            content_by_lua '
                if ngx.var.arg_name == nil then
                    ngx.say("name: missing")
                else
                    ngx.say("name: [", ngx.var.arg_name, "]")
                end
            ';
        }

    這個例子和前一個例子功能上非常接近,除了我們在 /test 接口中使用了 ngx_lua 模塊的 content_by_lua 配置指令,嵌入了一小段我們自己的 Lua 代碼來對 Nginx 變量 $arg_name 的特殊值進行判斷。在這個例子中,當 $arg_name 的值為“沒找到”(或者“不合法”)時,/foo 接口會輸出 name: missing 這一行結果:

        curl 'http://localhost:8080/test'
        name: missing

    因為這是我們第一次接觸到 ngx_lua 模塊,所以需要先簡單介紹一下。ngx_lua 模塊將 Lua 語言解釋器(或者LuaJIT 即時編譯器)嵌入到了 Nginx 核心中,從而可以讓用戶在 Nginx 核心中直接運行 Lua 語言編寫的程序。我們可以選擇在 Nginx 不同的請求處理階段插入我們的 Lua 代碼。這些 Lua 代碼既可以直接內聯在 Nginx 配置文件中,也可以單獨放置在外部 .lua 文件里,然后在 Nginx 配置文件中引用 .lua 文件的路徑。

     

        回到上面這個例子,我們在 Lua 代碼里引用 Nginx 變量都是通過 ngx.var 這個由 ngx_lua 模塊提供的 Lua 接口。比如引用 Nginx 變量 $VARIABLE 時,就在 Lua 代碼里寫作 ngx.var.VARIABLE 就可以了。當 Nginx 變量 $arg_name 為特殊值“沒找到”(或者“不合法”)時, ngx.var.arg_name 在 Lua 世界中的值就是 nil,即 Lua 語言里的“空”(不同于 Lua 空字符串)。我們在 Lua 里輸出響應體內容的時候,則使用了ngx.say 這個 Lua 函數,也是 ngx_lua 模塊提供的,功能上等價于 ngx_echo 模塊的 echo 配置指令。

     

        現在,如果我們提供空字符串取值的 name 參數,則輸出就和剛才不相同了:

        $ curl 'http://localhost:8080/test?name='
        name: []

    在這種情況下,Nginx 變量 $arg_name 的取值便是空字符串,這既不是“沒找到”,也不是“不合法”,因此在 Lua 里,ngx.var.arg_name 就返回 Lua 空字符串(""),和剛才的 Lua nil 值就完全區分開了。

     

        這種區分在有些應用場景下非常重要,比如有的 web service 接口會根據 name 這個 URL 參數是否存在來決定是否按 name 屬性對數據集合進行過濾,而顯然提供空字符串作為 name 參數的值,也會導致對數據集中取值為空串的記錄進行篩選操作。

     

        不過,標準的 $arg_XXX 變量還是有一些局限,比如我們用下面這個請求來測試剛才那個 /test 接口:

        $ curl 'http://localhost:8080/test?name'
        name: missing

    此時,$arg_name 變量仍然讀出“找不到”這個特殊值,這就明顯有些違反常識。此外,$arg_XXX 變量在請求 URL 中有多個同名 XXX 參數時,就只會返回最先出現的那個 XXX 參數的值,而默默忽略掉其他實例:

        $ curl 'http://localhost:8080/test?name=Tom&name=Jim&name=Bob'
        name: [Tom]

    要解決這些局限,可以直接在 Lua 代碼中使用 ngx_lua 模塊提供的 ngx.req.get_uri_args 函數。

     

        (未完待續)

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