下面我以简单的两台node服务器来说明如何使用nginx进行前端跨域访问。
- node1服务器 在localhost:8083上启动
const app = express();
app.get('/web/users',(req, res)=>{
res.json([{name:"张三",age:12},{name:"李四",age:14}]);
res.end()
})
app.listen(process.env.PORT || 8083);
同域下的前端代码只需调用
function getUsers() {
var xhr=new XMLHttpRequest();
xhr.open('GET', 'http://localhost:8083/web/users');
xhr.send(null);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}else {
alert(xhr.statusText);
}
}
}
这里有一点需要关注的是,前后端代码处在同一个域下,xhr.open() url路径写成下面这样也是可以的,它会默认请求到http://localhost:8083/web/users
xhr.open('GET', '/web/users');
下面我们将node1服务器中的web/users接口删除:
const app = express();
app.listen(process.env.PORT || 8083);
前端此时自然无法访问后台的web/users了,将报一个404错误。
下面增加一个node2服务器,在localhost:8085上启动,同时我们将原先在8083上删除的web/users接口搬迁到8085上:
const app = express();
app.get('/web/users',(req, res)=>{
res.json([{name:"张三",age:12},{name:"李四",age:14}]);
res.end()
})
app.listen(process.env.PORT || 8085);
由于8085实现了这个接口,我们尝试在原先8083端口下的ajax调用它试试:
xhr.open('GET', 'http://localhost:8085/web/users');
如我们所料,浏览器阻止了此次行为,并抛出一个跨域错误。
难道就无法访问那个接口了吗?其实服务器之间和服务器之间是可以相互调用的,阻止跨域访问只是在浏览器端做的限制而已。
下面我通过两种方式来实现如何访问到8085上的web/users接口。
1 . node直接作为代理访问
原理就是交由8083后端去访问8085端口接口,访问完成交给前端
8083node后端实现代码:
npm install request --save // 需要安装一个http request模块
const app = express()
const request = require('request')
// 访问此接口时通过request模块去访问8085 再返回给前端。
app.get('/web/users',(req, res)=>{
var url='http://localhost:8085'+req.url
console.log(url) // http://localhost:8085/web/users
req.pipe(request(url)).pipe(res);
})
2. jsonp方式访问
就是通过script 的src,向服务器请求数据,且这不受同源策略限制;然后服务器将相应的数据放入指定的函数回调名中,返回给前端。说的有点绕,下面通过实例讲解:
8083前端请求8085,这里已经不再是ajax请求了,而是直接加载8085上资源。
上述代码第一个script标签定义了一个函数getUsers 但是并没有执行,只是定义了而已,要想有执行能力,需要
getUsers(data)
所以我们要让第二个标签script src="http://localhost:8085/jsonp?callback=getUsers" 返回getUsers(data)内容即可,这样第一个标签内定义的函数就可以执行了。返回接口内容只需要放到函数参数里即可
后台8085实现:
const app = express();
const querystring=require('querystring')
const url=require('url')
// 处理前端jsonp请求
app.get('/jsonp',(req,res)=>{
var qs = querystring.parse(req.url.split('?')[1]); //{callback:'getUsers'}
var users=JSON.stringify([{name:"张三",age:12},{name:"李四",age:14}]) // 注意需要传成字符串格式
var callback=`${qs.callback}(${users})`
res.end(callback)
})
前端返回
是不是有点黑魔法的味道!但是缺点也是明显的,首先通过此方式有一定安全性的,通过callback后加一些乱七八糟的东西可能会有xss攻击,最主要的是jsonp不支持post方法。
3 .nginx反向代理器
此方式即类似于第一种node代理方式,也是通过服务器和服务器之间通信,只是不需要8083下后台服务器自己去访问8085,而是专门交给nginx,为什么?姑且认为nginx更加专业吧!
nginx配置
server {
listen 8007; # nginx启动端口,需要访问这个端口才能够代理
server_name localhost:8083; # 需要代理的服务器
location / { # 如果你访问127.0.0.1/8002/的话nginx去请求
proxy_pass http://localhost:8083;
}
location /web { #访问/web/aa时会映射成http://localhost:8085/web/aa;
proxy_pass http://localhost:8085;
}
}
8083请求路径
xhr.open('GET', '/web/users');
3 CORS
CORS全称是跨域资源共享 是一个W3C标准,它规定浏览器允许发送ajax到不同域下的服务器来获取数据,从而克服了原来限制,使用CORS需要前后端都支持,现代浏览器基本上都支持。具体CORS的知识可以参考阮一峰博客 http://www.ruanyifeng.com/blog/2016/04/cors.html。node实现只需要增加代码
app.use('*',function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*'); //这个表示任意域名都可以访问,这样写不能携带cookie了。
//res.header('Access-Control-Allow-Origin', 'http://www.baidu.com'); //这样写,只有www.baidu.com 可以访问。
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');//设置方法
if (req.method == 'OPTIONS') {
res.send(200);
}
else {
next();
}
});
或者使用现成的CORS模块
npm install cors
var express = require('express')
var cors = require('cors')
var app = express()
var corsOptions = {
origin: 'http://www.baidu.com',
optionsSuccessStatus: 200
}
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
res.json({msg: '只有百度可以访问'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
如有不正确,请指正_