緊跟在 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 狀態碼的。