1、什么是同源策略级限制?
2、前后端如何通信?
3、如何创建ajax?
4、跨域通信的几种方式?
什么是同源策略限制:
先解释什么是源,然后再解释
源:协议,域名,端口 三个有其中一个不一样,就是不同源,
就是不是同一个源不能操作另外一个源的东西
主要限制在以下三个方面:
cookie、localstorage 和indexDB无法读取
DOM无法获得
ajax请求不能发送
前后端如何通信
Ajax(同源策略限制) WebSocket(不受同源策略限制) CORS(支持同源也支持不同源)
如何创建ajax?
XMLHttpRequest对象的工作流程
兼容性处理
事件触发的条件
事件的触发顺序
创建对象xhr ->open() ->send()
响应 xhr.onload = function(){}
/**
* [json 实现ajax的json]
* @param {[type]} options [description]
* @return {[type]} [description]
*/
util.json = function (options) {
var opt = {
url: ‘‘,
type: ‘get‘,
data: {},
success: function () {},
error: function () {},
};
util.extend(opt, options);
if (opt.url) {
var xhr = XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject(‘Microsoft.XMLHTTP‘);
var data = opt.data,
url = opt.url,
type = opt.type.toUpperCase(),
dataArr = [];
for (var k in data) {
dataArr.push(k + ‘=‘ + data[k]);
}
if (type === ‘GET‘) {
url = url + ‘?‘ + dataArr.join(‘&‘);
xhr.open(type, url.replace(/\?$/g, ‘‘), true);
xhr.send();
}
if (type === ‘POST‘) {
xhr.open(type, url, true);
xmlhttp.setRequestHeader(‘Content-type‘, ‘application/x-www-form-urlencoded‘);
xhr.send(dataArr.join(‘&‘));
}
xhr.onload = function () {
if (xhr.status === 200 || xhr.status === 304) {
var res;
if (opt.success && opt.success instanceof Function) {
res = xhr.responseText;
if (typeof res ==== ‘string‘) {
res = JSON.parse(res);
opt.success.call(xhr, res);
}
}
} else {
if (opt.error && opt.error instanceof Function) {
opt.error.call(xhr, res);
}
}
};
}
};
跨域通信的几种方式
JSONP:img中的src,link的href,script 中的src这三个都不符合同源策略,但是它们都能跨域获取资源,jsonp就是利用script的这个特点来实现跨域获取数据的
它只支持GET请求而不支持POST等其它类型的HTTP请求
jsonp是由两部份组成的,回调函数和数据
Hash: url#后面的就是hash,hash改变,页面不刷新 缺点:另外由于URL大小的限制,支持传递的数据量也不大。
postMessage:
优缺点:
优点:方便,安全,有效的解决了跨域问题
缺点:万恶的资本主义,ie8+才支持,而且ie8+<ie10只支持iframe的方式。
WebSocket:
CORS:
它允许浏览器向跨源服务器,发出XMLHttpRequest
请求,从而克服了AJAX只能同源使用的限制
CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
浏览器发出CROS请求,就是在头信息增加一个origin字段,这个字段用来说明本次请求来自哪个源,服务器会根据这个源绝对是否同意这次请求
jsonp
<div class="left" id="left">left</div>
<div class="right">right</div>
</section>
<script type="text/javascript" charset="utf-8">
document.getElementById(‘left‘).onclick = function () {
var script = document.createElement(‘script‘);
script.src = "https://api.douban.com/v2/book/search?q=javascript&count=1&callback=handleResponse";
document.body.insertBefore(script,document.body.firstChild)
}
function handleResponse(res){
console.log(‘res‘,res)
}
</script>
hash
原理是利用location.hash来进行传值。
假设域名a.com下的文件cs1.html要和cnblogs.com域名下的cs2.html传递信息。
1) cs1.html首先创建自动创建一个隐藏的iframe,iframe的src指向cnblogs.com域名下的cs2.html页面
2) cs2.html响应请求后再将通过修改cs1.html的hash值来传递数据
3) 同时在cs1.html上加一个定时器,隔一段时间来判断location.hash的值有没有变化,一旦有变化则获取获取hash值
注:由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe
代码如下:
先是a.com下的文件cs1.html文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
function startRequest(){ var ifr = document.createElement( ‘iframe‘ ); ifr.style.display = ‘none‘ ; document.body.appendChild(ifr); } function checkHash() { try { var data = location.hash ? location.hash.substring(1) : ‘‘ ; if (console.log) { console.log( ‘Now the data is ‘ +data); } } catch (e) {}; } setInterval(checkHash, 2000); |
cnblogs.com域名下的cs2.html:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//模拟一个简单的参数处理操作 switch (location.hash){ case ‘#paramdo‘ : callBack(); break ; case ‘#paramset‘ : //do something…… break ; } function callBack(){ try { parent.location.hash = ‘somedata‘ ; } catch (e) { // ie、chrome的安全机制无法修改parent.location.hash, // 所以要利用一个中间的cnblogs域下的代理iframe var ifrproxy = document.createElement( ‘iframe‘ ); ifrproxy.style.display = ‘none‘ ; document.body.appendChild(ifrproxy); } } |
a.com下的域名cs3.html
1
2
|
//因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值 parent.parent.location.hash = self.location.hash.substring(1); |
// 利用hash,场景是当前页面 A 通过iframe或frame嵌入了跨域的页面 B
/
postMessage
postMessage实现跨域
语法:window.postMessage(msg,targetOrigin)
window: 指目标窗口,可能是window.frames属性的成员或者由window.open方法创建的窗口
message:要发送的消息,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果
targetOrigin:“目标域“,包括:协议、主机名、端口号。若指定为”*“,则表示可以传递给任意窗口,指定为”/“,则表示和当前窗口的同源窗口。
获取postMessage传来的消息:为页面添加onmessage事件
1 window.addEventListener(‘message‘,function(e) { 2 3 }
onmessage事件接受一个参数e,它是一个event对象。
e的几个重要属性:
1、data:postMessage传递过来的msg
2、发送消息的窗口对象
3、origin:发送消息窗口的源(协议+主机+端口号)
来写一个简单的demo:
http://source.com/source.html用来发送数据:
1 <iframe id="iframe" src="http://target.com/target.html"></iframe> 2 <input id="msg" type="text" placeholder="请输入要发送的消息"> 3 <button id="send">发送</button>
1 window.onload =function() { 2 document.getElementById(‘send‘).onclick = function() { 3 var msg = document.getElementById(‘msg‘).value; 4 var iframeWindow = document.getElementById(‘iframe‘).contentWindow; 5 iframeWindow.postMessage(msg,"http://target.com/target.html"); 6 } 7 }
http://target.com/target.html用来接收数据:
1 <div> 2 <h2>target.html,以下是接收到的消息:</h2> 3 <section id="msg"> 4 5 </section> 6 </div>
1 window.onload = function() { 2 3 if(window.addEventListener){ 4 window.addEventListener("message", handleMessage, false); 5 } 6 else{ 7 window.attachEvent("onmessage", handleMessage); 8 } 9 10 function handleMessage(event) { 11 event = event || window.event; 12 13 if(event.origin === ‘http://source.com‘) { 14 document.getElementById(‘msg‘).innerHTML = event.data; 15 } 16 } 17 }
// Websocket【参考资料】http://www.ruanyifeng.com/blog/2017/05/websocket.html
var ws = new WebSocket(‘wss://echo.websocket.org‘);
ws.onopen = function (evt) {
console.log(‘Connection open ...‘);
ws.send(‘Hello WebSockets!‘);
};
ws.onmessage = function (evt) {
console.log(‘Received Message: ‘, evt.data);
ws.close();
};
ws.onclose = function (evt) {
console.log(‘Connection closed.‘);
};
// CORS【参考资料】http://www.ruanyifeng.com/blog/2016/04/cors.html
// url(必选),options(可选)
fetch(‘/some/url/‘, {
method: ‘get‘,
}).then(function (response) {
}).catch(function (err) {
// 出错了,等价于 then 的第二个参数,但这样更好用更直观
});