Ajax:跨域 & JSONP
同源与跨域
同源
如果两个页面的协议、域名、端口号都相同,则两个页面同源
例如:
javascript
http://www.test.com/index.html
与其同源的网页:
javascript
http://www.test.com/other.html
http://www.test.com:80/other.html
注意,如果一个网址没有写:端口号
,说明默认端口号为80
。
不同源的网页:
javascript
http://blog.test.com/other.html // 域名不同
http://www.test.com:8888/other.html // 端口不同
https://www.test.com/other.html // 协议不同
注意,http
和https
不是一个协议。
同源策略
是浏览器提供的一个安全功能,同源策略限制了从同一个源加载的文档或脚本与另一个源的资源交互,这样可以隔离潜在的恶意文件。
JavaScript
就是一种脚本,受到同源策略限制,简单来说就是A
网站的JavaScript
无法与B
网站进行交互。
- 无法读取非同源页面的
cookie
、localStorage
等 - 无法接触非同源页面的
DOM
- 无法向非同源地址发送
Ajax
请求
跨域
如果两个页面的协议、域名、端口号有任何一项不同,则两个页面跨域
也就是说,同源的对立面就是跨域。
基于同源策略,浏览器会对跨域请求进行拦截,如下图:
浏览器允许用户通过Ajax
向跨域的地址发起请求,但是当响应到达浏览器,基于同源策略检测到响应跨域了,那么就会进行拦截,Ajax
收不到响应。
为了实现跨域请求,有JSONP
和CORS
两种解决方案。
JSONP
:兼容性好,兼容低版本IE,只支持GET
请求,不支持POST
CORS
:是跨域请求的根本解决方案,支持GET
和POST
请求,但是不兼容低版本浏览器
JSONP
JSONP
全称Json with Padding
,是一种json
的使用模式,可以解决主流浏览器的跨域问题。
由于浏览器同源策略的限制,网页中无法通过阿贾克斯请求非同源的接口数据。但是<script>
标签不受浏览器同源策略的影响,可以通过src
属性,请求非同源的js
脚本。
JSONP
的实现原理,就是通过 <script>
标签的 src
属性,请求跨域的数据接口,并通过函数调用的形式接收跨域接口响应回来的数据。
javascript
<script>
function test(data) {
console.log(data)
}
</script>
<script>
test({ name: '张三', age: 20 })
</script>
以上代码中,定义了两个<script>
标签,第一个标签内部实现了test
方法,第二个标签内调用了这个方法。由于所有的 <script>
标签共享全局作用域,这是合法的。
假设现在把这个调用的方法放到另一个js
文件中./js/test.js
:
javascript
test({ name: 'ls', age: 30 })
在./js/test.js
文件中,直接调用test
方法。
这样可以通过<script src=''>
进行引入:
javascript
<script>
function test(data) {
console.log(data)
}
</script>
<script src="./js/test.js"></script>
这样,当前页面的test
方法,就可以被./js/test.js
这个文件进行调用。
基于这个策略,可以实现两个服务器之间的JavaScript
脚本互相配合,如果想要告诉调用方法的服务器,自己有哪些函数可以调用,可以通过?callback=
的方式传递参数:
javascript
<script src="./js/test.js?callback=test"></script>
这就是JSONP
解决跨域的原理。
测试:
javascript
$.ajax({
method: 'GET',
url: 'file://D:\code_web\blog\test\test.js',
success: function (res) {
console.log(res)
}
})
以上代码在test.html
中,向file://D:\code_web\blog\test\test.js
发起请求,这是一个本地文件路径。为了用户安全考虑,浏览器不允许Ajax
直接访问用户主机上的内容,这是基于同源策略实现的。
报错:
其中Cross origin requests
表示这是一个跨域请求。
javascript
<script>
function test(data) {
console.log(data)
}
</script>
<script src="file://D:\code_web\blog\test\test.js?callback=test"></script>
同样的情况,把test
函数定义在test.html
中,通过src=
属性选择同一主机下的test.js
文件。
这次请求成功了,可以看到其发送了一个GET
请求,去请求file://D:\code_web\blog\test\test.js
这个文件,请求成功后,下方的控制台调用函数,并输出调用结果。
要注意的是,平常要通过<script>
引入本地js
文件,直接src="路径"
就可以了,无需file://
这样的协议前缀,这里只是为了测试跨域问题。
可以看出<script>
可以实现跨域请求,这也是JSONP
实现跨域的方式。
jQuery发送JSONP
使用jQuery
发送JSONP
请求非常简单,增加一个dataType
属性即可:
javascript
$(function () {
$.ajax({
url: 'https://jsonplaceholder.typicode.com/posts?id=1',
dataType: 'jsonp',
success: function (res) {
console.log(res)
}
})
})
如果要发送JSONP
请求,填入dataType: 'jsonp'
,由于JSONP
只能使用get
,所以也不再需要指定请求的方法为get
。
发送出去的请求,多出来一个callback=jQuery....
参数。这是由jQuery
自动生成的,如果通过jQuery
发送JSONP
请求,其会携带一个callback=jQuery....
参数,jQuery....
是随机生成的回调函数名称。
jQuery
允许用户自己指定这个回调函数的参数:
javascript
$.ajax({
url: 'https://jsonplaceholder.typicode.com/posts?id=1',
dataType: 'jsonp',
jsonp: 'call',
jsonpCallback: 'test',
success: function (res) {
console.log(res)
}
})
jsonp
:指定回调函数的key
jsonpCallback
:指定回调函数的value
此时回调函数就变成了call=test
,这可以方便后端进行解析。但是一般不会去指定jsonp
,直接用默认的callback
即可,更多时候只指定回调函数的函数名。
jQuery
也是通过<script>
标签的src
属性实现的跨域访问,但是jQuery
会动态创建和移除<script>
标签。在发起请求时创建一个<script>
,在请求成功后,再移除刚才创建的标签。