接触前端以来,都是在掘金社区学习大佬们的精华,一篇短短的文章可能是别人总结了很久的产出,作为一名阅读者,我们可以更快速更全面的学习到一些新的知识点,感谢掘金,感谢默默贡献自己力量的掘金人。从这篇文章开始,我也要在掘金社区写博客,记录自己的学习总结,以严谨的态度开启我的掘金之旅。
第一篇掘金博客,我想研究一下跨域这个问题。校招面试常被问到的问题,什么是跨域?如何解决跨域问题?这是两个高频被问的两个问题,虽然理论上能将这个问题回答过去,但是真正在项目中遇到跨域问题就犯难了。今天这篇文章适合跟我一样的小白阅读。在面试的过程中能将跨域的原理以及常用的几种解决方式说出来,对于这次面试一定是加分的。
学完这篇文章,我们可以回答出以下几个问题
-
什么是跨域?
-
跨域问题的产生以及意义
-
CORS跨域资源共享
-
解决跨域请求
-
如何使用proxyTable 解决开发环境的跨域
以前我们前后端不分离时代,我们前端只需要写最基础的html、css,然后将文件发给后台开发人员。我们都是用服务器来渲染的,这样前端与服务器的代码完全在一起,在同一个web服务器里面,同一个服务不存在跨域这个问题。
后来随着前端飞速发展,想要实现异步、无刷新操作,我们开始使用ajax实现异步无刷新操作,效果惊人,这时候部署还会将前端代码与服务器代码放在同一个web服务器上,这也不会造成跨域问题。这就属于同源策略,域名、协议、端口相同。想要同源,这就要求前端开发人员也得需要使用后端的编译器,比如eclipse,自己跑项目才能顺利开发前端代码。
什么是跨域?
跨域实际上也叫做非同源策略请求。 谈到跨域问题,我们必须先了解一下什么是同源策略?
同源策略:它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。
域名,协议,端口,三者全部相同就是同源,其中一个不同就是**跨域**
web服务器: http://127.0.0.1:3000/index.html
接口地址: http://127.0.0.1:4000/list
在这个项目中,域名相同,协议相同,端口号不一样,跨域
跨域解决方案:
1、JSONP实现跨域
原理:jsonp实现跨域的原理是跨域的服务端把客户端所需要的数据放进客户端本地的一个js方法里,进行调用,客户端在本地的js对返回的数据进行处理。这样就实现了不同域名下的两个站点间的交流。
基于script无跨域限制帮我我们带到服务器上,然后利用callback回调函数做一些我们想做的事,
callback=fun function fun(){//基于script无跨域限制帮我们带到服务器上,然后把本地的函数传递给服务器,服务器就可以接到请求,同时也能拿到callback传过来的func、服务器1、准备数据json格式的 data={...} 2、给客户端返回数据func(+JSON.stringify(data)+) ,3、客户端拿到数据
注:1、func得是全局函数。2、JSONP需要服务器端的支持服务器端
script、img、link、iframe这些标签可以跨域
例如下面的代码,既可以访问自己的,也可以访问跨域的。
客户端请求:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<title>Document</title>
</head>
<body>
</body>
<script>
//jquery支持jsonp请求
$.ajax({
//发送ajax
url: 'http://127.0.0.1:8001/list',//请求地址
method: 'get', //请求方式只能是get方式,这也是jsonp跨域的一个缺点
dataType: 'jsonp', //=>执行的是JSONP请求
success: res => {
console.log(res)
}
}) //客户端
console.log('aaa')
</script>
</html>
服务端用node来实现:
let express = require('express'),
app = express();
app.listen(8001, _ => {
console.log('OK!')
})
app.get('/list', (req, res) => {
let { callback = Function.prototype } = req.query //解构出来,设置默认值Function.prototype匿名空函数,
//callback与ajax的回调函数绑定
let data = {
code: 0,
meg: '这就是我要返回的数据'
} //返回给服务器的数据
res.send(`${callback}(${JSON.stringify(data)})`)//拿到的数据返回(注:数据服务器可以插入任何恶意代码)
})
http://127.0.0.1:8001/list?callback=jQuery214085466902496328_1593939725240&_=1593939725241
上面发了list请求,传了一个callback函数,这个函数是jquery帮我们创建出来的随机的的全局函数。
response返回的结果如下图:jquery创建的随机全局函数包着我们的数据,当我们浏览器拿到这个结果以后,就会把全局函数执行。全局函数会帮我们默认执行success执行,把数据传给res。
jquery在这个过程中帮我们完成了取值的过程:
- jquery帮我们默认创建一个全局函数
- 帮我们默认创建script向我们服务器发请求,
- 帮我们默认执行全局函数的时候,把success执行,拿到我们的数据。
2、CORS实现跨域
CROS(Cross-Origin Resource Sharing)跨源资源分享。
CROS允许浏览器向跨源服务器发出XMLHttpRequest请求,克服了ajax不能跨源请求的限制🚫。 CROS通信的主要在于服务器,只要服务器实现CROS接口,就可以实现跨域请求。
浏览器在头信息中增加一个Origin字段,从而发出CROS请求
要支持CORS访问需要服务器在响应头中添加Access-Control-Allow-Origin,可以使用*来表示允许所有域跨域访问。
详细的介绍可以参考阮一峰老师的这篇文章:www.ruanyifeng.com/blog/2016/0…
3、 vue项目开发使用proxyTable 解决开发环境的跨域
这是我想重点介绍的,因为公司前端页面是基于vue实现的,前端使用vue-cli脚手架搭建的vue项目做开发的时候,项目本身启动本地服务需要占用一个端口。我们在与后端服务器接口联调的时候,一定会产生跨域问题。大多数公司都是采用前后端分离的开发模式,必然会产生跨域。我们尽量在前端去解决跨域问题,避免更改后端代码。
现在很多项目使用webpack自动化构建前端项目,在webpack项目中我们使用ProxyTable来实现跨域。 ProxyTable实现跨域原理:
ProxyTable底层采用了http-proxy-middleware,这是http代理中间件。
我们在项目根目录的config文件夹下找到的index.js文件
(1)利用vue-cli脚手架搭建一个vue项目
下面的步骤是基于你已经安装了nodejs、npm,(可以安装一个全局的淘宝镜像cnpm速度会比较快)
npm install cnpm -g --registry=https://registry.npm.taobao.org
步骤:
-
cnpm install -g vue-cli //全局安装vue-cli
-
cd进入你想放置项目的文件夹
-
在当前文件夹下执行 vue init webpack
-
cnpm i //安装依赖包
-
npm run dev // 执行项目
-
至此我们已经利用vue-cli搭建好了vue项目
(2)配置跨域
打开刚刚搭建好的vue项目的config文件夹下的index.js文件,在proxyTable对象中填写需要被代理的api,
proxyTable: {
//配置特定的请求代理到对应的API接口
//请求接口为/HomeApi被代理到 http://10.64.61.129:3000/home
"/HomeApi": {
target: "http://10.64.61.129:3000/home", //目标地址
changeOrigin: true, //是否开启代理
pathRewrite: {
'^/HomeApi': ''
}
},
//请求的借口 /details被代理到 http://10.64.61.129:3000/details
"/DetailsApi": {
target: "http://10.64.61.129:3000/details", // 目标地址
changeOrigin: true,
pathRewrite: {
'^/HomeApi': ''
}
},
},
注:接口原本为/payment/list,为了匹配代理地址,我们在前面加上/HomeApi,就可以被分发到/HomeApi中,因此我们需要将接口地址写成/HomeApi/payment/list。被分配到指定的代理中,我们通过写
pathRewrite:{'^/HomeApi': ''}
重写路径,去掉我们之前为了匹配代理而加上的/HomeApi。