当前位置:网站首页>解决跨越的几种方案
解决跨越的几种方案
2022-07-28 12:49:00 【Maic】
跨域产生的原因首先是受浏览器的安全性设计影响,由于浏览器同源策略的设计,所以产生了跨域。
在项目中,我们常常遇到跨域的问题,虽然在你的项目里,脚手架已经100%做好了本地代理、或者运维老铁在nginx中也已经给你做了接口代理,所以你遇到跨域的概率会少了很多,但是在传统的项目中,在那个jquery的横行时代,或者你接手了一个祖传项目时,跨域问题会是时有发生,本文只做笔者了解跨域的通用解决方案,希望在实际项目中对你有些思考和帮助。
正文开始...
何为同源
- 协议相同
- 域名相同
- 端口相同
非同源是无法彼此访问cookie,dom操作,ajax、localStorage、indexDB操作
但是非同源可以访问以下一些属性
window.postMessage();
window.location
window.frames
window.parent
...
对于非同源网站跨域通信,我们可以采用以下几种方案
片段标识符
通过在url上放入#type,利用hashchange事件,获取父页面的相关hash数据
// parent
const type = 'list';
const originUrl = originUrl + '#'+type;
const iframe = document.getElementById('iframe');
iframe.src = originUrl;
在子iframe中,我们可以监听hashchange事件
// iframe
window.addEventListener('hashchange', (evt) => {
console.log(evt) // 获取片段标识符的相关数据
})
跨文档通信 window.postMessage
子页面可以利用window.postMessage向父页面发送信息,父页面监听message接收子页面发送的消息,通常这种方式在iframe通信中用得比较多,也是跨域通信的一种解决方案
// parent
window.addEventListener('message', (evt) => {
const data = evt.data;
console.log(data)
})
// child
const origin = '*'; // 这里一般是父页面的域名,但是也可以是*
const data = {
text: 'hello, world'
};
window.postMessage(data,origin)
jsonp
利用script标签,向服务端发送一个get请求,url上绑定一个callback=fn,这个fn通常是与后端约束好,fn是客户端执行的函数名。
用一个简单的例子来解释下jsonp
客户端示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>jsonp</title>
</head>
<body>
<h3>jsonp</h3>
<div id="app"></div>
<script>
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.src = 'http://192.168.31.40:8080/api?callback=jsonp';
document.body.appendChild(script);
const renderHtml = ({name, age}, dom) => {
dom.innerHTML = `<div>我的名字 ${name},今年 ${age}岁了</div>`
}
function jsonp(data) {
console.log(data);
const app = document.getElementById('app');
const {name, age} = data;
renderHtml({name, age}, app)
}
</script>
</body>
</html>
服务端示例代码
// server.js
const http = require('http');
const fs = require('fs');
const path = require('path');
const PORT = '8080';
const server = http.createServer((req, res) => {
res.statusCode = 200;
// console.log(req.url);
res.setHeader('Content-Type', 'text/html');
fs.readFile(path.resolve(__dirname, '../', 'index.html'), (err, data) => {
if (err) {
res.end('404');
return;
}
res.end(data);
});
const data = {
name: 'Maic',
age: Math.floor(Math.random() * 20)
}
if (req.url.includes('/api')) {
res.end(`jsonp(${JSON.stringify(data)})`)
}
});
server.listen(PORT, () => {
console.log('server is start'+ PORT);
})
注意我们看到有这样的一段判断req.url.includes('/api'),然后我们res.end(jsonp(JSON.stringify(data))),返回的就是jsonp这个回调函数,把数据当形参传给前端,在客户端定义的jsonp方法就能获取对应的数据。
执行node server.js打开http://localhost:8080我们会发现
返回的数据就是jsonp({name: 'xx',age: 'xx'})
我们看下请求头
浏览器会发送一个get请求,所携带的参数就是callback: jsonp,而我们在客户端确实是通过jsonp这个方法拿到对应的数据了。
所以我们可以知道jsonp实际上就是利用一个客户端发送的get请求携带一个后端服务的返回的回调函数,在客户端,我们定义这个回调函数就可以获取后端返回的形参数据了。
从jsonp这种跨域通信来看,其实有也它的缺点和优点
- 缺点
1、它的安全性会有一定风险,因为依赖的结果就是那个回调函数的形参内容,如果被人劫持修改返回数据,那可能会造成安全性问题
2、仅支持get请求,不支持其它http请求方式,我们发现jsonp这种通信就是利用script标签请求了一个url,url上携带了一个可执行的回调函数,进而通过后端给回调函数传递数据的。
3、没有任何状态码,数据丢给客户端,如果有失败情况,不会有像http状态码一样
- 优点
能解决跨域通信问题,兼容性比较好,不受同源策略的影响,对后端来说实现也简单。
在以前比较久远的项目中,你可能是直接使用jquery,jsonp就ok了,大概的代码就是长这样的
$.ajax({
url: 'xxx',
dataType: 'jsonp',
jsonp: 'successCallback',
success: (data) => {
console.log(data)
},
error: (err) => {
console.log(err)
}
})
successCallback这个就是与服务端约束的回调函数,一般与服务端沟通一致就行,那么简单的jsonp就已经完成了,是不是感觉很简单呢?
WebSocket
由于WebSocket不受同源策略的限制,因此WebSocket也是可以实现跨域通信的。这里就不举例子了,具体可以参考之前写的一篇浅谈websocket这篇文章
CORS跨域资源分享
这种方式是在服务端进行控制,允许任何资源请求。其实在浏览器端,即使跨域,还是会正常请求,只是请求非同源环境的后端服务,浏览器禁止请求访问,更多可以参考这篇文章cors[1]
我们写个例子具体测试一下,在客户端加入这段代码
const send = document.getElementById('send');
send.onclick = function () {
if (!window.fetch) {
return;
}
fetch('http://localhost:8081/list.json')
.then((res) => res.json())
.then((result) => {
console.log(result);
const contentDom = document.querySelector('.content');
renderHtml(result, contentDom);
});
};
服务端代码,我们新建一个index2.js服务端代码,并执行node index2.js
const http = require('http');
const PORT = '8081';
const server = http.createServer((req, res) => {
res.statusCode = 200;
// // console.log(req.url);
res.setHeader('Content-Type', 'application/json');
if (req.url.includes('/list.json')) {
res.end(JSON.stringify({
name: 'maic',
age: Math.ceil(Math.random() * 20)
}));
}
});
server.listen(PORT, () => {
console.log('server is start'+ PORT);
})
我们注意到请求的端口是8081,打开8080页面
点击按钮,发送fetch请求,我们发现浏览器报了这样你常常看到的跨域信息as been blocked by CORS policy: No 'Access-Control-Allow-Origin' header...,因为跨域了
紧接着我们在服务端设置下Access-Control-Allow-Origin: *
const http = require('http');
const PORT = '8081';
const server = http.createServer((req, res) => {
res.statusCode = 200;
// // console.log(req.url);
res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin', '*');
if (req.url.includes('/list.json')) {
res.end(JSON.stringify({
name: 'maic',
age: Math.ceil(Math.random() * 20)
}));
}
});
server.listen(PORT, () => {
console.log('server is start'+ PORT);
})
此时再次访问时,已经ok了
注意我们可以看下Response Headers
HTTP/1.1 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: *
Date: Sun, 01 May 2022 14:23:59 GMT
Connection: keep-alive
Content-Length: 24
Access-Control-Allow-Origin:*这是我们服务的设置响应请求头,设置允许所有域名请求。
因此cors跨域其实在服务端设置Access-Control-Allow-Origin:*也就完美的解决了跨域问题。
总结
- 跨域产生的原因,主要受同源策略的影响,非同源环境,无法相互访问
cookie、localStorage、dom操作等 - 解决跨域的方案主要有
片段标识符、iframe通信postMessage,jsonp、WebSocket、cors - 用具体实际例子深入了解几种跨域模式,比如
jsonp,实际上是利用script发送一个get请求,在get请求的参数中传入一个可执行的回调函数,服务根据请求,将返回一个前端可执行的回调函数,并且将数据当成该回调函数的形参,在前端定义该回调函数,从而获取调函数传入的数据。 - 用具体例子服务端设置
cors,主要是在后端接口返回响应头里设置Access-Control-Allow-Origin:*允许所有不同源网站访问,这种方法也是比较粗暴的解决跨域问题的常用手段。 - 本文示例代码code example[2]
参考资料
[1]cors: https://www.wangdoc.com/javascript/bom/cors.html
[2]code example: https://github.com/maicFir/lessonNote/tree/master/node/jsonp
边栏推荐
- C语言:优化后的归并排序
- Customized template in wechat applet
- 记一次使用pdfbox解析pdf,获取pdf的关键数据的工具使用
- 不用Swagger,那我用啥?
- Intra prediction and transform kernel selection based on Neural Network
- 数据库系统原理与应用教程(060)—— MySQL 练习题:操作题 11-20(四)
- To build agile teams, these methods are indispensable
- Excellent performance! Oxford, Shanghai, AI Lab, Hong Kong University, Shangtang, and Tsinghua have joined forces to propose a language aware visual transformer for reference image segmentation! Open
- SAP UI5 FileUploader 控件实现本地文件上传,接收服务器端的响应时遇到跨域访问错误的试读版
- 30天刷题计划(三)
猜你喜欢

