在前後端分離的開發中,前端的靜態資源存於本機上,通過 localhost 訪問,如果直接呼叫服務端 api 則會因為跨域的問題不能正常訪問。解決跨域問題可以通過 JSONP 或者讓服務端設定 Access-Control-Allow-Origin
解除限制。但是並不是所有介面都可以這麼搞的,所以一般需要前端自己配置代理來解決跨域問題。
背景
最近接手了一個專案,代理配置過程很有意思,記錄下來。簡單描述下場景:projectA
是一個普通的頁面,但裡面部分割槽域和部分彈層是通過 iframe 的形式引用的 projectB
,而且後端介面限制出於安全考慮 只接受來自指定域名 (*.server.com
) 的請求,對於來自 localhost 或者 IP 的請求會被直接重定向至 server 域的登陸介面(server.com/login
)。需要同時啟動 projectA
(localhost:8087)和 projectB
(localhost:8085)兩個專案。
devServer.proxy大法
前端配置代理最常用的就是利用webpack的devServer.proxy了。
我首先嚐試的是在 projectA
的 devServer 中這樣配置:
devServer: {
proxy: {
'/api': { // 需要直接代理到線上環境的介面
target: 'http://ad.server.com',
changeOrigin: true,
headers: {
// 後端要校驗請求源,那改下 host 或者 origin 不就美滋滋了?
Host: 'ad.server.com',
Origin: 'ad.server.com'
},
},
'/apitest': { // 需要與後端聯調的介面
target: '10.8.0.1:9909',// 後端本地開發環境
},
'/iframe': {
target: 'http://localhost:8085',
}
// 另外還有 iframe 裡的 api 呼叫指向線上環境
}
}
複製程式碼
然而,後端介面殘酷地給跳轉到登陸介面去了 ?。猜想一下可能後端的介面是通過 cookie 來判斷當前登陸域的,從 localhost 過去的請求不帶 cookie。後來嘗試發現在 proxy的headers裡再增加 cookie 即可破跳轉,但是請求過去返回結果還是在報錯,後端表示仍然是認證失敗。
SwitchyOmega
既然從 localhost 訪問不行那隻好就從 server.com 這個域來訪問了。簡單的做法就是利用瀏覽器外掛 SwitchyOmega
配置代理。即 http://ad.server.com/*
的所有請求全部指向 localhost8087, http://ad.server.com/iframe/*
的請求全部指向 localhost8085, 然後將 api 的部分拎出來直接代理回線上。
坑爹的是有的靜態資源也不需要被代理到本地,例如 http://ad.server.com/static/*
本地可能根本就沒有,得重新指回到線上。這時候 iframe 裡的 projectB 又開始作妖了,裡面的靜態資源存於好幾個目錄,得挨著挨著配置,最後出來的完整配置就變得無比長。
SwitchyOmega
既可以對全域性進行代理設定,也可以對單獨域進行設定(點開 SwitchyOmega
看到下面那個漏斗便是)。這就導致了我的噩夢:期間我在做其他專案的時候進行一些小流量測試也是在用這個 SwitchyOmega
代理,也會涉及到對 server.com
域的修改,幾番折騰後已經分不清到底當前是在全域性代理還是對單獨域代理了(而且實際專案中不止 server.com
這一個域),每日疑惑便是:“現在到底把哪個域代理到哪去了?”
為了避免這種困惑最後只能左手一個 Chrome,右手一個 Canary(Chrome 的開發版),兩個不同瀏覽器分別獨立的 SwitchyOmega
配置。
Charles
後來感覺用 SwitchyOmega
這種上網工具來做開發不太專業,而且切換瀏覽器來代理的做法也實在不方便,就改用 Charels
。開啟代理後利用其 Map Remote
功能,可以配置具體哪些請求代理到哪個地方去。因為其介面是圖形化的,功能也比較強大,把 SwitchyOmega
裡的配置抄一份過來便可用了。比 SwitchyOmega
相比,不用再把本就應該代理到線上的靜態資源再寫一遍,所以配置會少一些。
最後讓我放棄 Charles 的原因是:實在太卡了 ?。同時配置了多個 url 代理的情況下,電腦風扇轉得飛起,請求一個被代理了的 url 半天回不了結果。而且 SwitchyOmega
和 Charles
都不能批量複製修改,得一個一個在圖形介面改,有的時候就很麻煩(比如將線上環境切到後端的開發環境)。
Nginx
想更靈活地複製修改配置,當然最理想的就是一個可編輯的配置檔案了。Nginx
正好可以非常方便的修改配置檔案,而且本地啟一個 Nginx
程式不會像 Charels
那麼耗資源。最後配置出來的 Nginx
大概長這個樣子:
http:{
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream localhost8083 {
server 127.0.0.1:8083;
keepalive 2000;
}
upstream localhost8087 {
server 127.0.0.1:8087;
keepalive 2000;
}
upstream rd {
server 10.8.0.1:9909;
keepalive 2000;
}
upstream server {
server ad.server.com;
keepalive 2000;
}
server {
listen 80;
# api介面用於和後端開發聯調
location ^~ /api/{
proxy_pass http://rd;
proxy_set_header Host $host:$server_port;
}
# 代理 projectA 的頁面
location ~ /(path1|path2|path3)/ {
proxy_pass http://localhost8087;
proxy_set_header Host $host:$server_port;
}
# 代理 projectA 的靜態資源
location ^~ /projectA_static/ {
proxy_pass http://localhost8087;
proxy_set_header Host $host:$server_port;
}
# 代理 projectA 的 hot-update
location ~ \.hot-update.js$ {
proxy_pass http://localhost8087;
proxy_set_header Host $host:$server_port;
}
# 代理 projectB 的 iframe 頁面
location ~ /(path_iframe1|path_iframe2|path_iframe3) {
proxy_pass http://localhost8085;
proxy_set_header Host $host:$server_port;
}
# 代理 projectB 的靜態資源
location ^~ /projectB_static/ {
proxy_pass http://localhost8085;
proxy_set_header Host $host:$server_port;
}
# 剩下的請求正常走 server.com 線上環境
location / {
proxy_pass http://server;
proxy_set_header Host server;
}
}
複製程式碼
利用 Nginx
靈活的規則,可以合併一些相似的代理規則,不用像配置 Charles
那樣得一個一個自己貼上了。另外還有個好處就是可以靈活管理自己的代理環境, 如果今後再有像這種代理比較麻煩的專案,可以分別拆成兩個 nginx.conf
檔案,使用時指定用哪個 conf 檔案即可。如果用 Charles
管理的話,所有代理都一個配置列表裡面,代理多了之後會非常難以維護。
不過本機用 Nginx
代理一般得配合改 hosts 檔案,在 hosts 檔案裡將 test.server.com
(自己瞎編的一個域,這裡滿足 *.server.com
就能通過後端的校驗) 指向 localhost即可。啟動 Nginx
指定監聽 80 埠,這樣訪問 test.server.com
的時候(實際上訪問的就是 localhost:80
) 就會被 Nginx
給代理了。配置好了之後,訪問 ad.server.com/
就是線上環境,改成 test.server.com/
就是本機環境了,無痛切換,非常舒服。
總結
以上幾種辦法都在一定場景下能實現代理的需求。後來發現社群有專門的輪子 xswitch、zan-proxy。不過作為前端開發,多熟悉下 Charels
和 Nginx
也是個不錯的選擇。