Nginx 變數漫談(六)

來源:互聯網
上載者:User

標籤:blog   http   ar   io   os   使用   sp   on   資料   

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

 

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

    location /main {
        echo "main args: $args";
        echo_location /sub "a=1&b=2";
    }
 
    location /sub {
        echo "sub args: $args";
    }

這裡在 /main 介面中,先用 echo 指令輸出當前請求的 $args 變數的值,接著再用 echo_location 指令發起子請求 /sub. 這裡值得注意的是,我們在 echo_location 語句中除了通過第一個參數指定“子請求”的 URI 之外,還提供了第二個參數,用以指定該“子請求”的 URL 參數串(即 a=1&b=2)。最後我們定義了 /sub 介面,在裡面輸出了一下 $args 的值。請求 /main 介面的結果如下:

    $ curl ‘http://localhost:8080/main?c=3‘
    main args: c=3
    sub args: a=1&b=2

顯然,當 $args 用在“主請求” /main 中時,輸出的就是“主請求”的 URL 參數串,c=3;而當用在“子請求” /sub 中時,輸出的則是“子請求”的參數串,a=1&b=2。這種行為正符合我們的直覺。

 

    與 $args 類似,內建變數 $uri 用在“子請求”中時,其“取處理常式”也會正確返回當前“子請求”解析過的 URI:

    location /main {
        echo "main uri: $uri";
        echo_location /sub;
    }
 
    location /sub {
        echo "sub uri: $uri";
    }

請求 /main 的結果是

    $ curl ‘http://localhost:8080/main‘
    main uri: /main
    sub uri: /sub

這依然是我們所期望的。

 

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

 

    變數 $request_method 在讀取時,總是會得到“主請求”的要求方法,比如 GET、POST 之類。我們來測試一下:

    location /main {
        echo "main method: $request_method";
        echo_location /sub;
    }
 
    location /sub {
        echo "sub method: $request_method";
    }

在這個例子裡,/main 和 /sub 介面都會分別輸出 $request_method 的值。同時,我們在 /main 介面裡利用echo_location 指令發起一個到 /sub 介面的 GET “子請求”。我們現在利用 curl 命令列工具來發起一個到/main 介面的 POST 請求:

    $ curl --data hello ‘http://localhost:8080/main‘
    main method: POST
    sub method: 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 {
        echo_location /sub;
        echo "main method: $request_method";
    }
 
    location /sub {
        echo "sub method: $request_method";
    }

讓我們重新測試一下:

    $ curl --data hello ‘http://localhost:8080/main‘
    sub method: POST
    main method: POST

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

 

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

    location /main {
        echo "main method: $echo_request_method";
        echo_location /sub;
    }
 
    location /sub {
        echo "sub method: $echo_request_method";
    }

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

    $ curl --data hello ‘http://localhost:8080/main‘
    main method: POST
    sub method: GET

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

 

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

 

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

    map $uri $tag {
        default     0;
        /main       1;
        /sub        2;
    }
 
    server {
        listen 8080;
 
        location /main {
            auth_request /sub;
            echo "main tag: $tag";
        }
 
        location /sub {
            echo "sub tag: $tag";
        }
    }

這裡我們使用久違了的 map 指令來把內建變數 $uri 的值對應到使用者變數 $tag 上。當 $uri 的值為 /main時,則賦予 $tag 值 1,當 $uri 取值 /sub 時,則賦予 $tag 值 2,其他情況都賦 0. 接著,我們在 /main介面中先用 ngx_auth_request 模組的 auth_request 指令發起到 /sub 介面的子請求,然後再輸出變數 $tag的值。而在 /sub 介面中,我們直接輸出變數 $tag. 猜猜看,如果我們提供者 /main,將會得到什麼樣的輸出呢?

    $ curl ‘http://localhost:8080/main‘
    main tag: 2

咦?我們不是分明把 /main 這個值對應到 1 上的嗎?為什麼實際輸出的是 /sub 映射的結果 2 呢?

 

    其實道理很簡單,因為我們的 $tag 變數在“子請求” /sub 中首先被讀取,於是在那裡計算出了值 2(因為 $uri 在那裡取值 /sub,而根據 map 映射規則,$tag 應當取值 2),從此就被 $tag 的值容器給緩衝住了。而 auth_request 發起的“子請求”又是與“父請求”共用一套變數的,於是當 Nginx 的執行流回到“父請求”輸出 $tag 變數的值時,Nginx 就直接返回緩衝住的結果 2 了。這樣的結果確實太意外了。

 

    從這個例子我們再次看到,父子請求間的變數共用,實在不是一個好主意。

 

    (未完待續)

Nginx 變數漫談(六)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.