静态网页使用Node.js跨域代理服务

Node.js

1. 需求背景

公司网站的本地开发版之前一直都是部署在本地电脑上Tomcat容器里的,好处就是本地搭建服务器环境接口无需做跨域请求处理,坏处就是后台代码的每次更新都需要拷贝一份至我的电脑覆盖,并且本地环境与测试线环境数据仍然有所差异,在本地环境调试不便。

前天在与新入职的Java工程师讨论如何分工协作的时候聊到了部署Tomcat容器到我本地的坏处,然后仔细想想我最近不是在学nodejs嘛,为何不学以致用在我本地用nodejs部署跨域代理服务呢?于是花了两天零碎的时间研究了如何使用nodejs来实现静态页面的接口请求代理。
在实现跨域之前先理清楚我想实现的什么样的功能。

2. 跨域的几种方式

静态网站的里的ajax请求是相对路径,同一套代码要在本地开发环境、测试线环境、大陆正式线、香港正式线部署。所以请求不适合使用绝对路径。

前端跨域

前端跨域的方式有两种

  1. 通过script标签跨域
  2. 通过jsonp跨域

两者的原理都是使用请求静态资源文件的方式,用回调函数的包装把json数据返回前端。从而骗过浏览器的跨域审查。使用这种方法来跨域意味着前端和后端的代码都要根据跨域的要求来重构一番,并且只能发送GET请求,这样的方式肯定是不可行的,因为只有在本地开发时才需要跨域,代码部署到线上服务器后就不需要跨域了。
所以开始研究后端方案跨域。也有两种。

后端跨域

  1. CORS跨域资源共享
  2. 跨域代理服务

CORS (Cross-Origin Resource Sharing)跨域资源共享,它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。CORS需要浏览器和服务器同时支持,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

使用CORS来实现跨域仍然需要在目标服务器的接口上配置跨域请求参数,所以实现起来要前后端配合还是比较麻烦的。而且仅仅是测试线服务器需要跨域,正式线服务器禁止跨域,这样子修改后,后台代码就会出现不一致。所以此方法也弃用。

跨域代理服务,原理是把前端http请求发送给后台的代理,让代理代为转发请求,从而不需要在浏览器上进行跨域请求。代理获取到响应结果再转发到前端。使用它的好处是目标服务器上的接口代码和前端代码都不需要做任何更改,只需开启本地跨域代理服务即可。

每种跨域方式分析过后,觉得最可行的操作就是使用静态资源代理服务来做前端跨域请求。

3. 使用nodejs跨域代理服务

在nginx和nodejs间选择了后者,于是开始了在本地搭建http服务器和寻找合适的代理跨域中间件。

搭建了本地http服务后,具体怎么操作实现代理跨域其实还是没多大思路的,于是呢就开始百度寻找案例来参考,了解了使用nodejs中间件跨域的大概思路。

先是搭建起http服务器,对访问主机地址的网络请求进行判断,如果是api接口的请求时则使用中间件的代理服务进行转发。让中间件代为向目标服务器发起请求,响应结果再转发至前端。

4. 代码实现

文件结构目录大概如此,ROOT文件夹为网页文件夹


项目文件目录

1.创建项目文件夹并npm初始化文件夹

mkdir demo
cd demo
npm init -y

2.安装node-http-proxy中间件
npm install http-proxy --save-dev

3.创建启动文件proxy.js

var PORT = 3000;//定义端口号
var tatgetPATH='http://www.a.com/'//目标服务器地址
var http = require('http'); //引入http模块
var url=require('url');  //引入url模块
var fs=require('fs');  //引入文件模块
var mine=require('./fileFormat').types;  //文件格式字典
var path=require('path'); //引入path模块
var httpProxy = require('http-proxy');  //跨域代理中间件

