事情的起因是這樣的珊泳,在一個(gè)已上線的項(xiàng)目中富纸,其中一個(gè)包含登錄和獲取菜單的接口因響應(yīng)時(shí)間較長(zhǎng),后端讓我嘗試未經(jīng)服務(wù)轉(zhuǎn)發(fā)的另一域名下的新接口旨椒,舊接口允許跨域請(qǐng)求晓褪,但新接口不允許本地訪問(wèn)(只允許發(fā)布測(cè)試/生產(chǎn)的域名訪問(wèn))。
問(wèn)題
那么問(wèn)題來(lái)了综慎,本地環(huán)境該如何成功訪問(wèn)到新的接口并驗(yàn)證業(yè)務(wù)功能是否生效呢涣仿?
嘗試過(guò)程
我首先就想到了直接在 webpack 項(xiàng)目中配置 devServer,并且修改接口地址(為了安全隱私示惊,隱去公司實(shí)際域名好港,使用 xxxxx 來(lái)替代。)
devServer: {
proxy: {
'/': {
target: 'https://xxxxx.cn',
pathRewrite: {
'/proxyApi': '',
},
changeOrigin: true,
},
},
}
但返回的接口提示【登錄態(tài)無(wú)效】米罚,這下起碼不跨域了钧汹!本來(lái)以為已經(jīng)代理成功,只需要找到后端看看報(bào)錯(cuò)即可
但后端反饋這個(gè)報(bào)錯(cuò)是因?yàn)檎?qǐng)求頭沒(méi)有攜帶指定參數(shù)录择,他也查不到該請(qǐng)求的詳細(xì)信息拔莱。這時(shí)候我又開(kāi)始有疑問(wèn)了,明明查看請(qǐng)求頭是有的呀隘竭。
疑問(wèn)
在 chrome 瀏覽器上看到的請(qǐng)求地址并不是后端提供的真實(shí)接口請(qǐng)求地址塘秦,而是加了我代理的字段。在響應(yīng)體上我也沒(méi)有找到 location 等字段反饋到真實(shí)的請(qǐng)求接口动看。
此時(shí)的我懷疑尊剔,代理是真的生效了嗎,我請(qǐng)求的接口是真實(shí)的后端接口嗎菱皆?開(kāi)始驗(yàn)證 devServer 的 proxy 是否執(zhí)行须误。在 proxy 處配置請(qǐng)求前后輸出的函數(shù),結(jié)果發(fā)現(xiàn) onProxyReq 和 onProxyRes 都沒(méi)有執(zhí)行仇轻。
proxy: {
'/proxyApi': {
target: 'https://xxxxx.cn',
pathRewrite: {
'/proxyApi': '',
},
changeOrigin: true,
onProxyReq(proxyReq, req, res) {
console.log('>>>請(qǐng)求', req);
},
onProxyRes(proxyRes, req, res) {
// 響應(yīng)的鉤子函數(shù)
console.log('>>>響應(yīng)', res);
},
},
},
所以此時(shí)猜測(cè)是不是整個(gè) devServer 都沒(méi)有生效京痢,但如何證明它沒(méi)有生效呢?
證實(shí)
目前代理后端域名不受我們控制拯田,我無(wú)從知曉它是否發(fā)送到后端服務(wù)器上历造,所以我打算自己用 nodejs 開(kāi)啟一個(gè)服務(wù),開(kāi)啟服務(wù)的方式很簡(jiǎn)單,使用核心模塊 https 幾行代碼搞定吭产。
const http = require("http");
const server = http.createServer((req, res) => {
console.log(">>req", req.url, req.rawHeaders );
res.end("hello");
});
server.listen("3002", () => {
console.log("3002端口啟動(dòng)了");
});
通過(guò) node 啟動(dòng)服務(wù)后侣监,首先驗(yàn)證是否可攔截請(qǐng)求,直接通過(guò)瀏覽器窗口 輸入
哎~ 服務(wù)啟動(dòng)了臣淤,頁(yè)面也得到的響應(yīng)橄霉,服務(wù)器能獲取到剛剛 get 請(qǐng)求的數(shù)據(jù)
此時(shí)將項(xiàng)目中 proxy 的配置改為 3002 端口的服務(wù),再次執(zhí)行業(yè)務(wù)邏輯代碼發(fā)送請(qǐng)求邑蒋,發(fā)現(xiàn)此時(shí)3002端口啟動(dòng)的服務(wù)控制臺(tái)空空如也姓蜂!也就是說(shuō),它根本沒(méi)有攔截到該請(qǐng)求医吊。
proxy: {
'/proxyApi': {
target: 'http://localhost:3002',
},
猜想是否因?yàn)轫?xiàng)目里的接口請(qǐng)求工具導(dǎo)致無(wú)法攔截钱慢,于是直接在頁(yè)面上使用 fetch 發(fā)送請(qǐng)求,此時(shí)發(fā)現(xiàn) 3002 端口的服務(wù)仍然沒(méi)有接收到請(qǐng)求卿堂。
fetch('https://xxxxx.cn/proxyApi/yyyyy/operateTargetNew')
本來(lái)以為是不是 proxy 字符匹配的問(wèn)題束莫,因?yàn)?/proxyApi 標(biāo)識(shí)出現(xiàn)在整個(gè)url 中間,試圖修改為正則表達(dá)式 "*/proxyApi/"草描,也是無(wú)效的
proxy: {
'**/proxyApi/*': {
},
再次嘗試
這時(shí)候我意識(shí)到一個(gè)問(wèn)題览绿,帶有域名的接口訪問(wèn)好像不行,那我直接去掉域名呢穗慕?
此時(shí)直接使用 fetch 請(qǐng)求不包含域名的接口地址
fetch('/proxyApi/yyyyy/operateTargetNew')
這個(gè)時(shí)候饿敲,終于看到了問(wèn)題即將解決的曙光!調(diào)用接口成功獲取到了 3002 端口返回的響應(yīng)
撥開(kāi)云霧
查詢資料發(fā)現(xiàn)果然是接口地址的原因怀各。Webpack DevServer的proxy配置主要用于開(kāi)發(fā)環(huán)境中,針對(duì)的是由本地DevServer發(fā)出的API請(qǐng)求暑脆。
當(dāng)你在前端代碼中發(fā)送請(qǐng)求時(shí)渠啤,通常會(huì)使用相對(duì)路徑(如/api/xxx/yyy),這樣它們就會(huì)被發(fā)送到當(dāng)前頁(yè)面所在的主機(jī)和端口添吗,也就是Webpack DevServer。
這時(shí)份名,DevServer的proxy設(shè)置可以將請(qǐng)求轉(zhuǎn)發(fā)到配置的后端服務(wù)器碟联。
// webpack.config.js
module.exports = {
// ...
devServer: {
proxy: {
'/api': {
target: 'http://your-backend-server.com',
changeOrigin: true,
},
},
},
};
現(xiàn)在,如果你發(fā)送一個(gè)請(qǐng)求到/api/xxx/yyy僵腺,DevServer會(huì)將它代理到http://your-backend-server.com/api/xxx/yyy鲤孵。
然而,如果你在前端代碼中直接使用了完整的URL(即包含域名https://www.xxxx.com/api/xxx/yyy)辰如,就繞過(guò)了Webpack DevServer普监,請(qǐng)求直接發(fā)往該完整URL對(duì)應(yīng)的服務(wù)器。DevServer的proxy配置不會(huì)和這個(gè)請(qǐng)求交互,因此無(wú)法將它代理到你配置的目標(biāo)服務(wù)器凯正。
請(qǐng)求改造
于是再改回需要代理的接口毙玻,并對(duì)項(xiàng)目邏輯進(jìn)行一些改造,因?yàn)槟J(rèn)的網(wǎng)絡(luò)庫(kù)會(huì)拼接url廊散,這里做一個(gè)判斷桑滩,將需要代理的域名和代理的字符作為一組值保存起來(lái)。
如果匹配中需要代理的需求允睹,并用前綴來(lái)替換运准。
// 本地環(huán)境,需要將代理的接口剔除域名缭受,并拼接代理前綴
if (process.env.NODE_ENV === 'development') {
const proxyObj = {
'https://xxxx.cn': '/proxyApi',
};
const proxyKeys = Object.keys(proxyObj);
for (let i = 0; i < proxyKeys.length; i++) {
const host = proxyKeys[i];
if (option.url.includes(host)) {
const prefix = proxyObj[host];
option.url = option.url.replace(host, prefix);
}
}
}
這樣就可以將接口請(qǐng)求拼接為 https://xxxx.con 域名的全部替換為指定前綴胁澳,這樣這部分的請(qǐng)求就都會(huì)走代理。
很慚愧米者,雖然早就知道 webpack 的 proxy 配置解決本地跨域問(wèn)題韭畸,但確實(shí)很少自己去配置,一般是后端解決掉跨域問(wèn)題或者項(xiàng)目里的自帶里處理方案塘雳,所以真正到自己配置的時(shí)候多少有點(diǎn)迷糊了陆盘。