Ajax衍生的一系列问题
同源策略
MDN上的概念
:同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
要求:
- 协议相同
- 域名相同
- 端口相同
虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。
Ajax请求
同源政策规定,**AJAX请求只能发给同源的网址 **,否则就报错。
接下来让我们通过一个简单的例子理解一下ajax吧
简单手写一个简易的Ajax
function ajax(url) {
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// 请求成功
resolve(JSON.parse(xhr.responseText))
} else if (xhr.status === 404) {
reject(new Error('404 not found'))
}
// ...
}
}
xhr.send(null)
})
return p
}
const url = './user.json'
ajax(url)
.then(res => console.log(res))
.catch(err => console.log(err))
xhr.readyStatew
五种状态
- 0 未初始化:还没有调用send()方法
- 1 载入:已调用send()方法,正在发送请求
- 2 载入完成:send()方法执行完成,已经接收到全部响应内容
- 3 交互:正在解析响应内容
- 4 完成:响应内容解析完成,可以在客户端调用了
xhr.status
状态码
状态码会在http模块详细说,这里就先大概写一个:
- 2xx:表示成功处理请求,如200
- 3xx:需要重定向,浏览器直接跳转,如301、302、304
- 4xx:客户端请求错误,如404、403
- 5xx:服务器端错误
那么为了解决跨域通信,我们就得了解几种前端的方法
跨域通信的几种方式
JSONP
在CORS和postMessage以前,我们一直都是通过JSONP来做跨域通信的。
JSONP的原理:通过<script>
标签的异步加载来实现的。比如说,实际开发中,我们发现,head标签里,可以通过<script>
标签的src,里面放url,加载很多在线的插件。这就是用到了JSONP。
JSONP的实现:
比如说,客户端这样写:
<script src="http://www.huabyte.com/?data=name&callback=jsonp"></script>
上面的src中,data=name
是get请求的参数,jsonp
是和后台约定好的函数名。
服务器端这样写:
jsonp({
data: {}
})
于是,本地要求创建一个jsonp 的全局函数,才能将返回的数据执行出来。
WebSocket
Websocket的推荐链接:www.ruanyifeng.com/blog/2017/0…
CORS
CORS 可以理解成是既可以同步、也可以异步的Ajax。
跨域时,浏览器会拦截Ajax请求,并在http头中加Origin。
fetch 是一个比较新的API,用来实现CORS通信。用法如下:
// url(必选),options(可选)
fetch('/api/url/', {
method: 'get',
}).then(function (response) { //类似于 ES6中的promise
}).catch(function (err) {
// 出错了,等价于 then 的第二个参数,但这样更好用更直观
});
CORS的推荐链接:www.ruanyifeng.com/blog/2016/0…
Hash
url的#
后面的内容就叫Hash。Hash的改变,页面不会刷新。这就是用 Hash 做跨域通信的基本原理。
补充:url的?
后面的内容叫Search。Search的改变,会导致页面刷新,因此不能做跨域通信。
使用举例:
场景:我的页面 A 通过iframe或frame嵌入了跨域的页面 B。
现在,我这个A页面想给B页面发消息,怎么操作呢?
(1)首先,在我的A页面中:
//伪代码
var B = document.getElementsByTagName('iframe');
B.src = B.src + '#' + 'jsonString'; //我们可以把JS 对象,通过 JSON.stringify()方法转成 json字符串,发给 B
(2)然后,在B页面中:
// B中的伪代码
window.onhashchange = function () { //通过onhashchange方法监听,url中的 hash 是否发生变化
var data = window.location.hash;
};
postMessage()方法
在之前实习时遇到了这个情况当时特地写了一篇记录:使用 iframe + postMessage实现跨域通信
H5中新增的postMessage()方法,可以用来做跨域通信。既然是H5中新增的,那就一定要提到。
场景:窗口 A (http:A.com
)向跨域的窗口 B (http:B.com
)发送信息。步骤如下。
(1)在A窗口中操作如下:向B窗口发送数据:
// 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息
Bwindow.postMessage('data', 'http://B.com'); //这里强调的是B窗口里的window对象
(2)在B窗口中操作如下:
// 在窗口B中监听 message 事件
Awindow.addEventListener('message', function (event) { //这里强调的是A窗口里的window对象
console.log(event.origin); //获取 :url。这里指:http://A.com
console.log(event.source); //获取:A window对象
console.log(event.data); //获取传过来的数据
}, false);