標簽(空格分隔): nginx
在 Nginx 配置中狈癞,變量只能存放一種類型的值分扎,那就是字符串。
1 自定義變量
1.1 配置 $foo=hello
server {
listen 8080;
server_name localhost;
location /test {
set $foo hello;
echo "foo: $foo";
}
}
輸出
[root@localhost html]# nginx -s reload
[root@localhost html]# curl localhost/test
foo: hello
1.2 輸出 $ 符
如果我們想通過 echo 指令直接輸出含有“美元符”($)的字符串闷叉,那么有沒有辦法把特殊的 $ 字符給轉(zhuǎn)義掉呢棍弄?答案是否定的。不過幸運的是倒彰,我們可以繞過這個限制审洞,比如通過不支持“變量插值”的模塊配置指令專門構(gòu)造出取值為 $ 的 Nginx 變量,然后再在 echo 中使用這個變量∶⒗剑看下面這個例子:
http {
...
geo $dollar {
default "$";
}
server {
...
location /test-dollar {
echo "This is a dollar sign: $dollar";
}
}
}
輸出
[root@localhost html]# nginx -s reload
[root@localhost html]# curl localhost/test-dollar
This is a dollar sign: \$
這里用到了標準模塊 ngx_geo 提供的配置指令 geo 來為變量 $dollar 賦予字符串 "$"仰剿,這樣我們在下面需要使用美元符的地方,就直接引用我們的 $dollar 變量就可以了痴晦。
1.3 使用大括號插值
在“變量插值”的上下文中南吮,還有一種特殊情況,即當引用的變量名之后緊跟著變量名的構(gòu)成字符時(比如后跟字母誊酌、數(shù)字以及下劃線)部凑,我們就需要使用特別的記法來消除歧義,例如:
server {
...
location /test-brace {
set $first "hello ";
echo "${first}world";
}
}
輸出
[root@localhost html]# nginx -s reload
[root@localhost html]# curl localhost/test-brace
hello world
這里碧浊,我們在 echo 配置指令的參數(shù)值中引用變量 $first 的時候涂邀,后面緊跟著 world 這個單詞,所以如果直接寫作 "$firstworld" 則 Nginx “變量插值”計算引擎會將之識別為引用了變量 $firstworld. 為了解決這個難題箱锐,Nginx 的字符串記法支持使用花括號在 $ 之后把變量名圍起來比勉,比如這里的 ${first}。
1.4 變量作用域
set 指令(以及前面提到的 geo 指令)不僅有賦值的功能驹止,它還有創(chuàng)建 Nginx 變量的副作用浩聋,即當作為賦值對象的變量尚不存在時,它會自動創(chuàng)建該變量臊恋。比如在上面這個例子中衣洁,如果 $a 這個變量尚未創(chuàng)建,則 set 指令會自動創(chuàng)建 $a 這個用戶變量捞镰。如果我們不創(chuàng)建就直接使用它的值闸与,則會報錯。
例如
server {
...
location /bad {
echo $foo;
}
}
此時 Nginx 服務(wù)器會拒絕加載配置:
[emerg] unknown "foo" variable
Nginx 變量的創(chuàng)建和賦值操作發(fā)生在全然不同的時間階段岸售,Nginx 變量的創(chuàng)建只能發(fā)生在 Nginx 配置加載的時候践樱,或者說 Nginx 啟動的時候,而賦值操作則只會發(fā)生在請求實際處理的時候凸丸。
這意味著不創(chuàng)建而直接使用變量會導(dǎo)致啟動失敗拷邢,同時也意味著我們無法在請求處理時動態(tài)地創(chuàng)建新的 Nginx 變量。
Nginx 變量一旦創(chuàng)建屎慢,其變量名的可見范圍就是整個 Nginx 配置瞭稼,甚至可以跨越不同虛擬主機的 server 配置塊。我們來看一個例子:
server {
listen 8080;
location /foo {
echo "foo = [$foo]";
}
location /bar {
set $foo 32;
echo "foo = [$foo]";
}
}
輸出
[root@localhost html]# curl 'http://localhost/foo'
foo = []
[root@localhost html]# curl 'http://localhost/bar'
foo = [32]
[root@localhost html]# curl 'http://localhost/foo'
foo = []
這里我們在 location /bar 中用 set 指令創(chuàng)建了變量 $foo腻惠,于是在整個配置文件中這個變量都是可見的环肘,因此我們可以在 location /foo 中直接引用這個變量而不用擔心 Nginx 會報錯。
從這個例子我們可以看到集灌,set 指令因為是在 location /bar 中使用的悔雹,所以賦值操作只會在訪問 /bar 的請求中執(zhí)行。而請求 /foo 接口時,我們總是得到空的 $foo值腌零,因為用戶變量未賦值就輸出的話梯找,得到的便是空字符串。
從這個例子我們可以窺見的另一個重要特性是益涧,Nginx 變量名的可見范圍雖然是整個配置锈锤,但每個請求都有所有變量的獨立副本,或者說都有各變量用來存放值的容器的獨立副本闲询,彼此互不干擾久免。比如前面我們請求了 /bar 接口后,$foo 變量被賦予了值 32嘹裂,但它絲毫不會影響后續(xù)對 /foo 接口的請求所對應(yīng)的 $foo 值(它仍然是空的M),因為各個請求都有自己獨立的 $foo 變量的副本寄狼。
對于 Nginx 新手來說,最常見的錯誤之一氨淌,就是將
Nginx 變量理解成某種在請求之間全局共享的東西泊愧,或者說“全局變量”。而事實上盛正,Nginx 變量的生命期是不可能跨越請求邊界的删咱。
關(guān)于 Nginx 變量的另一個常見誤區(qū)是認為變量容器的生命期,是與 location 配置塊綁定的豪筝。其實不然痰滋。我們來看一個涉及“內(nèi)部跳轉(zhuǎn)”的例子:
server {
listen 8080;
location /foo {
set $a hello;
echo_exec /bar;
}
location /bar {
echo "a = [$a]";
}
}
輸出
[root@localhost html]# curl localhost/foo
a = [hello]
這 里我們在 location /foo 中,使用第三方模塊 ngx_echo 提供的 echo_exec 配置指令续崖,發(fā)起到 location /bar 的“內(nèi)部跳轉(zhuǎn)”敲街。所謂“內(nèi)部跳轉(zhuǎn)”,就是在處理請求的過程中严望,于服務(wù)器內(nèi)部多艇,從一個 location 跳轉(zhuǎn)到另一個 location 的過程。這不同于利用 HTTP 狀態(tài)碼 301 和 302 所進行的“外部跳轉(zhuǎn)”像吻,因為后者是由 HTTP 客戶端配合進行跳轉(zhuǎn)的峻黍,而且在客戶端拨匆,用戶可以通過瀏覽器地址欄這樣的界面姆涩,看到請求的 URL 地址發(fā)生了變化。內(nèi)部跳轉(zhuǎn)和 Bourne Shell(或 Bash)中的 exec 命令很像惭每,都是“有去無回”骨饿。另一個相近的例子是 C 語言中的 goto 語句。
既然是內(nèi)部跳轉(zhuǎn),當前正在處理的請求就還是原來那個样刷,只是當前的 location 發(fā)生了變化仑扑,所以還是原來的那一套 Nginx 變量的容器副本。對應(yīng)到上例置鼻,如果我們請求的是 /foo 這個接口镇饮,那么整個工作流程是這樣的:先在 location /foo 中通過 set 指令將 $a 變量的值賦為字符串 hello,然后通過 echo_exec 指令發(fā)起內(nèi)部跳轉(zhuǎn)箕母,又進入到 location /bar 中储藐,再輸出 $a 變量的值。因為 $a 還是原來的 $a嘶是,所以我們可以期望得到 hello 這行輸出钙勃。測試證實了這一點:
但如果我們從客戶端直接訪問 /bar 接口,就會得到空的 $a 變量的值聂喇,因為它依賴于 location /foo 來對 $a 進行初始化辖源。
從上面這個例子我們看到,一個請求在其處理過程中希太,即使經(jīng)歷多個不同的 location 配置塊克饶,它使用的還是同一套 Nginx 變量的副本。這里誊辉,我們也首次涉及到了“內(nèi)部跳轉(zhuǎn)”這個概念矾湃。值得一提的是,標準 ngx_rewrite 模塊的 rewrite 配置指令其實也可以發(fā)起“內(nèi)部跳轉(zhuǎn)”堕澄,例如上面那個例子用 rewrite 配置指令可以改寫成下面這樣的形式:
server {
listen 8080;
location /foo {
set $a hello;
rewrite ^ /bar;
}
location /bar {
echo "a = [$a]";
}
}
從上面這個例子我們看到邀跃,Nginx 變量值容器的生命期是與當前正在處理的請求綁定的,而與 location 無關(guān)蛙紫。
2 內(nèi)建變量
Nginx 內(nèi)建變量最常見的用途就是獲取關(guān)于請求或響應(yīng)的各種信息拍屑。
2.1 $uri vs $request_uri
由 ngx_http_core 模塊提供的內(nèi)建變量 $uri局义,可以用來獲取當前請求的 URI(經(jīng)過解碼雇盖,并且不含請求參數(shù)),
而 $request_uri 則用來獲取請求最原始的 URI (未經(jīng)解碼拄显,并且包含請求參數(shù))裁蚁。
location /test-uri {
echo "uri = $uri";
echo "request_uri = $request_uri";
}
輸出
[root@localhost html]# nginx -s reload
[root@localhost html]# curl localhost/test-uri
uri = /test-uri
request_uri = /test-uri
[root@localhost html]# curl "localhost/test-uri?a=3&b=4"
uri = /test-uri
request_uri = /test-uri?a=3&b=4
[root@localhost html]# curl "localhost/test-uri/hello%20world?a=3&b=4"
uri = /test-uri/hello world
request_uri = /test-uri/hello%20world?a=3&b=4
2.2 $arg_XXX
另一個特別常用的內(nèi)建變量其實并不是單獨一個變量矢渊,而是有無限多變種的一群變量,即名字以 arg_ 開頭的所有變量枉证,我們估且稱之為 $arg_XXX 變量群矮男。
一個例子是 $arg_name,這個變量的值是當前請求中名為 name 的參數(shù)的值室谚,而且還是未解碼的原始形式的值毡鉴。
location /test-arg {
echo "name: $arg_name";
echo "class: $arg_class";
}
輸出
[root@localhost html]# nginx -s reload
[root@localhost html]# curl localhost/test-arg
name:
class:
[root@localhost html]# curl "localhost/test-arg?name=Tom&class=3"
name: Tom
class: 3
[root@localhost html]# curl "localhost/test-arg?name=hello%20world&class=9"
name: hello%20world
class: 9
2.3 $arg_XXX 不區(qū)分大小寫
其實 $arg_name 不僅可以匹配 name 參數(shù)崔泵,也可以匹配 NAME 參數(shù),抑或是 Name猪瞬,Nginx 會在匹配參數(shù)名之前憎瘸,自動把原始請求中的參數(shù)名調(diào)整為全部小寫的形式。
[root@localhost html]# curl "localhost/test-arg?NAME=Marry"
name: Marry
class:
[root@localhost html]# curl "localhost/test-arg?Name=Jimmy"
name: Jimmy
class:
2.4 對 uri 解碼
如果你想對 URI 參數(shù)值中的 %XX 這樣的編碼序列進行解碼陈瘦,可以使用第三方 ngx_set_misc 模塊提供的
location /test-unescape-uri {
set_unescape_uri $name $arg_name;
set_unescape_uri $class $arg_class;
echo "name: $name";
echo "class: $class";
}
現(xiàn)在我們再看一下效果:
[root@localhost html]# curl "localhost/test-arg?name=hello%20world&class=9"
name: hello world
class: 9
從這個例子我們同時可以看到幌甘,這個 set_unescape_uri 指令也像 set 指令那樣,擁有自動創(chuàng)建 Nginx 變量的功能痊项。后面我們還會專門介紹到 ngx_set_misc 模塊锅风。
像 $arg_XXX 這種類型的變量擁有無窮無盡種可能的名字,所以它們并不對應(yīng)任何存放值的容器鞍泉。而且這種變量在 Nginx 核心中是經(jīng)過特別處理的皱埠,第三方 Nginx 模塊是不能提供這樣充滿魔法的內(nèi)建變量的。
類 似 $arg_XXX 的內(nèi)建變量還有不少咖驮,比如用來取 cookie 值的 $cookie_XXX 變量群边器,用來取請求頭的 $http_XXX 變量群,以及用來取響應(yīng)頭的 $sent_http_XXX 變量群游沿。這里就不一一介紹了饰抒,感興趣的讀者可以參考 ngx_http_core 模塊的官方文檔。
2.4 全局變量
arg_PARAMETER #這個變量包含GET請求中诀黍,如果有變量PARAMETER時的值。
args #這個變量等于請求行中(GET請求)的參數(shù)仗处,如:foo=123&bar=blahblah;
binary_remote_addr #二進制的客戶地址眯勾。
body_bytes_sent #響應(yīng)時送出的body字節(jié)數(shù)數(shù)量。即使連接中斷婆誓,這個數(shù)據(jù)也是精確的吃环。
content_length #請求頭中的Content-length字段。
content_type #請求頭中的Content-Type字段洋幻。
cookie_COOKIE #cookie COOKIE變量的值
document_root #當前請求在root指令中指定的值郁轻。
document_uri #與uri相同。
host #請求主機頭字段文留,否則為服務(wù)器名稱好唯。
hostname #Set to themachine’s hostname as returned by gethostname
http_HEADER
is_args #如果有args參數(shù),這個變量等于”?”燥翅,否則等于”"骑篙,空值。
http_user_agent #客戶端agent信息
http_cookie #客戶端cookie信息
limit_rate #這個變量可以限制連接速率森书。
query_string #與args相同靶端。
request_body_file #客戶端請求主體信息的臨時文件名谎势。
request_method #客戶端請求的動作,通常為GET或POST杨名。
remote_addr #客戶端的IP地址脏榆。
remote_port #客戶端的端口。
remote_user #已經(jīng)經(jīng)過Auth Basic Module驗證的用戶名台谍。
request_completion #如果請求結(jié)束须喂,設(shè)置為OK. 當請求未結(jié)束或如果該請求不是請求鏈串的最后一個時,為空(Empty)典唇。
request_method #GET或POST
request_filename #當前請求的文件路徑镊折,由root或alias指令與URI請求生成。
request_uri #包含請求參數(shù)的原始URI介衔,不包含主機名恨胚,如:”/foo/bar.php?arg=baz”。不能修改炎咖。
scheme #HTTP方法(如http赃泡,https)。
server_protocol #請求使用的協(xié)議乘盼,通常是HTTP/1.0或HTTP/1.1升熊。
server_addr #服務(wù)器地址,在完成一次系統(tǒng)調(diào)用后可以確定這個值绸栅。
server_name #服務(wù)器名稱级野。
server_port #請求到達服務(wù)器的端口號。