1. 程式人生 > >線上前端靜態資源代理到本地的幾種方式

線上前端靜態資源代理到本地的幾種方式

前言

我是一個小前端,前段時間開始支援集團監控平臺業務。
當我拉下程式碼,開啟本地服務,出現了一大堆請求錯誤。
原因是:本地的host是127.0.0.1,而介面呼叫的是線上資料
介面的host和平臺url的host都是x.alibaba-inc.com。
怎麼辦?我要跨域。。
直接修改hosts是不行的,使用http的withCredentials為真,導致允許跨域的源必須是本地,會導致監控平臺對外提供的接口出現問題。
。。。
然後我請教了@繪萌師兄,師兄提出可以把線上資源代理到本地。
OK,怎麼把線上資源代理到本地呢?
經過一番折騰,我總結出如下四種方式。

方式

1、抓包類工具

常用的抓包工具有Wireshark、Fiddler、Charles。
這裡我拿Charles舉例子,把線上資源代理到本地叫本地檔案對映。
選單路徑:Tools -> Map Local
與本地檔案對映對應的叫遠端對映檔案對映,遠端對映檔案對映將路徑對映到其他伺服器上,而本地對映則對映到本地磁碟上。

screenshot.png

Map From 設定好線上檔案 A 的相關資訊,Map To 裡選擇本地資源,請求檔案 A 時,就會返回指定的本地檔案。
圖中我把日常前端所有資源都代理到本地了。

2、瀏覽器外掛

Chrome的相關外掛:Proxy SwitchySharp、ReRes
Firefox的相關外掛:FoxyProxy、Autoproxy

試一把:
screenshot.png

本地修改程式碼,發現線上完全同步了,完美!

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程式碼。