一、 跨域请求的含义
2.3 使用 标签原生实现 JSONP
经过上面的事件,你是不是觉得 JSONP 的实现和 Ajax 大同小异?
其实,由于实现的原理不同,由 JSONP 实现的跨域调用不是通过 XmlHttpRequset 对象,而是通过 script 标签,所以在实现原理上,JSONP 和 Ajax 已经一点关系都没有了。看上去形式相似只是由于 jQuery 对 JSONP 做了封装和转换。
比如在上面的例子中,我们假设要传输的数据 data 格式如下:
{
name:"chiaki",
id": "3001"
}
那么数据是如何传输的呢?HTTP 请求头的第一行如下:
GET /ajax/deal?callback=jsonpCallback&name=chiaki&id=3001&_=1473164876032 HTTP/1.1
可见,即使形式上是用 POST 传输一个 JSON 格式的数据,其实发送请求时还是转换成 GET 请求。
其实如果理解 JSONP 的原理的话就不难理解为什么只能使用 GET 请求方法了。由于是通过 script 标签进行请求,所以上述传输过程根本上是以下的形式:
这样从服务器返回的代码就可以直接在这个 script 标签中运行了。下面我们自己实现一个 JSONP:
服务器 3000请求页面的 JavaScript 代码中,只有回调函数 jsonpCallback:
functionjsonpCallback(data){
console.log("jsonpCallback: "+data.name)
}
服务器 3000请求页面还包含一个 script 标签:
服务器 3001上对应的处理函数:
app.get('/jsonServerResponse',function(req,res){
varcb=req.query.jsonp
console.log(cb)
vardata='var data = {'+'name: $("#name").val() + " - server 3001 jsonp process",'+'id: $("#id").val() + " - server 3001 jsonp process"'+'};'
vardebug='console.log(data);'
varcallback='$("#submit").click(function() {'+data+cb+'(data);'+debug+'});'
res.send(callback)
res.end()
})
与上面一样,我们在所获取的参数后面加上 “ – server 3001 jsonp process” 代表服务器对数据的操作。从代码中我么可以看到,处理函数除了根据参数做相应的处理,更多的也是进行字符串的拼接。
最终的结果为:
2.4 JSONP 总结
至此,我们了解了 JSONP 的原理以及实现方式,它帮我们实现前端跨域请求,但是在实践的过程中,我们还是可以发现它的不足:
只能使用 GET 方法发起请求,这是由于 script 标签自身的限制决定的。
不能很好的发现错误,并进行处理。与 Ajax 对比,由于不是通过 XmlHttpRequest 进行传输,所以不能注册 success、 error 等事件监听函数。
三、 使用 CORS 实现跨域调用
3.1 什么是 CORS?
Cross-Origin Resource Sharing(CORS)跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。用 CORS 可以让网页设计师用一般的 XMLHttpRequest,这种方式的错误处理比 JSONP 要来的好。另一方面,JSONP 可以在不支持 CORS 的老旧浏览器上运作。现代的浏览器都支持 CORS。
3.2 CORS 的实现
还是以 服务器 3000 上的请求页面向 服务器 3001 发送请求为例。
服务器 3000 上的请求页面 JavaScript 不变,如下:
$(function(){
$("#submit").click(function(){
vardata={
name:$("#name").val(),
id:$("#id").val()
};
$.ajax({
type:'POST',
data:data,
url:'http://localhost:3001/cors',
dataType:'json',
cache:false,
timeout:5000,
success:function(data){
console.log(data)
},
error:function(jqXHR,textStatus,errorThrown){
console.log('error '+textStatus+' '+errorThrown);
}
});
});
});
服务器 3001上对应的处理函数:
app.post('/cors',function(req,res){
res.header("Access-Control-Allow-Origin","*");
res.header("Access-Control-Allow-Headers","X-Requested-With");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By",' 3.2.1')
res.header("Content-Type","application/json;charset=utf-8");
vardata={
name:req.body.name+' - server 3001 cors process',
id:req.body.id+' - server 3001 cors process'
}
console.log(data)
res.send(data)
res.end()
})
在服务器中对返回信息的请求头进行了设置。
最终的结果为:
3.3 CORS 中属性的分析
Access-Control-Allow-Origin
The origin parameter specifies a URI that may access the resource. The browser must enforce this. For requests without credentials, the server may specify “*” as a wildcard, thereby allowing any origin to access the resource.
Access-Control-Allow-Methods
Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request. The conditions under which a request is preflighted are discussed above.
Access-Control-Allow-Headers
Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
3.4 CORS 与 JSONP 的对比
CORS 除了 GET 方法外,也支持其它的 HTTP 请求方法如 POST、 PUT 等。
CORS 可以使用 XmlHttpRequest 进行传输,所以它的错误处理方式比 JSONP 好。
JSONP 可以在不支持 CORS 的老旧浏览器上运作。
四、 一些其它的跨域调用方式
4.1 window.name
window对象有个name属性,该属性有个特征:即在一个窗口 (window) 的生命周期内,窗口载入的所有的页面都是共享一个 window.name 的,每个页面对 window.name 都有读写的权限,window.name 是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
4.2 window.postMessage()
这个方法是 HTML5 的一个新特性,可以用来向其他所有的 window 对象发送消息。需要注意的是我们必须要保证所有的脚本执行完才发送 MessageEvent,如果在函数执行的过程中调用了他,就会让后面的函数超时无法执行。
参考:https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage