Ajax
XMLHttpRequest
Ajax 原理如下:
const xhr = new XMLHttpRequest()
// 第三个参数(Boolean)为是否异步执行请求,即待服务器响应后再继续执行 Ajax 接下来的代码
xhr.open('GET', 'https://api.github.com', true)
xhr.onreadystatechange = function () { // 异步回调
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
console.log(xhr.responseText)
} else {
throw new Error(`Request was unsuccessful : ${xhr.status}`)
}
}
}
xhr.send(null)
2
3
4
5
6
7
8
9
10
11
12
13
14
注:根据最新 xhr Standard - open() method,主线程中的同步的 XMLHttpRequest
已被废弃,默认地,open()
方法第三参数为 true
,即默认异步请求。
状态码
2XX - Success - 表示成功处理请求。如 200。
3XX - Redirection - 需要重定向,浏览器直接跳转。
4XX - Client Error - 客户端请求错误。
5XX - Server Error - 服务端响应错误。
客户端跨域
(以下是重新整理我的博客 —— 客户端跨域解决方案)
跨域的定义
客户端为因安全问题,会默认阻止解析(并不阻止请求,这也是 CORS 的原理) Ajax 请求的非同源(请求地址的协议、域名、端口任意一项与当前页面不同)资源。即浏览器并不会阻止 Ajax 跨域请求,阻止的是解析 Ajax 跨域请求返回的数据。
上图中,请求地址是非同源地址,由 response
可看出客户端并未阻止跨域 Ajax 请求,但是浏览器在服务端返回数据后进行 Access-Control-Allow-Origin
验证时,发现当前源(http://localhost:8889
)并没有跨域权限(没有在服务端的白名单中)中,故浏览器拒绝解析服务端返回给客户端的数据,此时的客户端是已经接受了跨域请求的数据了的,只是客户端拒绝解析。
浏览器默认执行同源策略。即只会解析与当前源对比同协议,同域名,同端口的请求地址返回的数据请求。
正确在服务端配置 CORS
后的返回数据如下:
由上图可看出,浏览器在跨域请求后,通过验证 Access-Control-Allow-Origin
得到允许当前域(http://localhost:8889
)跨域请求目标服务器(https://api.github.com
)。
重要:一般情况下,浏览器并不会阻止跨域请求,但会阻止跨域请求后的数据解析。
客户端跨域解决方案
只有在客户端才会存在同源策略,那么可以依据此原理并结合 HTML
标签的 src
属性(如 script
、img
(可能有防盗链措施)、link
标签)不受客户端同源策略限制,得到以下客户端跨域解决方案:
JSONP (JSON with padding)
- 利用
script
标签的src
属性是不受客户端同源策略限制的原理,但必须保证请求源的安全性
- 利用
目标服务器设置
header:Access-Control-Allow-Origin
,即服务端配置CORS
- 趋势- 需要客户端和服务端同时支持,若客户端不支持跨域请求,那么即使服务端开启
CORS
也无济于事
- 需要客户端和服务端同时支持,若客户端不支持跨域请求,那么即使服务端开启
中转服务器代理转发客户端请求
- 利用只有客户端存在同源限制,服务端不存在同源限制的原理
拓展:
利用元素标签的 src
属性跨域的使用场景:
img
用于统计link
、script
用于 CDNscript
用于JSONP
JSONP 实现原理
<script>
window.callBack = function (data) {
console.log(data)
}
</script>
<script src="https://example.com/api.js?jsonpCallback=callBack"></script>
<!-- src 将返回 callBack({ x: 100, y: 200 }) -->
2
3
4
5
6
7
前置知识:
HTML
在src
请求 JS 脚本后就会立即执行得到的 JS 脚本(除非设置defer
属性,表示在下载 JS 同时,不阻塞 DOM 渲染,并延迟至 DOM 树建立后执行)。因为浏览器会默认会将不同的
script
(包含外联 JS 和嵌入式 JS)合并为一个模块。那么,一个document
文档内的script
标签中src
请求的 JS 代码默认(除非设置type="module"
,表示当前 JS 脚本文件是一个单独模块)都属于同一模块(都在同一个容器中)。JavaScript
中圆括号即表示函数调用,如fn()
表示调用fn
。
示例中,JSONP
将返回数据作为 callBack
的参数返回。并且此时执行 callBack
调用,那么数据得以解析。进而绕过浏览器同源策略。
JSONP 局限性
只支持
GET
请求。因为是通过
HTML
标签的src
属性来实现跨域的,那么无法验证请求数据的安全性,那么一定要保证请求域的安全性的情况下使用JSONP
来实现跨域。必须另外添加计时器来判断请求是否超时。