openresty開發系列33--openresty執行流程之3重寫rewrite和重定向
重寫rewrite階段
1)重定向
2)內部,偽靜態
先介紹一下if,rewrite指令
一)if指令
語法:if (condition){...}
默認值:無
作用域:server,location
對給定的條件condition進行判斷。如果為真,大括號內的指令將被執行。
上面的if和(之間需要留空格,否則會報錯。
1)條件可以為一個變量
如果一個變量名進行條件判斷,空字符串'' 或 字符串為'0',都表示為假 false
location /api {
set $a '11111';
if ($a){
return 200 "11111";
}
# 如果沒有匹配到上面的就返回 200 2222222222
return 200 "2222222222";
}
2)條件為表達式
正則表達式匹配:
= ,!= 比較的一個變量和字符串
~:與指定正則表達式模式匹配時返回“真”,判斷匹配與否時區分字符大小寫;
~*:與指定正則表達式模式匹配時返回“真”,判斷匹配與否時不區分字符大小寫;
!~:與指定正則表達式模式不匹配時返回“真”,判斷匹配與否時區分字符大小寫;
!~*:與指定正則表達式模式不匹配時返回“真”,判斷匹配與否時不區分字符大小寫;
location /api {
if ($request_uri ~* "/api/[0-9]+") {
return 200 "api";
}
}
3) 文件及目錄匹配判斷:
-f, !-f:判斷指定的路徑是否為存在且為文件;
-d, !-d:判斷指定的路徑是否為存在且為目錄;
-e, !-e:判斷指定的路徑是否存在,文件或目錄均可;
-x, !-x:判斷指定路徑的文件是否存在且可執行;
location /api {
if (-f "/usr/local/lua/test.lua") {
return 200 "test存在";
}
}
注意:
1)nginx if 沒有對應的else
2)if 表達式中 是不能用 && ||
4)nginx的配置中不支持if條件的邏輯與&& 邏輯或|| 運算等邏輯運算符
而且不支持if的嵌套語法,否則會報錯。
如:
location /api {
if ( $arg_a != '1' && $arg_a != '2' ) {
rewrite ^/(.*)$ https://www.baidu.com permanent;
}
}
會報錯nginx: [emerg] invalid condition
修改為:
set $flag 0;
if ($arg_a != '1') {
set $flag "${flag}1"; ---> 01
}
if ($arg_a != '2'){
set $flag "${flag}1"; ---> 011
}
if ($flag = "011"){
rewrite ^/(.*)$ https://www.baidu.com permanent;
}
二)Rewrite規則
1)http status code 301 與 302區別
301 redirect: 301 代表永久性轉移(Permanently Moved)
302 redirect: 302 代表暫時性轉移(Temporarily Moved)
詳細來說,301和302狀態碼都表示重定向,就是說瀏覽器在拿到服務器返回的這個狀態碼后會自動跳轉到一個
新的URL地址,這個地址可以從響應的Location首部中獲取,用戶看到的效果就是他輸入的地址A瞬間變成了另一個地址B
—這是它們的共同點。他們的不同在於。301表示舊地址A的資源已經被永久地移除了(這個資源不可訪問了),
搜索引擎在抓取新內容的同時也將舊的網址交換為重定向之后的網址;302表示舊地址A的資源還在(仍然可以訪問),
這個重定向只是臨時地從舊地址A跳轉到地址B,搜索引擎會抓取新的內容而保存舊的網址。
2)rewrite指令
語法rewrite regex replacement [flag];
regex:perl兼容正則表達式語句進行規則匹配
replacement:將正則匹配的內容替換成replacement
flag標記:rewrite支持的flag標記
rewrite功能就是,使用nginx提供的全局變量或自己設置的變量,結合正則表達式和標志位實現url重寫以及重定向。
rewrite只能放在server{},location{},if{}中,並且只能對域名后邊的除去傳遞的參數外的字符串起作用,
例如http://www.a.com/api/index.jsp?id=1&u=str 只對api/index.jsp重寫。
正則表達式元字符:
. :匹配除換行符以外的任意字符
? :重復0次或1次
+ :重復1次或更多次
* :重復0次或更多次
\d :匹配數字
^ :匹配字符串的開始字符
$ :匹配字符串的結束字符
{n} :重復n次
{n,} :重復n次或更多次
[c] :匹配單個字符c
[a-z] :匹配a-z小寫字母的任意一個
在rewrite中,如果使用小括號(),那么在小括號之間匹配的內容,可以在后面通過$1來引用,
$2表示的是前面第二個()里的內容
flag標志位
last : 相當於Apache的[L]標記,表示完成rewrite
break : 停止執行當前虛擬主機的后續rewrite指令集
redirect : 返回302臨時重定向,地址欄會顯示跳轉后的地址
permanent : 返回301永久重定向,地址欄會顯示跳轉后的地址
-----------------------------------
location /foo {
rewrite ^ /bar redirect;
}
location /bar {
echo "bar";
}
-----------------------------------
location /api {
rewrite ^/(.*) https://www.baidu.com/s?wd=$1 permanent;
}
請求:http://192.168.31.150/api ---> $1 = api
請求:http://192.168.31.150/api/pro ---> $1 = api/pro
-----------------------------------
last 和 break 區別:
last: 停止當前這個請求,並根據rewrite匹配的規則重新發起一個請求。新請求又從第一階段開始執行…
break:相對last,break並不會重新發起一個請求,只是跳過當前的rewrite階段,並執行本請求后續的執行階段…
break與last都停止處理后續rewrite指令集,不同之處在與last會重新發起新的請求,
而break不會。當請求break時,如匹配內容存在的話,可以直接請求成功
案例:
location /break/ {
rewrite ^/break/(.*) /test/$1 break; #----break;/test/$1 會在根目錄下去查找/test/$1文件,即去 /data/www/html/test/目錄下找 $1 文件
}
location /last/ {
rewrite ^/last/(.*) /test/$1 last; #----last;/test/$1 會重新走一遍location匹配流程
}
location /test/ {
echo "test page";
}
---------------------------------
偽靜態
location = /pro {
echo "pro_$arg_pid";
}
location /{
rewrite ^/product/(\d+).html$ /pro?pid=$1 last;
}
應用場景:
a)可以調整用戶瀏覽的URL,看起來更規范,合乎開發及產品人員的需求。
b)為了讓搜索引擎搜錄網站內容及用戶體驗更好,企業會將動態URL地址偽裝成靜態地址提供服務。
c)網址換新域名后,讓舊的訪問跳轉到新的域名上。例如,訪問京東的360buy.com會跳轉到jd.com
d)根據特殊變量、目錄、客戶端的信息進行URL調整等
可應用在server,location,if標簽
執行順序是:
a)執行server塊的rewrite指令
b)執行location匹配
c)執行選定的location中的rewrite指令
如果其中某步URI被重寫,則重新循環執行a-c,直到找到真實存在的文件;循環超過10次,
則返回500 Internal Server Error錯誤。
三)rewrite_by_lua
語法:rewrite_by_lua <lua-script-str>
語境:http、server、location、location if
階段:rewrite tail
作為rewrite階段的處理,為每個請求執行指定的lua代碼。注意這個處理是在標准HtpRewriteModule之后進行的:
執行內部URL重寫或者外部重定向,典型的如偽靜態化的URL重寫。其默認執行在rewrite處理階段的最后
1) ngx.redirect ---重定向
語法:ngx.redirect(uri, status?)
該方法會給客戶端返回一個301/302重定向,具體是301還是302取決於設定的status值,
如果不指定status值,默認是返回302
rewrite ^ /bar redirect; 等價於 ngx.redirect("https://www.baidu.com", 302)
rewrite ^ /bar permanent; 等價於 ngx.redirect("https://www.baidu.com", 301)
--------------nginx.conf配置文件
location /lua_rewrite_1 {
rewrite_by_lua_block {
if ngx.req.get_uri_args()["jump"] == "1" then
return ngx.redirect("https://www.baidu.com", 302)
end
}
echo "no rewrite";
}
當我們請求http://10.11.0.215/lua_rewrite_1時發現沒有跳轉,
而請求http://10.11.0.215/lua_rewrite_1?jump=1時發現跳轉到百度首頁了。
此處需要 301/302跳轉根據自己需求定義。
----------------------------------------------
在取個案例
location /foo {
set $a 12;
set $b '';
rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1';
if ($b = '13') {
rewrite ^ /bar redirect;
break;
}
echo "res = $b";
}
location /bar {
echo "bar";
}
因為if會在rewrite_by_lua之前運行,所以判斷將不成立。正確的寫法應該是這樣:
location /foo {
set $a 12;
set $b '';
rewrite_by_lua_block {
ngx.var.b = tonumber(ngx.var.a) + 1
if tonumber(ngx.var.b) == 13 then
return ngx.redirect("/bar");
end
}
echo "res = $b";
}
----------------------------------------------
2)ngx.req.set_uri ---內部重寫
語法: ngx.req.set_uri(uri, jump?)
通過參數uri重寫當前請求的uri;參數jump,表明是否進行locations的重新匹配。
當jump為true時,調用ngx.req.set_uri后,nginx將會根據修改后的uri,重新匹配新的locations;
如果jump為false,將不會進行locations的重新匹配,而僅僅是修改了當前請求的URI而已。jump的默認值為false。
rewrite ^ /lua_rewrite_3; 等價於 ngx.req.set_uri("/lua_rewrite_3", false);
rewrite ^ /lua_rewrite_3 break; 等價於 ngx.req.set_uri("/lua_rewrite_3", false);
rewrite ^ /lua_rewrite_4 last; 等價於 ngx.req.set_uri("/lua_rewrite_4", true);
---------------nginx.conf配置文件
location /foo {
rewrite_by_lua_block {
ngx.req.set_uri_args({a = 1, b = 2});
ngx.req.set_uri("/bar/index.html", true);
}
}
location /bar {
echo "bar uri : $uri,a : $arg_a,b : $arg_b";
}
ngx.req.set_uri_args:重寫請求參數;
注意 ngx.req.set_uri(uri, true) 時,ngx.req.set_uri_args的順序