Nginx 配置指令的執行順序(十一)

緊跟在 post-access 階段以後的是 try-files 階段。這個階段專門用於實現標準配置指令 try_files 的功能,並不支持 Nginx 模塊註冊處理程序。因爲 try_files 指令在許多 FastCGI 應用的配置中都有用到,因此咱們不妨在這裏簡單介紹一下。html

 

    try_files 指令接受兩個以上任意數量的參數,每一個參數都指定了一個 URI. 這裏假設配置了 N 個參數,則 Nginx 會在 try-files 階段,依次把前 N-1 個參數映射爲文件系統上的對象(文件或者目錄),而後檢查這些對象是否存在。一旦 Nginx 發現某個文件系統對象存在,就會在 try-files 階段把當前請求的 URI 改寫爲該對象所對應的參數 URI(但不會包含末尾的斜槓字符,也不會發生 「內部跳轉」)。若是前 N-1 個參數所對應的文件系統對象都不存在,try-files 階段就會當即發起「內部跳轉」到最後一個參數(即第 N 個參數)所指定的 URI.nginx

 

    前面在 (六) 和 (七) 中已經看到靜態資源服務模塊會把當前請求的 URI 映射到文件系統,經過 root配置指令所指定的「文檔根目錄」進行映射。例如,當「文檔根目錄」是 /var/www/ 的時候,請求 URI/foo/bar 會被映射爲文件 /var/www/foo/bar,而請求 URI /foo/baz/ 則會被映射爲目錄 /var/www/foo/baz/. 注意這裏是如何經過 URI 末尾的斜槓字符是否存在來區分「目錄」和「文件」的。咱們正在討論的 try_files配置指令使用一樣的規則來完成其各個參數 URI 到文件系統對象的映射。curl

 

    不妨來看下面這個例子:post

    root /var/www/;
 
    location /test {
        try_files /foo /bar/ /baz;
        echo "uri: $uri";
    }
 
    location /foo {
        echo foo;
    }
 
    location /bar/ {
        echo bar;
    }
 
    location /baz {
        echo baz;
    }

這裏經過 root 指令把「文檔根目錄」配置爲 /var/www/,若是你係統中的 /var/www/ 路徑下存放有重要數據,則能夠把它替換爲其餘任意路徑,但此路徑對運行 Nginx worker 進程的系統賬號至少有可讀權限。咱們在location /test 中使用了 try_files 配置指令,並提供了三個參數,/foo/bar/ 和 /baz. 根據前面對try_files 指令的介紹,咱們能夠知道,它會在 try-files 階段依次檢查前兩個參數 /foo 和 /bar/ 所對應的文件系統對象是否存在。url

 

    不妨先來作一組實驗。假設如今 /var/www/ 路徑下是空的,則第一個參數 /foo 映射成的文件/var/www/foo 是不存在的;一樣,對於第二個參數 /bar/ 所映射成的目錄 /var/www/bar/ 也是不存在的。因而此時 Nginx 會在 try-files 階段發起到最後一個參數所指定的 URI(即 /baz)的「內部跳轉」。實際的請求結果證明了這一點:debug

    $ curl localhost:8080/test
    baz

顯然,該請求最終和 location /baz 綁定在一塊兒,執行了輸出 baz 字符串的工做。上例中定義的 location /foo 和 location /bar/ 徹底不會參與這裏的運行過程,由於對於 try_files 的前 N-1 個參數,Nginx 只會檢查文件系統,而不會去執行 URI 與 location 之間的匹配。調試

 

    對於上面這個請求,Nginx 會產生相似下面這樣的「調試日誌」:日誌

    $ grep trying logs/error.log
    [debug] 3869#0: *1 trying to use file: "/foo" "/var/www/foo"
    [debug] 3869#0: *1 trying to use dir: "/bar" "/var/www/bar"
    [debug] 3869#0: *1 trying to use file: "/baz" "/var/www/baz"

經過這些信息能夠清楚地看到 try-files 階段發生的事情:Nginx 依次檢查了文件 /var/www/foo 和目錄/var/www/bar,末了又處理了最後一個參數 /baz. 這裏最後一條「調試信息」容易產生誤解,會讓人誤覺得 Nginx 也把最後一個參數 /baz 給映射成了文件系統對象進行檢查,事實並不是如此。當 try_files 指令處理到它的最後一個參數時,老是直接執行「內部跳轉」,而不論其對應的文件系統對象是否存在。code

 

    接下來再作一組實驗:在 /var/www/ 下建立一個名爲 foo 的文件,其內容爲 hello world(注意你須要有/var/www/ 目錄下的寫權限):htm

    $ echo 'hello world' > /var/www/foo

而後再請求 /test 接口:

    $ curl localhost:8080/test
    uri: /foo

這裏發生了什麼?咱們來看,try_files 指令的第一個參數 /foo 能夠映射爲文件 /var/www/foo,而 Nginx 在try-files 階段發現此文件確實存在,因而當即把當前請求的 URI 改寫爲這個參數的值,即 /foo,而且再也不繼續檢查後面的參數,而直接運行後面的請求處理階段。

 

    上面這個請求在 try-files 階段所產生的「調試日誌」以下:

    $ grep trying logs/error.log
    [debug] 4132#0: *1 trying to use file: "/foo" "/var/www/foo"

顯然,在 try-files 階段,Nginx 確實只檢查和處理了 /foo 這一個參數,然後面的參數都被「短路」掉了。

 

    相似地,假設咱們刪除剛纔建立的 /var/www/foo 文件,而在 /var/www/ 下建立一個名爲 bar 的子目錄:

    $ mkdir /var/www/bar

則請求 /test 的結果也是相似的:

    $ curl localhost:8080/test
    uri: /bar

在這種狀況下,Nginx 在 try-files 階段發現第一個參數 /foo 對應的文件不存在,就會轉向檢查第二個參數對應的文件系統對象(在這裏即是目錄 /var/www/bar/)。因爲此目錄存在,Nginx 就會把當前請求的 URI 改寫爲第二個參數的值,即 /bar(注意,原始參數值是 /bar/,但 try_files 會自動去除末尾的斜槓字符)。

 

    這一組實驗所產生的「調試日誌」以下:

    $ grep trying logs/error.log
    [debug] 4223#0: *1 trying to use file: "/foo" "/var/www/foo"
    [debug] 4223#0: *1 trying to use dir: "/bar" "/var/www/bar"

咱們看到,try_files 指令在這裏只檢查和處理了它的前兩個參數。

 

    經過前面這幾組實驗不難看到,try_files 指令本質上只是有條件地改寫當前請求的 URI,而這裏說的「條件」其實就是文件系統上的對象是否存在。當「條件」都不知足時,它就會無條件地發起一個指定的「內部跳轉」。固然,除了無條件地發起「內部跳轉」以外,try_files 指令還支持直接返回指定狀態碼的 HTTP 錯誤頁,例如:

    try_files /foo /bar/ =404;

這行配置是說,當 /foo 和 /bar/ 參數所對應的文件系統對象都不存在時,就直接返回 404 Not Found 錯誤頁。注意這裏它是如何使用等號字符前綴來標識 HTTP 狀態碼的。

相關文章
相關標籤/搜索