摘要:本文同步在個人博客上,歡迎關注這篇文章整理了在前端開發中,在開發環境下使用重寫及代理功能的方法。表示該規則是使用正則定義的,區分大小寫。因此牢記在上下文中使用,而在上下文中使用。
本文同步在個人博客shymean.com上,歡迎關注
這篇文章整理了在前端開發中,在開發環境下使用nginx重寫uri及代理功能的方法。
參考
nginx中文文檔
前端開發者必備的 Nginx 知識
Nginx與前端開發
location匹配
參考
一文弄懂Nginx的location匹配
多個項目共用同一個域名時,往往需要根據url將請求轉發到不同的項目上,此時需要配置location
location [ = | ~ | ~* | ^~ ] uri { ... }
在location指令和uri請求中間可以添加可選的修飾符,四種修飾符的含義分別如下
= 表示精確匹配。只有請求的url路徑與后面的字符串完全相等時,才會命中。
~ 表示該規則是使用正則定義的,區分大小寫。
~* 表示該規則是使用正則定義的,不區分大小寫。
^~ 表示如果該符號后面的字符是最佳匹配,采用該規則,不再進行后續的查找。
當不添加任何修飾符時,則使用請求資源路徑與配置的uri進行前綴匹配:如果請求資源路徑以配置的uri開頭,則表示可命中該規則。
需要注意的是,不能同時存在相同的uri匹配規則,即
location /img/ {} location ^~ /img/ {}
會提示錯誤
nginx: [emerg] duplicate location "/img/" in /usr/local/etc/nginx/servers/test.conf:61
注意uri末尾帶斜杠與不帶斜杠會被視作兩條匹配規則,他們的處理也是不一樣的,在下面的例子中也有提到(上面引用的文章里面關于這點描述貌似錯了)。
location的具體的匹配過程為
首先先檢查無修飾符的規則,并進行前綴匹配,選擇最長匹配的項并記錄下來。
然后檢查是否存在精確匹配的location(使用了=修飾符),如果存在,則結束查找,使用它的配置。
然后查找是否存在最優匹配,如果存在,則選擇最優匹配結果最長的項,并使用它的配置
然后按順序查找使用正則修飾符定義的location,如果匹配,則停止查找,使用它定義的配置。
最后,如果沒有匹配的正則location,則使用前面記錄的最長匹配前綴字符location。
從上面的匹配過程可以看出,匹配順序是:
精準匹配 > 最優匹配 > 按順序的正則匹配 > 最長的前綴匹配
接下來我們將編寫一些測試來練習location,其大概形式如下
# uri表示location的需要匹配的規則 location uri { # config表示某個config配置 [ config ] }
為了驗證"存在多個location時,到底是哪個location匹配規則生效"的問題,我們可以將請求轉發到某個不存在的文件上,然后使用錯誤日志查看某個請求對應的location是啥
現在,讓我們開始動手測試吧
server { listen 80; server_name test.com; index index.html; error_log /usr/local/etc/nginx/logs/error.log error; location / { root /Users/Txm/A/; } location /img { root /Users/Txm/B/; } location /img/ { root /Users/Txm/C/; } }
接下來準備了一些請求鏈接,通過訪問并查看日志,就可以知道請求到底去了那個地方
請求url | 匹配規則 | 備注 |
---|---|---|
test.com/s/img/1.png | A | 只有/符合前綴匹配規則 |
test.com/img212/1.pn… | B | 只有/img符合前綴匹配,/img/不符合 |
test.com/img/1/1.png | C | /img/和/img末尾有無/是有區別的 |
test.com/img/1.png | C | /img/比/img的前綴匹配更長,更符合 |
接下來測試正則匹配,往上面的server模塊中添加如下location配置
location ~* /im { root /Users/Txm/D/; } location ~* .png { root /Users/Txm/E/; }
此時再訪問/img212/1.png、/img/1/1.png和/img/1.png這三個鏈接,都會命中規則D,如上面的匹配規則:
正則匹配的優先級大于前綴匹配,因此不會匹配規則ABC
正則匹配是按照定義的順序進行匹配的,如果命中,則停止查找,因此雖然規則E也符合規則,但是沒有被命中
我們繼續來測試^~最優匹配
location ~ /bund { root /Users/Txm/F/; } location ~ /bundle/1 { root /Users/Txm/F1/; } location ^~ /bundl { root /Users/Txm/G/; } location ^~ /bundle/ { root /Users/Txm/G1/; } location ~ .js$ { root /Users/Txm/H/; }
我們用http://test.com/bundle/1.js這個鏈接來進行測試,理論上來說這個鏈接符合上述所有規則,實際上該請求命中規則G1,我的理解是:
最優匹配的優先級高于正則匹配
存在多個最優匹配的規則時,命中匹配長度最長的規則
因此此處命中了G1,如果刪除G1,則根據優先級應該匹配G,繼續刪除G,此時狀態回到了上面的正則匹配,根據正則按順序匹配的規則,此時應該匹配F。
最后,讓我們測試一下精準匹配,精準匹配表示請求的路徑與配置的uri要完全一致才可以
location = /img { root /Users/Txm/I/; }
請求url | 匹配規則 | 備注 |
---|---|---|
test.com/img | I | 精準匹配優先級最高 |
test.com/img/ | D | 不滿足精準匹配和最優匹配,在順序上滿足正則匹配D |
root 和alias
參考
nginx的location、root、alias指令用法和區別
上面整理了location的語法和匹配規則,但是location并不會改變請求的uri,實際上請求到的文件路徑是由其他指令進行處理的。
root與alias都可以用來指定文件的路徑,他們的主要區別在于nginx如何解釋location后面的uri,這會使兩者分別以不同的方式將請求映射到服務器文件上
root的處理方式:root路徑+location路徑
alias的處理方式:使用alias路徑替換location路徑
以http://test.com/test/index.html請求為例,
server_name test.com; location /test/ { # 當配置為root時,實際請求的Users/Txm/nginx_test/test/index.html # root可以放在 http、server、location、if等多個配置段下面 # root /Users/Txm/nginx_test/; # 當配置為alias時,實際請求的是/Users/Txm/nginx_test/index.html # alias只能放在location中 # 注意alias末尾必須跟/ alias /Users/Txm/nginx_test/; }
換句話說,alias是一個目錄別名的定義,root則是最上層目錄的定義。
結合location,使用root或alias就可以把請求的url映射到磁盤上對應的真實目錄。但是在某些時候,僅僅有目錄卻沒有真實文件也是不夠的(最常見的場景大概是開發環境沒有生成環境文件名的緩存hash值和.min.等后綴),此時可以通過rewrite重寫請求uri路徑。
rewrite
參考
rewrite文檔
一篇文章說透Nginx的rewrite模塊
rewrite模塊允許使用正則修改請求的URI,發起內部跳轉再匹配location,或者直接做30x重定向返回客戶端。
rewrite regex replacement [flag]
其中的regex是PCRE風格的正則,rewrite的運行規則如下
如果regex匹配當前請求的uri,則replacement 會被當作是新的uri參與后續處理。
如果在server級別設置該選項,那么他們將在location之前生效。
如果新URI字符中有關于協議的任何東西,比如http://或者https://等,則終止處理并直接響應302
如果同一個上下文中(server、location、if)有多個能夠匹配uri的rewrite正則,則會根據rewrite指令出現的先后順序連續進行重寫替換,并將替換后的replacement當作新的uri參與后續處理
如果想要終止匹配,可以使用第三個參數flag,其取值如下
last表示停止處理任何rewrite相關的指令,并用替換后的uri開始下一輪的location匹配
break表示停止處理任何rewrite相關的指令,且直接使用該uri來處理請求,不再進行location匹配
redirect如果不包含協議,且是一個新的uri,則用新的uri匹配的location去處理請求,不會進行30x跳轉;但是他也可以直接返回30x,讓瀏覽器自己進行重定向請求
permanent與redirect相同,區別在于它是直接返回301永久重定向
需要注意的是last和break的區別,如果在location中使用location,則會再次以新的URI重新發起重定向,并再次進行location匹配,如果新的uri和舊的uri都再次匹配到一個相同的location,就會發生死循環,當這種循環超過10次時,nginx就會返回500。因此牢記:在server上下文中使用last,而在location上下文中使用break。
接下來讓我們通過一些例子來驗證rewrite的規則。
server { listen 80; server_name test2.com; index index.html; root /Users/Txm/nginx_test/; access_log /usr/local/etc/nginx/logs/test2.access.log; error_log /usr/local/etc/nginx/logs/error2.log error; rewrite ^/baidu http://www.baidu.com; rewrite ^/bai http://image.baidu.com; return 403; }
請求url | 最終rewrite的uri | 備注 |
---|---|---|
test2.com/bai | image.baidu.com | |
test2.com/baidu | www.baidu.com | 匹配到baidu,則直接返回 |
然后增加location |
location /bundle/ { rewrite ^/bundle/(.*?)$ /dist/$1 break; } location /dist/ { rewrite ^/dist/(.*?)$ /src/$1 break; }
請求url | 最終rewrite的uri | 備注 |
---|---|---|
test2.com/bundle/1.js | /Users/Txm/nginx_test/dist/1.js | break不再進行location匹配 |
test2.com/dist/1.js | /Users/Txm/nginx_test/src/1.js |
然后將/bundle/里面的標識符break修改為last,
location /bundle/ { rewrite ^/bundle/(.*?)$ /dist/$1 break; }
則可以看見最終的uri跟/dist/一樣,重寫成了/Users/Txm/nginx_test/src/1.js,因此牢記在location中使用break的警告。
通過rewrite,我們可以重寫路徑,將一些原本不存在的文件修改為實際存在磁盤上的文件,下面是一個去掉.min后綴和-hash后綴的重寫規則,可以將歷史項目中使用grunt打包的靜態資源映射到src對應源文件去
rewrite ^(.*?)((.min)?-.*?)(..*?)$ $1$4 last;
nginx代理的一些用法
反向代理是為服務端服務的,反向代理可以幫助服務器接收來自客戶端的請求,幫助服務器做請求轉發,負載均衡等。
反向代理對服務端是透明的,對我們是非透明的,即我們并不知道自己訪問的是代理服務器,而服務器知道反向代理在為他服務。
正向代理是為我們服務的,即為客戶端服務的,客戶端可以根據正向代理訪問到它本身無法訪問到的服務器資源,一種應用場景是:假設公司的局域網不允許訪問外網,則局域網中的客戶端要訪問Internet,則需要通過代理服務器來訪問。
正向代理對我們是透明的,對服務端是非透明的,即服務端并不知道自己收到的是來自代理的訪問還是來自真實客戶端的訪問。
下面是在前端開發中,可以使用nginx代理實現的一些功能場景
在本地開發環境模擬線上請求場景
代碼運行環境可以分為本地開發環境、測試環境和線上生產環境。以現有開發流程中某個web項目為例:
開發時是本地啟動的3015端口號
測試時在提測平臺根據工單拉取相關服務,通過k8s部署在容器并運行服務,最終輸入一系列的host列表
工單可上線時,通過發布平臺合并代碼到develop及master,然后按命令步驟部署到生產服務器上
假設訪問服務的鏈接是http://m.xxx.com/h5/test為了達到三個環境相同的訪問場景,一般來說需要做下面處理:
需要訪問生產環境時,直接在瀏覽器輸入當前鏈接即可,線上的域名一般會預先解析到對應的服務器上ip上,默認情況下輸入域名訪問到的就是生產環境的服務。
可以把測試環境理解成生產環境的鏡像,應用也是部署在一臺服務器上,需要訪問測試環境時,我們就需要將域名指向測試服務器的ip地址
在本地開發時,如果需要通過域名訪問本地開發環境,則可以通過修改host,然后將域名請求域名代理到本地node服務
127.0.0.1 xxx.com
然后修改nginx配置,通過nginx將xxx.com域名的請求代理到本地的服務端口號上面
server { listen 80; server_name m.xxx.com; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_pass http://127.0.0.1:3015; } }
這里推薦一個超級好用的host修改工具:SwitchHosts,可以很方便地在本地、測試環境、生成環境進行域名切換。
將線上請求資源文件映射到本地
由于生產環境的靜態資源如樣式表、JavaScript文件一般是經過壓縮和打包的,為了緩存控制甚至為文件名添加了hash后綴,如果在某些時候需要對線上代碼進行調試,一般由兩種方式
第一種方式是通過charles等抓包工具,將請求的文件通過Map Local的方式映射到本地磁盤上,此時請求資源實際返回的是本地文件,然后可以通過修改本地文件來達到調試的目的。這種方式適合未經過代碼合并打包處理的文件,在維護一些使用requirejs、seajs等模塊管理工具加載文件的老項目比較有用。
第二種將靜態資源域名配置到本地,然后通過nginx的location、alias和rewrite實現靜態資源文件代理
server { listen 80; server_name cnd.shymean.com; location /wargame/ { alias /Users/Txm/github/wargame/dist/; # 移除請求如jquery.min-a2dfg.js鏈接上的hash值a2dfg # http://cnd.shymean.com/wargame/jquery.min-a2dfg.js 實際返回文件為 /Users/Txm/github/wargame/dist/jquery.min.js rewrite ^(.+)/(.+)[-.]w+.(w+)$ $1/$2.$3 last; } location /blog/ { # 直接映射到本地目錄 alias /Users/Txm/blog/public/; } }
nginx配置跨域
正向代理一個經典的場景是使用nginx繞開瀏覽器的跨域限制,在前后端分離的開發調試過程中,本地起的前端功能可能是localhost:port域名形式,一般情況下會使用本地mock數據進行開發,如果需要直接訪問遠程接口進行聯調,則會遇見跨域問題。
這種場景主要是在開發時頁面域名(localhost)和接口域名(api.xxx.com)不一致導致的,通過nginx的location和proxy_pass,將指定請求代理到對應服務商,從瀏覽器的角度來看,請求的都是同一個域名,也就不存在跨域限制了
server { listen 80; server_name shymean.com; location /api/ { # 局域網中后臺開發的本地服務,用于聯調 proxy_pass http://192.168.132.253:7654; } }
另外一種場景是:出于性能優化的目的,一些靜態資源等往往使用多帶帶的cdn域名,當業務需要使用跨域資源(如在canvas上繪制網絡圖片最后需要調用canvas.toDataURL),此時也會存在跨域限制,通過nginx的add_header功能可以非常簡單地實現CORS。
# 靜態資源 map $http_origin $imgCorsHost { default 0; "~http://shymean.com" http://shymean.com; "~https://shymean.com" https://shymean.com; } server { listen 80; listen 443; server_name cdn.shymean.com; root /Users/Txm/Desktop/blog/static; add_header Access-Control-Allow-Origin $imgCorsHost; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; add_header Access-Control-Allow-Headers "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization"; if ($request_method = "OPTIONS") { return 204; } }
從上面的例子中可以看到,如果需要指定Access-Control-Allow-Origin為多個域名,可以使用nginx的map結構。
通過nginx修改響應的內容
有時會有一些只在開發環境下生效的邏輯,如引入mock代碼、向移動端頁面增加eruda調試工具等。
在本地開發時,可以通過運行時指定環境變量為development來判斷是否為生產環境,從而修改響應內容
<% if(!app.isProduction()) { %> <%- IncludeAssets("start:statics/h5/act/js/_mock.js") %> <% } %>
上面這種方式,在代碼中增加額外的環境判斷代碼,然后注入mock代碼,通過nginx通過攔截并修改響應內容,可以同樣實現這個功能。在最開始實現這個需求的時候,花了大量時間來查閱相關的實現方法,發現最簡單的方式應該是通過openresty來實現。參考:
Lua + OpenResty修改response body
location ~* 1.js$ { body_filter_by_lua_file /usr/local/etc/openresty/lua/hello.lua; }
然后新增hello.lua腳本,編寫相關邏輯
-- body_filter_by_lua, body filter模塊,ngx.arg[1]代表輸入的chunk,ngx.arg[2]代表當前chunk是否為last local chunk, eof = ngx.arg[1], ngx.arg[2] local buffered = ngx.ctx.buffered if not buffered then buffered = {} -- XXX we can use table.new here ngx.ctx.buffered = buffered end if chunk ~= "" then buffered[#buffered + 1] = chunk ngx.arg[1] = nil end if eof then local whole = table.concat(buffered) ngx.ctx.buffered = nil whole = string.gsub(whole, "console.log", "console.warn") ngx.arg[1] = whole end
需要注意的是,修改后的whole內容長度,不能超過之前原本的內容長度,否則后面的數據會被截取掉,估計跟content-length頭部有關。關于openresty和lua,之前接觸的也不是很多,后面會繼續深入學習一下。
小結
本文總結了幾個與nginx匹配uri相關的一些指令,包括
使用location匹配uri
使用root和alias指定請求資源目錄
使用rewrite重寫uri及后續匹配規則
結合uri和代理,我們就可以把請求導向自己需要的資源上去,從而滿足開發環境的多種開發需求。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/6676.html
摘要:啟用或禁用反應是否啟用壓縮響應報文不是所有瀏覽器都支持壓縮機制設置一個響應的壓縮級別??山邮艿闹翟诘街g。 博文參考 http://wiki.nginx.org/HttpUpstreamConsistentHash http://wiki.nginx.org/HttpUpstreamFairModule http://wiki.nginx.org/HttpUpstreamRequest...
摘要:啟用或禁用反應是否啟用壓縮響應報文不是所有瀏覽器都支持壓縮機制設置一個響應的壓縮級別。可接受的值在到之間。 博文參考 http://wiki.nginx.org/HttpUpstreamConsistentHash http://wiki.nginx.org/HttpUpstreamFairModule http://wiki.nginx.org/HttpUpstreamRequest...
摘要:安裝簡單配置簡潔啟動快速便捷支持熱部署支持擁有高度模塊化的設計。備注在版本之前,不能在中使用權重。不能與同時使用。當有服務器需要剔除,必須手動掉。表示把請求轉發給連接數較少的后端服務器。表示當前的暫時不參與負載均衡。表示預留的備份機器。 本文已同步到專業技術網站 www.sufaith.com, 該網站專注于前后端開發技術與經驗分享, 包含Web開發、Nodejs、Python、Lin...
摘要:語法如果相對域名或參數字符串起作用,可以使用全局變量匹配,也可以使用反向代理。不能返回限速,可以通過指令設置如果請求的文件名不存在,則反向代理到。 location正則寫法 一個示例: location = / { # 精確匹配 / ,主機名后面不能帶任何字符串 [ configuration A ] } location / { # 因為所有的地址都以 / 開...
閱讀 823·2019-08-30 15:55
閱讀 1406·2019-08-30 13:55
閱讀 1982·2019-08-29 17:13
閱讀 2840·2019-08-29 15:42
閱讀 1329·2019-08-26 14:04
閱讀 1016·2019-08-26 13:31
閱讀 3270·2019-08-26 11:34
閱讀 828·2019-08-23 18:25