关于跨域
在使用Vue搭建的一个后端管理系统中,我使用axios请求本地的Node环境下的接口,但是请求失败,然后我错误信息是:
大概意思就是不能访问http://localhost:8080
我的Vue项目端口是http://localhost:8081
,Node服务端运行在http://localhost:8080
端口上,也就是说因为请求端口和响应端口不一致,所以请求失败。 我也在网上查看了一些关于跨域出现的原因及解决的方法,并记录下来。
#为什么会有跨域
跨域一句话的理解就是:服务端和请求端的地址不一样。
#什么是跨域
Ajax 的便利性大家都清楚,可以在不向服务器提交完整的页面的情况下,实现局部更新页面。但是浏览器处于对安全方面的考虑,不允许跨域调用其他页面的对象。
其实这个也不能怪浏览器,假设谁都可以随随便便向你发送请求,那样有很大的安全隐患。
根据浏览器的同源策略, 只有当协议,域名,端口相同的时候才算是同源, 反之则均视为是一个跨域的请求.
也就是说我刚刚的Vue端口是8081
,服务端端口是8080
,端口不一样,因为同源策略的存在 ,所有我的请求会失败。
一个问题,当找到了原因,这个问题就解决了一半了。
#怎么解决跨域
下面就先介绍三种跨全域的方法:
#JSONP
应该是最常见解决跨域的方法了, 他为什么能解决跨域呢,是因为Web 页面上调用 js 文件不受浏览器同源策略的影响,所以通过 Script 便签可以进行跨域的请求:
- 首先前端先设置好回调函数,并将其作为 url 的参数。
- 服务端接收到请求后,通过该参数获得回调函数名,并将数据放在参数中将其返回
- 收到结果后因为是 script 标签,所以浏览器会当做是3脚本进行运行,从而达到跨域获取数据的目的。 我的前端是
index.html
,后端是server.js
后端逻辑:
//server.js
const url = require('url');
const http = require('http');
http.createServer((req, res)=>{
const data = {
x: 10//返回的数据
};
const callback = url.parse(req.url, true).query.callback;
res.writeHead(200);
res.end(`${callback}(${JSON.stringify(data)})`);
//执行回调函数,返回data
}).listen(3000, 'localhost');
console.log('启动服务,监听 localhost:3000');
然后使用node server.js
运行 前端:
//index.html
<body>
<script>
function jsonpCallback(data) {
console.log('获得 X 数据:' + data.x);
}
</script>
<script src="http://localhost:3000?callback=jsonpCallback"></script>
</body>
之后打开index.html;就可以在控制台看到返回的数据了:
至此,通过 JSONP 跨域获取数据已经成功了,jsonp这种方法跨域,他的兼容性很好,可以在古老的浏览器中国使用,因为这种方法是利用了<script>
标签的特殊性,所有只支持GET请求。
#CORS
CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 ajax 只能同源使用的限制。
CORS 需要浏览器和服务器同时支持才可以生效,对于开发者来说,CORS 通信与同源的 ajax 通信没有差别,代码完全一样。浏览器一旦发现 ajax 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。
前端:
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script>
$.ajax({
url:"http://127.0.0.1:3000",
success:function(res){
var res = JSON.parse(res);
$('body').text(res.data);
console.log(res.data);
}
});
</script>
这次前端启动需要使用node-server
来启动,使用npm install node-server
下载,然后当前目录下使用node-server
就可以了 后端:
const http = require('http');
http.createServer((req, res)=>{
const data = {
'data': 'Hello world'//返回的数据
};
res.writeHead(200, {'Access-Control-Allow-Origin': 'http://127.0.0.1:8080'});
//设置的头部信息需要和前端请求的地址一致
res.end(JSON.stringify(data));
//返回data
}).listen(3000, '127.0.0.1');
console.log('启动服务,监听 127.0.0.1:3000');
使用命令node server.js
启动;
CORS与JSONP的使用目的相同,但是比JSONP更强大。
JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
#Server Proxy
服务器代理,顾名思义,当你需要有跨域的请求操作时发送请求给后端,让后端帮你代为请求,然后最后将获取的结果发送给你。
假设有这样的一个场景,你的页面需要获取 CNode:Node.js专业中文社区 论坛上一些数据,如通过 https://cnodejs.org/api/v1/topics,当时因为不同域,所以你可以将请求后端,让其对该请求代为转发。
后端代码如下:
const url = require('url');
const http = require('http');
const https = require('https');
http.createServer((req, res)=>{
const path = url.parse(req.url).path.slice(1);
//核对请求路由是否一致
if(path === 'topics'){
https.get('https://cnodejs.org/api/v1/topics', (resp)=>{
//https代发请求
let data='';
resp.on('data', chunk=>{
data+= chunk
});
resp.on('end', ()=>{
res.writeHead(
200,
{'Content-Type': 'application/json; charset=utf-8'}
);
res.end(data);
//返回数据
})
})
}
}).listen(3000, '127.0.0.1');
console.log('启动服务,监听 127.0.0.1:3000');
前端代码:
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script>
$.ajax({
url:"https://cnodejs.org/api/v1/topics",
success:function(res){
$('body').text(JSON.stringify(res));
console.log(res);
}
});
</script>
这样就成功了
#总结
常用的跨域方式基本就是这三种:
- JSONP 优点是可以兼容老浏览器,缺点是只能发送GET请求
- CORS 优点简单方便,支持post请求,缺点是需要后端的配合,不支持老版浏览器。。
- Server Proxy 优点是前端正常发送ajax请求,缺点是后端会二次请求。
其他的跨域方式还有:location.hash
、window.name
、postMessage
等方式,有时间也可以了解一下。
参考资料:
- 跨域资源共享 CORS 详解[阮一峰的博客]:http://www.ruanyifeng.com/blog/2016/04/cors.html
- 关于跨域,你想知道的全在这里:https://zhuanlan.zhihu.com/p/25778815
- 不要再问我跨域的问题了[sf]:https://segmentfault.com/a/1190000015597029
- 关于跨域,以及跨域的几种方式[cnblog]:https://www.cnblogs.com/chenshishuo/p/4919224.html
- 浏览器的同源策略[MDN]:https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
- Elasticsearch检索分类深入详解—基础篇
- 通过Java程序测试数据库连接信息 (r10笔记第64天)
- GoldenGate安装简记(r10笔记第78天)
- 【Go 语言社区】各种变量的声明
- 【Go 语言社区】Golang 高效字符串拼接
- 实战 | Elasticsearch实现类Google高级检索
- Golang中time包用法--转
- 干货 | Elasticsearch 集群健康值红色终极解决方案
- Go语言interface的value.(type)使用小技巧-转
- 干货 | Elasticsearch5.X Mapping万能模板
- MySQL 5.7安装部署总结(r10笔记第77天)
- Go语言中Socket通信TCP服务端
- MySQL和Oracle的添加字段的处理差别 (r10笔记第73天)
- MySQL修改数据类型的问题总结(r10笔记第74天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Spring-IOC-从单例缓存中获取单例 Bean
- Linux上为你的任务创建一个自定义的系统托盘指示器
- 14个Seaborn数据可视化图
- Linux中的进程守护supervisor安装配置及使用
- 《JavaScript数据结构与算法》读书笔记
- 《JavaScript函数式编程指南》读书笔记
- 冒泡排序
- 详解linux下避免僵尸进程的几种方法
- Ubuntu下Subversion(SVN)的快速安装与配置详解
- Linux查看某个端口的连接数的方法
- 浅析Linux中使用nohup及screen运行后台任务的示例和区别
- 快速搭建简易、高效、多线程http服务器
- Linux解压文件到指定目录的方法
- Linux系统中CPU占用率较高问题排查思路与解决方法
- linux中ftp服务搭建需要注意的地方