Nginx變量使用方法詳解

標簽(空格分隔): 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ù)器的端口號。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末粹胯,一起剝皮案震驚了整個濱河市蓖柔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌风纠,老刑警劉巖况鸣,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異竹观,居然都是意外死亡镐捧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門臭增,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懂酱,“玉大人,你說我怎么就攤上這事速址⊥嫜妫” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵芍锚,是天一觀的道長昔园。 經(jīng)常有香客問我蔓榄,道長,這世上最難降的妖魔是什么默刚? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任甥郑,我火速辦了婚禮,結(jié)果婚禮上荤西,老公的妹妹穿的比我還像新娘澜搅。我一直安慰自己,他們只是感情好邪锌,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布勉躺。 她就那樣靜靜地躺著,像睡著了一般觅丰。 火紅的嫁衣襯著肌膚如雪饵溅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天妇萄,我揣著相機與錄音蜕企,去河邊找鬼。 笑死冠句,一個胖子當著我的面吹牛轻掩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播懦底,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼唇牧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了聚唐?” 一聲冷哼從身側(cè)響起奋构,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拱层,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宴咧,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡根灯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了掺栅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烙肺。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖氧卧,靈堂內(nèi)的尸體忽然破棺而出桃笙,到底是詐尸還是另有隱情,我是刑警寧澤沙绝,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布搏明,位于F島的核電站鼠锈,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏星著。R本人自食惡果不足惜购笆,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望虚循。 院中可真熱鬧同欠,春花似錦、人聲如沸横缔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茎刚。三九已至襟锐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間斗蒋,已是汗流浹背捌斧。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留泉沾,地道東北人捞蚂。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像跷究,于是被迫代替她去往敵國和親姓迅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容