我是一个小前端,前段时间开始支持集团监控平台业务。
当我拉下代码,开启本地服务,出现了一大堆请求错误。
原因是:本地的host是127.0.0.1,而接口调用的是线上数据
。
接口的host和平台url的host都是x.alibaba-inc.com。
怎么办?我要跨域。。
直接修改hosts是不行的,使用http的withCredentials为真,导致允许跨域的源必须是本地,会导致监控平台对外提供的接口出现问题。
。。。
然后我请教了@绘萌师兄,师兄提出可以把线上资源代理到本地。
OK,怎么把线上资源代理到本地呢?
经过一番折腾,我总结出如下四种方式。
常用的抓包工具有Wireshark、Fiddler、Charles。
这里我拿Charles举例子,把线上资源代理到本地叫本地文件映射。
菜单路径:Tools -> Map Local
。
与本地文件映射对应的叫远程映射文件映射,远程映射文件映射将路径映射到其他服务器上,而本地映射则映射到本地磁盘上。
Map From 设置好线上文件 A 的相关信息,Map To 里选择本地资源,请求文件 A 时,就会返回指定的本地文件。
图中我把日常前端所有资源都代理到本地了。
Chrome的相关插件:Proxy SwitchySharp、ReRes
Firefox的相关插件:FoxyProxy、Autoproxy
本地修改代码,发现线上完全同步了,完美!
传送门:
Proxy SwitchySharp
ReRes
FoxyProxy
Autoproxy
原理:使用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;
}
}
}
完成该项功能的是 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("404 Read Error
");
});
_stream.pipe(res);//连接文件流和http返回流的管道,用于返回实际Web内容
_stream.on('end',function(){
_stream = null;
})
}else{//返回404错误
res.writeHead(404, {"Content-Type": "text/html"});
res.end("404 Not Found
");
}
}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版界面,可以观测请求情况,还具有开放接口,允许用户进行充分的自定义。
anyproxy
其实都是为了解决问题,只是方式不同,大家爱用哪个用哪个哈~
最后,我想感谢@谷童@季真@笑斌三位同学!
谷童做为最初的引导老师;
季真教我如何配置nginx;
笑斌帮我review nodejs代码。