好久没有更新博客了,前段时间在疯狂面试,最近工作了才有时间来写博客。
准备来讲讲面试里常问到的跨域处理吧。
说到跨域,我们可能会下意思的说出jsonp,服务端配置cors,node配置代理等,再多了,我可能想不起来了。本篇本来打算只记录postMessage这种方式的,但光说这个显得太单薄了,所以一并都总结一下吧。
跨域是什么,为什么会跨域?
要想知道为什么,你得先知道什么是同源策略。同源策略是浏览器的一个安全协议,它限制了只有在同源的情况下才可以安全访问请求到数据,同源即同一个协议、域名、端口即是同源,比如:
http://localhost:8000
这里http就是协议
localhost是域名
而8000就是端口
协议域名端口哪怕有一个不同,都会造成跨域导致无法正常去请求(否则都乱套了,大家都能随意访问对方的数据,都在裸奔,也就没有隐私而言了)。但我们在日常开发的时候,早已经前后端分离了,所以在联调接口的时候,就会有各种跨域问题。
JSONP
在那个用jq刀耕火种的那个年代,jq就基于xmlHttprequest封装了ajax请求库,在请求配置项里可以直接配置jsonp:true
来做跨域请求。原理是利用script标签加载不受跨域的影响,将请求放到script标签的src属性上。同时声明一个回调函数用于接收数据,在请求后面加上这个回调函数,服务端接收到请求后把参数放到回调函数里,本次http连接结束后会自动执行这个回调函数。通过这种方式,我们实现了跨域请求。
上才艺
function jsonp(src) {
const script = document.createElement("script");
script.src = `${src}?callback=onSuccess`;
document.head.appendChild(script);
script.onload = () => {
// 加载成功后自动删除该标签
document.head.removeChild(script)
}
}
同时声明一个onSuccess
函数用于响应请求接收结果
function onSuccess(res) {
console.log(res);
}
以上:我们声明了一个jsonp函数用于跨域请求数据,并声明一个onSuccess
函数用于接收请求数据;
服务端响应(这里以node为例)
随便拉个脚手架搭个node服务,创建一个路由用于测试jsonp跨域请求。
search.get('/test', async ctx => {
const cb = ctx.query.callback;
const result = {
code: 0,
data: {
name: 'xxxxxx',
token: 'xxxxxxxxxxxxxxx'
}
}
ctx.body = `${cb}(${JSON.stringify(result)})`
})
获取请求里的callback参数,把结果放到这个callback方法里;
前端触发jsonp这个方法去请求/search/test 看看结果
可以看到已经可以成功请求到数据了。至此,通过jsonp方法来跨域我们已经了解了。但是一定要知道这种方式的弊端,为何我们现在都不用Jsonp来做跨域请求了呢?
jsonp它只支持get请求,因为浏览器加载script脚本是通过get方式来请求的,所以没法用post请求了。众所周知的是get和post的区别里 有一条特别重要的是get会直接把请求参数拼接到连接上,相对post来说这是不够安全的。
CORS
服务端通过配置请求头,允许所有的跨域请求;
这里还是以node服务为例,比如Koa库,我们建一个中间件用于处理跨域请求;
app.use(async (ctx, next)=> {
ctx.set('Access-Control-Allow-Origin', '*');
ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
if (ctx.method == 'OPTIONS') {
ctx.body = 200;
} else {
await next();
}
});
在koa实例里use这个middleware
这里主要是配置了Access-Control-Allow-Origin 允许了所有请求都能访问;
关于Access-Control-Allow-Origin的解释看mdn
node代理
现代化前端工程,基本都是通过node来打包构建的。常见的如webpack,vite等,在解决跨域请求的时候,都提供了相关的配置来处理跨域问题。通过配置proxy代理,将请求转发到当前项目里进行请求。
原理是跨域只存在于浏览器端,也就是说只受浏览器的影响,而通过node层去请求则不会有跨域问题。所以在项目开发阶段,把跨域请求转发到当前项目的node层里去请求,就不会有跨域的问题。
禁用同源策略
既然你浏览器有相关的安全协议禁止跨域请求,那我也可以禁用你这个协议,这样我就无视跨域的影响能正常发出请求了。
找到chrome浏览器新建一个快捷方式,点击快捷方式右键属性,找到目标 在目标里加上 --allow-file-access-from-files --user-data-dir="C:MyChromeUserData" --disable-web-security
点击应用并关闭窗口
c盘根目录新建一个名叫MyChromeUserData
的文件夹用于存储新建的这个浏览器快捷方式的信息
双击桌面上chrome新建的那个快捷方式,如果出现这样的提示,就说明成功了。
postMessage
页面跨域通信还可以通过postMessage来实现。这个场景是比如通过window.open打开一个小窗口或者页面加载一个iframe,两者需要进行通信。但受跨域影响,两者是无法进行通信的,此时可以通过postMessage来实现通信。
上才艺
!DOCTYPE html>
html>
head>
meta charset="utf-8" />
title>/title>
/head>
body>
iframe src="http://localhost:3000/" id="iframe">/iframe>
script type="module">
window.addEventListener('message', (e) => {
console.log(e.data);
});
/script>
/body>
/html>
页面的地址是http://127.0.0.1:8848/private/index.html
,它加载了一个iframe 同时window监听了message并输出结果
http://localhost:3000
页面有一个button,给这个btn添加一个点击事件,postMessage
方法第一个参数是传输的数据,第二个是传输的页面
!DOCTYPE html>
html>
head>
meta charset="utf-8">
title>postmsg/title>
/head>
body>
button id="btn">notify parent/button>
script>
document.getElementById("btn").addEventListener("click", () => {
parent.postMessage('this is postmessage', 'http://127.0.0.1:8848/private/index.html');
})
/script>
/body>
/html>
通过点击btn触发请求,输出e.data,data即是'this is postmessage'
其他的譬如通过Nginx反向代理来实现跨域请求等,这些可以自行去百度了。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net