openresty開發系列33--openresty執行流程之3重寫rewrite和重定向

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的順序html

相關文章
相關標籤/搜索