Nginx 變數漫談(六)

2021-09-06 20:18:45 字數 4480 閱讀 6335

nginx 內建變數用在「子請求」的上下文中時,其行為也會變得有些微妙。

前面在 (三) 中我們已經知道,許多內建變數都不是簡單的「存放值的容器」,它們一般會通過註冊「訪問處理程式」來表現得與眾不同,而它們即使有存放值的容器,也只是用於快取「訪問處理程式」的計算結果。我們之前討論過的 $args 變數正是通過它的「取處理程式」來返回當前請求的 url 引數串。因為當前請求也可以是「子請求」,所以在「子請求」中讀取 $args,其「取處理程式」會很自然地返回當前「子請求」的引數串。我們來看這樣的乙個例子:

location /main 

location /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 /main 

location /sub

請求/main的結果是

這依然是我們所期望的。

但不幸的是,並非所有的內建變數都作用於當前請求。少數內建變數只作用於「主請求」,比如由標準模組ngx_http_core 提供的內建變數 $request_method.

變數 $request_method 在讀取時,總是會得到「主請求」的請求方法,比如getpost之類。我們來測試一下:

location /main 

location /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 /main 

location /sub

讓我們重新測試一下:

可以看到,再次以post方法請求/main介面的結果與原先那個例子完全一致,除了父子請求的輸出順序顛倒了過來(因為我們在本例中交換了/main介面中那兩條輸出配置指令的先後次序)。

由此可見,我們並不能通過標準的 $request_method 變數取得「子請求」的請求方法。為了達到我們最初的目的,我們需要求助於第三方模組 ngx_echo 提供的內建變數 $echo_request_method:

location /main 

location /sub

此時的輸出終於是我們想要的了:

我們看到,父子請求分別輸出了它們各自不同的請求方法,postget.

類似 $request_method,內建變數 $request_uri 一般也返回的是「主請求」未經解析過的 url,畢竟「子請求」都是在 nginx 內部發起的,並不存在所謂的「未解析的」原始形式。

如果真如前面那部分讀者所擔心的,內建變數的值快取在共享變數的父子請求之間起了作用,這無疑是災難性的。我們前面在 (五) 中已經看到 ngx_auth_request 模組發起的「子請求」是與其「父請求」共享一套變數的。下面是乙個這樣的可怕例子:

map $uri $tag 

server 

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...