線上前端靜態資源代理到本地的幾種方式
前言
我是一個小前端,前段時間開始支援集團監控平臺業務。
當我拉下程式碼,開啟本地服務,出現了一大堆請求錯誤。
原因是:本地的host是127.0.0.1,而介面呼叫的是線上資料
。
介面的host和平臺url的host都是x.alibaba-inc.com。
怎麼辦?我要跨域。。
直接修改hosts是不行的,使用http的withCredentials為真,導致允許跨域的源必須是本地,會導致監控平臺對外提供的接口出現問題。
。。。
然後我請教了@繪萌師兄,師兄提出可以把線上資源代理到本地。
OK,怎麼把線上資源代理到本地呢?
經過一番折騰,我總結出如下四種方式。
方式
1、抓包類工具
常用的抓包工具有Wireshark、Fiddler、Charles。
這裡我拿Charles舉例子,把線上資源代理到本地叫本地檔案對映。
選單路徑:Tools -> Map Local
。
與本地檔案對映對應的叫遠端對映檔案對映,遠端對映檔案對映將路徑對映到其他伺服器上,而本地對映則對映到本地磁碟上。
Map From 設定好線上檔案 A 的相關資訊,Map To 裡選擇本地資源,請求檔案 A 時,就會返回指定的本地檔案。
圖中我把日常前端所有資源都代理到本地了。
2、瀏覽器外掛
Chrome的相關外掛:Proxy SwitchySharp、ReRes
Firefox的相關外掛:FoxyProxy、Autoproxy
試一把:
本地修改程式碼,發現線上完全同步了,完美!
3、Nginx
原理:使用nginx反向代理,起localhost的http服務,本地代理層攔截靜態資源請求,並返回本地的靜態資源;放過資料請求,將資料請求代理成線上真實的地址。
首先,要理清楚前端的靜態資源請求和資料請求。然後對nginx.conf進行配置。到這一步,我們已經能接觸到線上,但是線上訪問有跨域限制,所以需要繫結host,使得到線上的請求的不受跨域限制。
配置檔案內容如下:
user root;
worker_processes 4;
pid logs/nginx.pid;
events {
worker_connections 1024 ;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name x.alibaba-inc.com;
# 攔截並返回本地的靜態資源
location ~ ^/modules.*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$
{# 攔截並返回本地的靜態資源
root /Users/feiyu/Documents/xflush/xflush-burning/app;
}
location ~ ^/bower_components.*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$
{
root /Users/feiyu/Documents/xflush/xflush-burning;
}
location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$
{
root /Users/feiyu/Documents/xflush/xflush-burning/app;
}
# 放過的資料請求,該資源為伺服器端提供
location = /env.js {
proxy_pass http://100.67.99.11;
}
# 放過的資料請求,該資源為伺服器端提供
location = /tenants.js {
proxy_pass http://100.67.99.11;
}
# 放過的資料請求,此處為ajax請求的資料
location / {
proxy_pass http://100.67.99.11;
}
}
}
4、Nodejs
完成該項功能的是 http-proxy 包。下載、安裝請鍵入:
npm install http-proxy
在使用這個之前要做的是對請求型別的一些判斷,即請求Js/css/jpg等這些檔案時,返回的response資訊型別是什麼,於是有了下面的程式碼:
var getContentType = function(ext){
var contentType = '';
switch(ext){
case ".html":
contentType= "text/html";
break;
case ".js":
contentType="text/javascript";
break;
case ".css":
contentType="text/css";
break;
case ".gif":
contentType="image/gif";
break;
case ".jpg":
contentType="image/jpeg";
break;
case ".png":
contentType="image/png";
break;
case ".ico":
contentType="image/icon";
break;
default:
contentType="application/octet-stream";
}
return contentType;
};
接著是代理程式碼:
/*
http-proxy
*/
var http = require('http')
,httpProxy = require('http-proxy') //http-proxy
,proxy = httpProxy.createProxyServer({})
,fs = require('fs')
,path = require('path')
,getConType = require('./content-type')//自己定義 根據字尾轉換Content-Type
;
var server = http.createServer(function(req, res) {
var _url = req.url //獲取請求的url
,_file
,_localPath
,_localFile
,_ext
,_stream
;
//eg://g-assets.daily.taobao.net/ais-fed/sunfire/1.0.0/vendor.js
if(_url.indexOf('//g-assets.daily.taobao.net/ais-fed/sunfire/1.0.0/')>-1){
_ext = path.extname(_file); // 檔案擴充套件
//vendor.js
_file = _url.replace('//g-assets.daily.taobao.net/ais-fed/sunfire/1.0.0/','');
//轉換成本地路徑
_localPath = '/Users/feiyu/Documents/xflush/sunfire/build/';
_localFile = _localPath+_file;
//判斷檔案是否存在
if(fs.existsSync(_localFile)){//如果檔案存在
res.writeHead(200, {"Content-Type": getConType(_ext) });
_stream = fs.createReadStream(_localFile, {flags : "r", encoding : null});//只讀模式 讀取檔案內容
_stream.on('error',function(){//如果讀取錯誤 返回404
res.writeHead(404, {"Content-Type": "text/html"});
res.end("<h1>404 Read Error</h1>");
});
_stream.pipe(res);//連線檔案流和http返回流的管道,用於返回實際Web內容
_stream.on('end',function(){
_stream = null;
})
}else{//返回404錯誤
res.writeHead(404, {"Content-Type": "text/html"});
res.end("<h1>404 Not Found</h1>");
}
}else{
proxy.web(req, res, { target: 'x.alibaba-inc.com' });
}
});
console.log("listening on port 80")
server.listen(80);
總結來說,實現反向代理的靜態資源分離,首先是判斷request來判斷是否請求靜態檔案,如果是請求靜態檔案,就找到專案下的具體檔案,直接用fs讀取,如果不是請求靜態檔案那麼就用proxy.web來請求實際伺服器。最後別忘了繫結host。
另外給一個anyproxy的傳送門,它是用nodejs寫的,提供web版介面,可以觀測請求情況,還具有開放介面,允許使用者進行充分的自定義。
總結
其實都是為了解決問題,只是方式不同,大家愛用哪個用哪個哈~
最後,我想感謝@谷童@季真@笑斌三位同學!
谷童做為最初的引導老師;
季真教我如何配置nginx;
笑斌幫我review nodejs程式碼。