nginx 內建變數用在「子請求」的上下文中時,其行為也會變得有些微妙。
前面在 (三) 中我們已經知道,許多內建變數都不是簡單的「存放值的容器」,它們一般會通過註冊「訪問處理程式」來表現得與眾不同,而它們即使有存放值的容器,也只是用於快取「訪問處理程式」的計算結果。我們之前討論過的 $args 變數正是通過它的「取處理程式」來返回當前請求的 url 引數串。因為當前請求也可以是「子請求」,所以在「子請求」中讀取 $args,其「取處理程式」會很自然地返回當前「子請求」的引數串。我們來看這樣的乙個例子:
location /mainlocation /sub
這裡在/main
介面中,先用 echo 指令輸出當前請求的 $args 變數的值,接著再用 echo_location 指令發起子請求/sub
. 這裡值得注意的是,我們在 echo_location 語句中除了通過第乙個引數指定「子請求」的 uri 之外,還提供了第二個引數,用以指定該「子請求」的 url 引數串(即a=1&b=2
)。最後我們定義了/sub
介面,在裡面輸出了一下 $args 的值。請求/main
介面的結果如下:
顯然,當 $args 用在「主請求」/main
中時,輸出的就是「主請求」的 url 引數串,c=3
;而當用在「子請求」/sub
中時,輸出的則是「子請求」的引數串,a=1&b=2
。這種行為正符合我們的直覺。
與 $args 類似,內建變數 $uri 用在「子請求」中時,其「取處理程式」也會正確返回當前「子請求」解析過的 uri:
location /mainlocation /sub
請求/main
的結果是
這依然是我們所期望的。
但不幸的是,並非所有的內建變數都作用於當前請求。少數內建變數只作用於「主請求」,比如由標準模組ngx_http_core 提供的內建變數 $request_method.
變數 $request_method 在讀取時,總是會得到「主請求」的請求方法,比如get
、post
之類。我們來測試一下:
location /mainlocation /sub
在這個例子裡,/main
和/sub
介面都會分別輸出 $request_method 的值。同時,我們在/main
介面裡利用echo_location 指令發起乙個到/sub
介面的get
「子請求」。我們現在利用curl
命令列工具來發起乙個到/main
介面的post
請求:
這裡我們利用curl
程式的--data
選項,指定hello
作為我們的請求體資料,同時--data
選項會自動讓傳送的請求使用post
請求方法。測試結果證明了我們先前的預言,$request_method 變數即使在get
「子請求」/sub
中使用,得到的值依然是「主請求」/main
的請求方法,post
.
有的讀者可能覺得我們在這裡下的結論有些草率,因為上例是先在「主請求」裡讀取(並輸出)$request_method 變數,然後才發「子請求」的,所以這些讀者可能認為這並不能排除 $request_method在進入子請求之前就已經把第一次讀到的值給快取住,從而影響到後續子請求中的輸出結果。不過,這樣的顧慮是多餘的,因為我們前面在 (五) 中也特別提到過,快取所依賴的變數的值容器,是與當前請求繫結的,而由ngx_echo 模組發起的「子請求」都禁用了父子請求之間的變數共享,所以在上例中,$request_method 內建變數即使真的使用了值容器作為快取(事實上它也沒有),它也不可能影響到/sub
子請求。
為了進一步消除這部分讀者的疑慮,我們不妨稍微修改一下剛才那個例子,將/main
介面輸出$request_method 變數的時間推遲到「子請求」執行完畢之後:
location /mainlocation /sub
讓我們重新測試一下:
可以看到,再次以post
方法請求/main
介面的結果與原先那個例子完全一致,除了父子請求的輸出順序顛倒了過來(因為我們在本例中交換了/main
介面中那兩條輸出配置指令的先後次序)。
由此可見,我們並不能通過標準的 $request_method 變數取得「子請求」的請求方法。為了達到我們最初的目的,我們需要求助於第三方模組 ngx_echo 提供的內建變數 $echo_request_method:
location /mainlocation /sub
此時的輸出終於是我們想要的了:
我們看到,父子請求分別輸出了它們各自不同的請求方法,post
和get
.
類似 $request_method,內建變數 $request_uri 一般也返回的是「主請求」未經解析過的 url,畢竟「子請求」都是在 nginx 內部發起的,並不存在所謂的「未解析的」原始形式。
如果真如前面那部分讀者所擔心的,內建變數的值快取在共享變數的父子請求之間起了作用,這無疑是災難性的。我們前面在 (五) 中已經看到 ngx_auth_request 模組發起的「子請求」是與其「父請求」共享一套變數的。下面是乙個這樣的可怕例子:
map $uri $tagserver
location /sub
}
這裡我們使用久違了的 map 指令來把內建變數 $uri 的值對映到使用者變數$tag
上。當 $uri 的值為/main
時,則賦予$tag
值 1,當 $uri 取值/sub
時,則賦予$tag
值 2,其他情況都賦0
. 接著,我們在/main
介面中先用 ngx_auth_request 模組的auth_request
指令發起到/sub
介面的子請求,然後再輸出變數$tag
的值。而在/sub
介面中,我們直接輸出變數$tag
. 猜猜看,如果我們訪問介面/main
,將會得到什麼樣的輸出呢?
咦?我們不是分明把/main
這個值對映到1
上的麼?為什麼實際輸出的是/sub
對映的結果2
呢?
其實道理很簡單,因為我們的$tag
變數在「子請求」/sub
中首先被讀取,於是在那裡計算出了值2
(因為 $uri 在那裡取值/sub
,而根據 map 對映規則,$tag
應當取值2
),從此就被$tag
的值容器給快取住了。而auth_request
發起的「子請求」又是與「父請求」共享一套變數的,於是當 nginx 的執行流回到「父請求」輸出$tag
變數的值時,nginx 就直接返回快取住的結果2
了。這樣的結果確實太意外了。
從這個例子我們再次看到,父子請求間的變數共享,實在不是乙個好主意。
(未完待續)
Nginx 變數漫談(三)
太棒了 變數漫談 三 rel noopener noreferrer nginx 變數漫談 三 agentzh 也有一些內建變數是支援改寫的,其中乙個例子是 args.這個變數在讀取時返回當前請求的 url 引數串 即請求 url 中問號後面的部分,如果有的話 而在賦值時可以直接修改引數串。我們來看...
Nginx 變數漫談(四)
在設定了 取處理程式 的情況下,nginx 變數也可以選擇將其值容器用作快取,這樣在多次讀取變數的時候,就只需要呼叫 取處理程式 計算一次。我們下面就來看乙個這樣的例子 map args foo server 這裡首次用到了標準 ngx map 模組的 map 配置指令,我們有必要在此介紹一下。ma...
Nginx 變數漫談(八)
與 arg 類似,我們在 二 中提到過的內建變數 cookie 變數也會在名為 的 cookie 不存在時返回特殊值 沒找到 location test 利用curl命令列工具的 cookie name value選項可以指定name value為當前請求攜帶的 cookie 通過新增相應的cook...