var proxy = httpProxy.createProxyServer({
    target: tatgetPATH,   //接口地址
    // 下面的设置用于https
    // ssl: {
    //     key: fs.readFileSync('server_decrypt.key', 'utf8'),
    //     cert: fs.readFileSync('server.crt', 'utf8')
    // },
    // secure: false
});

var server = http.createServer(function (request, response) {
    var pathname = url.parse(request.url).pathname;

    //访问根目录时改为指向首页文件
    if(pathname=='/'){
        pathname='index.html' 
    }
     // 指定根目录
    var realPath = path.join("./ROOT", pathname);

    var ext = path.extname(realPath);
    ext = ext ? ext.slice(1) : 'unknown';

    //判断如果是api接口访问,则通过proxy转发
    if(pathname.indexOf("./ROOT") > 0){
        // console.log('发起请求:',pathname)
        proxy.web(request, response);
        return;
    }

    fs.exists(realPath, function (exists) {
        if (!exists) {
            response.writeHead(404, {
                'Content-Type': 'text/plain'
            });

            response.write("This request URL " + pathname + " was not found on this server.");
            response.end();
        } else {
            fs.readFile(realPath, "binary", function (err, file) {
                if (err) {
                    response.writeHead(500, {
                        'Content-Type': 'text/plain'
                    });
                    response.end(err);
                } else {
                    var contentType = mine[ext] || "text/plain";
                    response.writeHead(200, {
                        'Content-Type': contentType
                    });
                    response.write(file, "binary");
                    response.end();
                }
            });
        }
    });
});
server.listen(PORT);

//代理服务执行错误的监听
proxy.on('error', function(err, req, res){
    res.writeHead(500, {
        'content-type': 'text/plain'
    });
    console.log(err);
    res.end('Something went wrong. And we are reporting a custom error message.');
});
console.log("Server runing at port: " + PORT + "."+tatgetPATH);

引入文件格式字典 fileFormat.js

exports.types = {
    "css": "text/css",
    "gif": "image/gif",
    "html": "text/html",
    "ico": "image/x-icon",
    "jpeg": "image/jpeg",
    "jpg": "image/jpeg",
    "js": "text/javascript",
    "json": "application/json",
    "pdf": "application/pdf",
    "png": "image/png",
    "svg": "image/svg+xml",
    "swf": "application/x-shockwave-flash",
    "tiff": "image/tiff",
    "txt": "text/plain",
    "wav": "audio/x-wav",
    "wma": "audio/x-ms-wma",
    "wmv": "video/x-ms-wmv",
    "xml": "text/xml",
    "woff": "application/x-woff",
    "woff2": "application/x-woff2",
    "tff": "application/x-font-truetype",
    "otf": "application/x-font-opentype",
    "eot": "application/vnd.ms-fontobject"
  };

代码写完后运行proxy.js即可通过跨域代理服务来实现本地调用不同域服务器的接口了。

5. 总结

跨域的解决方案有很多,具体选择哪一种方案还是根据实际的情况来进行分析。

以前还没学习nodejs的时候,遇到跨域问题能想到的解决方法就是使用ajax的jsonp,需要后端返回的接口加上回调函数,前端再通过通过函数对数据进行接收。改起来十分之麻烦。

前端若是懂得后端的一些知识,便能够使用更多的解决方案来解决问题。web开发中包括了前端和后端,能够熟练使用两端的技能,才能够在web开发中游刃有余。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容

  • 题目1.什么是同源策略? 同源策略(Same origin Policy): 浏览器出于安全方面的考虑,只允许与本...
    FLYSASA阅读 1,717评论 0 6
  • 什么是跨域 跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实...
    他方l阅读 1,062评论 0 2
  • 什么是跨域 跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实...
    Yaoxue9阅读 1,295评论 0 6
  • 原文地址:原文地址 什么是跨域? 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。 广义...
    C_Y大渔阅读 1,258评论 1 13
  • 什么是跨域 跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实...
    HeroXin阅读 835评论 0 4