由于同源策略的限制,Ajax
不允许跨域访问,但是像、
、
、这些标签是允许跨域的,但你并不能修改这些资源,比如
iframe
里的内容,更准确的来说这些标签可以获取资源。
JSONP
是JSON with padding
(填充式JSON
)。
JSONP
利用了HTML
中标签可以跨域的原理,利用
标签向服务器请求一段js代码,然后执行这一段JS代码,实现跨域。
首先,要了解标签上发生了什么。在编码中我们经常会通过
引用cdn等处于其它域名下的静态资源,毫无疑问是可以加载成功的。其实
标签的实质就是往
标签里的src发起一个请求,获取到JS代码,然后把代码放到当前执行。实际上里面的.js后缀只是我们习惯的用法,无论是什么文件,都会把里面的内容下载下来当作JS在当前来执行。所以,即使请求对的是
.txt
文件,也会把.txt
里面的内容下载下来当作JS在当前来执行。所以,无论标签里可以是任何东西,只要是可以访问到并且得到数据的一个接口就可以了,都会把返回的内容当作JS在当前运行。
下面我们来了解以下Ajax是怎么利用标签来进行跨域访问的。
首先要说一下,JSONP
是需要前后端的配合的,很多人之所以对对JSONP
懵懵懂懂,从我自身的感觉来说,很多是因为不了解后端是怎么与前端配合实现JSONP
的。
前端代码:
**HTML**
**JavaScript**
var btn=document.getElementById("#getData");
btn.addEventListener('click', function(){
var script = document.createElement('script');
script.src = 'http://www.wuxiaozhou.com/getData?callback=getData';
document.head.appendChild(script);
document.head.removeChild(script);
})
function getData(data){
document.write(data);
}
后端代码(后端语言有很多种,可能不同语言的代码会不一样):
app.get('/getData', function(req, res){
var data = ["海贼王","火影忍者","灌篮高手","名侦探柯南"];
var cb = req.query.callback;
if(cb){
res.send(cb + '('+ JSON.stringify(data) + ')');
}else{
res.send(data);
}
})
前端代码里,比如代码页面所在的域名是http://www.xiaozhou.com
我给个按钮绑定了一个事件,点击按钮会向为http://www.wuxiaozhou.com/getData
获取数据。因为两者的主机名不一样,所以这个是跨域访问。
其实是先创建一个标签,然后把
标签的src属性设置为获取数据的接口加一个键值对
http://www.wuxiaozhou.com/getData?callback=getData
,再把标签放到页码的
标签里面。当点击了按钮后,上面的操作就会进行,就会向
http://www.wuxiaozhou.com/getData
发起请求。
也许你会有疑问,为什么需要在请求的接口后面加上一个键值对?下面就为你解答。
后端接收到请求后,找到callback
的值,然后把callback
的值加上用括号括起来并转化成字符串的需要返回的数据返回来。在我的这个例子中,callback
的值就是getData
,返回给客户端的就是getData('["海贼王","火影忍者","灌篮高手","名侦探柯南"]')
。
可能你也注意到后端语言里有一个判断,如果判断到有callback
的值存在,那就说明是一个跨域的请求,是按照上面我们说的方式发起的,用if
后面的方式返回数据;因为同域请求的话请求里是没有后面的那一个键值对的,所以后台判断没有这个callback
的值,就会将数据直接返回,也就是else
后面的方式。这里是为了同时可以处理同源请求和跨域的JSONP
请求。
前端页面接收到后端返回对的数据后,就会将返回的数据当作JS去执行,在我们这个例子中就是:
getData('["海贼王","火影忍者","灌篮高手","名侦探柯南"]')
你也注意到我们在前端代码里声明了一个与callback
的值一样的函数,在我们这个例子就是:
function getData(data){
document.write(data);
}
所以就会执行这个函数,参数是'["海贼王","火影忍者","灌篮高手","名侦探柯南"]'
。这个时侯相当于数据被当作参数传进了这个函数了,这个函数就是数据的处理函数,你也可以对返回的字符串数据进行其它的各种各样的操作。在我的这个例子中就会把'["海贼王","火影忍者","灌篮高手","名侦探柯南"]'
显示在页面上。
前端跟后端要协调好的就是在发送请求的参数名要一致,比如我这个例子的参数名是callback
,后端要查询的参数名也要是callback
,否则的话后端就找不到前端发送的参数。没有特殊说明,一般约定俗成都是callback
吧。