.NET桌面开发的一些思考

严格模式——let和const——箭头函数——解构赋值——字符串模板symbol——Set和Map——生成器函数

Jenkins--持续集成服务器

30 day question brushing plan (IV)

使用 IPtables 进行 DDoS 保护

蓝桥集训(附加面试题)第七天

Excellent performance! Oxford, Shanghai, AI Lab, Hong Kong University, Shangtang, and Tsinghua have joined forces to propose a language aware visual transformer for reference image segmentation! Open

Use non recursive method to realize layer traversal, preorder traversal, middle order traversal and post order traversal in binary tree

No swagger, what do I use?

Strict mode -- let and const -- arrow function -- Deconstruction assignment -- string template symbol -- set and map -- generator function
随机推荐
蓝桥集训(附加面试题)第七天
Cesium pit -- pit used by various API calls and API itself
The domestic API management tool eolink is very easy to use, creating an efficient research and development tool
GO语言-栈的应用-表达式求值
.NET的求复杂类型集合的差集、交集、并集
Tutorial on the principle and application of database system (059) -- MySQL exercise questions: operation questions 1-10 (III)
JWT 登录认证 + Token 自动续期方案,写得太好了!
Cool operation preheating! Code to achieve small planet effect
The strongest distributed locking tool: redisson
比XShell更好用、更现代的终端工具!
Tutorial on the principle and application of database system (061) -- MySQL exercise: operation questions 21-31 (V)
图的遍历(BFS&&DFS基础)
Go language - Application of stack - expression evaluation
Use non recursive method to realize layer traversal, preorder traversal, middle order traversal and post order traversal in binary tree
[ecmascript6] function and its related use
Some thoughts on.Net desktop development
R语言使用lm函数构建多元回归模型(Multiple Linear Regression)、并根据模型系数写出回归方程、使用confint函数给出回归系数的95%置信区间
持续(集成--&gt;交付--&gt;部署)
C language: random number + quick sort
数据库系统原理与应用教程(059)—— MySQL 练习题:操作题 1-10(三)