1 跨域及同源策略
1.1 什么是跨域@H_502_7@
一般来说,如果你在开发中需要进行跨域操作(从一个非同源网站发送请求获取数据),一般而言,你在浏览器控制台看到的结果为:
XMLHttpRequest cannot load http://external-domain/service. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://my-domain’ is therefore not allowed access.
像这样发送ajax请求会在浏览器中得到如下访问受限的提示
<html> <head> <Meta charset="utf-8" /> <script> function success(text) { var textarea = document.getElementById('test-response-text'); textarea.innerHTML = text; } function fail(code) { var textarea = document.getElementById('test-response-text'); textarea.innerHTML = 'Error code: ' + code; } function helloAjax() { var request = new XMLHttpRequest(); // 新建XMLHttpRequest对象 request.onreadystatechange = function () { // 状态发生变化时,函数被回调 if (request.readyState === 4) { // 成功完成 // 判断响应结果: if (request.status === 200) { // 成功,通过responseText拿到响应的文本: //alert('request.responseText'); return success(request.responseText); } else { // 失败,根据响应码判断失败原因: //alert('request.status'); return fail(request.status); } } else { console.log('continuing……'); } } // 发送请求: request.open('GET','http://api.money.126.net/data/Feed/0000001,1399001,1399006?callback=refreshPrice'); request.send(); alert('请求已发送,请等待响应...'); } </script> </head> <body> <div> <p id="test-response-text"></p> <p><button type="button" onclick="helloAjax()">刷新</button></p> </div> </body> </html>
1.2 同源策略@H_502_7@
说到跨域就不得不提“同源策略”。同源策略是Web浏览器针对恶意的代码所进行的措施,为了防止世界被破坏,为了保护世界的和平,Web浏览器,采取了同源策略,只允许脚本读取和所属文档来源相同的窗口和文档的属性。那么,怎么判断文档来源是否相同呢?很简单,看三个部分: 协议、主机、端口号。只要其中一个部分不同,则不同源。
1.3 跨域的应用场景@H_502_7@
-
来自 home.example.com
的文档里的脚本读取 developer.example.com
载入的文档的属性。
-
来自 home.example.com
的文档里的脚本读取 text.segmentfault.com
载入的文档的属性
2 JSONP跨域
来自 home.example.com
的文档里的脚本读取 developer.example.com
载入的文档的属性。
来自 home.example.com
的文档里的脚本读取 text.segmentfault.com
载入的文档的属性
实际应用场景中不可避免地需要进行跨域操作,这时候“同源策略”就显得过于苛刻。JSONP跨域GET请求是一个常用的解决方案。
2.1 概念介绍@H_502_7@
JSONP即JSON with Padding。由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源。如果要进行跨域请求, 我们可以通过使用html的script标记来进行跨域请求(即利用在页面中创建<script>节点的方法向不同域提交HTTP请求),并在响应中返回要执行的script代码,其中可以直接使用JSON传递javascript对象。 这种跨域的通讯方式称为JSONP。
假设在http://example1.com/index.php这个页面中向http://example2.com/getinfo.PHP提交GET请求,我们可以将下面的JavaScript代码放在http://example1.com/index.php这个页面中来实现:
当GET请求从http://example2.com/getinfo.PHP返回时,可以返回一段JavaScript代码,这段代码会自动执行,可以用来负责调用http://example1.com/index.php页面中的一个callback函数。
2.2 原理及实例@H_502_7@
JSONP的最基本的原理是:
动态添加一个<script>标签,而script标签的src属性是没有跨域的限制的。这样说来,这种跨域方式其实与ajax XmlHttpRequest协议无关了。
这样其实"jQuery AJAX跨域问题"就成了个伪命题,jquery $.ajax方法名有误导人之嫌。如果设为dataType: 'jsonp',这个$.ajax方法就和ajax XmlHttpRequest没什么关系了,取而代之的则是JSONP协议。JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问。
Jsonp的执行过程如下:
首先在客户端注册一个callback (如:'jsoncallback'),然后把callback的名字(如:jsonp1236827957501)传给服务器。注意:服务端得到callback的数值后,要用jsonp1236827957501(......)把将要输出的json内容包括起来,此时,服务器生成 json 数据才能被客户端正确接收。
然后以 javascript 语法的方式,生成一个function, function 名字就是传递上来的参数 'jsoncallback'的值 jsonp1236827957501 .
最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。
例1:
<html> <head> <Meta charset="utf-8" /> <script> function refreshPrice(data) { var p = document.getElementById('test-jsonp'); p.innerHTML = '当前价格:' + data['0000001'].name +': ' + data['0000001'].price + ';' + data['1399001'].name + ': ' + data['1399001'].price + ';' + data['1399006'].name + ': ' + data['1399006'].price; } function getPrice() { var js = document.createElement('script'),head = document.getElementsByTagName('head')[0]; js.src = 'http://api.money.126.net/data/Feed/0000001,1399006?callback=refreshPrice'; head.appendChild(js); } </script> </head> <body> <div> <p id="test-jsonp">当前价格:</p> <p><button type="button" onclick="getPrice()">刷新</button></p> </div> </body> </html>
通过参数callback定义回调函数名字为refreshPrice,服务端得到callback的数值为refreshPrice后,要用refreshPrice(......)把将要输出的json内容包括起来,此时,服务器生成 json 数据才能被客户端正确接收。
例2:
<html> <head> <Meta charset="utf-8" /> <title>Test Jsonp</title> <script type="text/javascript"> function jsonpCallback(sb) { alert(sb['0000001'].name); } </script> <script type="text/javascript" src="http://api.money.126.net/data/Feed/0000001,1399006?callback=jsonpCallback"></script> </head> <body> </body> </html>
例3:
<html> <head> <Meta charset="utf-8" /> <title>Untitled Page</title> <script type="text/javascript" src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script> <script type="text/javascript"> jQuery(document).ready(function(){ $.ajax({ type: "get",async: false,url: "http://api.money.126.net/data/Feed/0000001,1399006",dataType: "jsonp",//jsonp: "zjg",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback) //jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据 success: function(json){ alert(json['0000001'].name); },error: function(){ alert('fail'); } }); }); </script> </head> <body> </body> </html>
这里需重点说明下jquery中ajax的jsonp和jsonpCallback这两个参数的含义
3 JSONP优缺点@H_502_7@
JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。
JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
参考:
http://www.w3school.com.cn/jquery/ajax_ajax.asp
https://segmentfault.com/a/1190000006908944?utm_source=tuicool&utm_medium=referral
http://www.nowamagic.net/librarys/veda/detail/224/
http://kb.cnblogs.com/page/139725/