javascript|跨域解决方案JSONP

(转自jlin991的博客)

什么是跨域
老生常谈的问题了。下面列出一个表格:

URL 说明 是否跨域
http://www.a.com/a.js http://www.a.com/b.js 同一个域名
http://www.a.com/lab/a.js http://www.a.com/script/b.js 同一个域名,不同文件夹
http://www.a.com:8080/a.js http://www.a.com/b.js 同一个域名,不同端口
http://www.a.com/a.js http://79.23.1.21/b.js 域名与域名对应IP
http://www.a.com/a.js http://script.a.com/b.js 主域和子域
http://www.a.com/a.js http://a.com/b.js 一级域名和二级域名
http://www.XXX.com/a.js http://www.a.com/b.js 不同域名
http://localhost/a.js http://127.0.0.1/b.js 本地和本地对应的IP
牢记:只要协议schema ,域名,子域名,端口有一个不同的都是跨域!
而且跨域问题上,域仅仅是通过URL的首部来识别的,而不会去尝试判断两个域是否在同一个IP上(这涉及到DNS解析)
下面介绍几种能实现跨域的方法 图像Ping
JS高程提到的一个方法,利用的是img的src可以跨域。
var img= new Image(); img.onload=img.onerror=function(){ console.log('Done~!'); }; img.src='http://www.example.com/test?name=Nicolas';

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
这个方法唯一的能力就是可以知道是否接受到了响应,但是浏览器无法得到任何信息,但是可以请求服务器,比如可以请求服务器做一个删除的操作,或者创建的操作,因为querystring可以通过img.src提交给服务器。
仅仅是 浏览器 –> 服务器
1.只能发送get请求,并且无法获得任何服务器的响应。 2. 仅仅是单向的数据传递,浏览器-->服务器

  • 1
  • 2
JSONP
JSON with Padding
JSONP 两部分组成: 回调函数+数据
JSONP中的回调函数
响应到来的时候,应该在页面中调用的函数。
JSON就是数据
数据是传入回调函数中的JSON数据。
JSONP的原理
动态添加一个
  • 1
  • 2
  • 3
客户端解析script标签,并执行返回的js文档,此时js文档数据,作为参数传入到了客户端预先定义好的callback函数里。
可以说jsonp的方式原理上和是一致的(qq空间就是大量采用这种方式来实现跨域数据交换的)。JSONP是一种脚本注入(Script Injection)行为,所以有一定的安全隐患。
服务端代码可能是这样的
1,'b'=>2,'c'=>3,'d'=>4,'e'=>5); $result=json_encode($arr); //echo $_GET['callback'].'("Hello,World!")'; //echo $_GET['callback']."($result)"; //动态执行回调函数 $callback=$_GET['callback']; //得到回调函数 echo $callback."($result)"; //输出,result作为参数传递

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
服务端做的事
我们其实是要querystring,获取url中传递的参数,这里是callback,然后把callback的值’jsonpCallback’包裹要传递的json字符串。也就像这样:
jsonpCallback({“name”:”xxx”,”id”:23});
返回的就是用这个callback的值包裹的json数据。
然后这个回调函数就会被执行。因为你创建了script标签然后函数在里面被调用执行了。
jQuery的实现
原理是一样的,只不过我们不需要手动的插入script标签以及定义回掉函数。
jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。
$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。
jQuery源码部分:
//Build temporary JSONP function if( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) { jsonp = s.jsonpCallback || ("jsonp" + jsc++); // Replace the =? sequence both in the query string and the data if ( s.data ) { s.data = https://www.it610.com/article/(s.data +"").replace(jsre, "=" + jsonp + "$1"); } /*...*/ if ( s.cache === false && type === "GET" ) { var ts = now(); // try replacing _= if it is there var ret = s.url.replace(rts, "$1_=" + ts + "$2"); // if nothing was replaced, add timestamp to the end s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : ""); }// If data is available, append data to url for get requests if ( s.data && type === "GET" ) { s.url += (rquery.test(s.url) ? "&" : "?") + s.data; }// Watch for a new set of requests if ( s.global && ! jQuery.active++ ) { jQuery.event.trigger( "ajaxStart" ); }// Matches an absolute URL, and saves the domain var parts = rurl.exec( s.url ), remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host); // If we're requesting a remote document // and trying to load JSON or Script with a GET if ( s.dataType === "script" && type === "GET" && remote ) { var head = document.getElementsByTagName("head")[0] || document.documentElement; var script = document.createElement("script"); script.src = https://www.it610.com/article/s.url; // Handle Script loading if ( !jsonp ) { var done = false; // Attach handlers for all browsers script.onload = script.onreadystatechange = function() { if ( !done && (!this.readyState || this.readyState ==="loaded" || this.readyState === "complete") ) { done = true; success(); complete(); // Handle memory leak in IE script.onload = script.onreadystatechange = null; if ( head && script.parentNode ) { head.removeChild( script ); } } }; }// Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709 and #4378). head.insertBefore( script, head.firstChild ); // We handle everything using the script element injection return undefined; }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
jQuery一开始先判断JSON类型的调用,然后为本次调用创建了临时的JSONP方法,并且添加了一个随机的数字(源于日期值)
然后创建了script标签,构造这个script片段,并且追加到了原文档的head标签后面。
还有就是判断浏览器的脚本onreadystatechange事件,判断readyState是loaded或complete两个都要判断。
NOTE
因为无论是哪个属性都表示资源已经可用了。有时候readyState会停在“loaded”有时候会跳过”loaded”直接完成)
JSONP的优点
  1. JSON可读性好,在JS中容易处理
  2. 比XML轻了很多
  3. PHP对JSON的支持也不错
JSONP的弊端
【javascript|跨域解决方案JSONP】JSONP很好用,但是它有如下的缺点:
  1. JSONP是从其他域中加载代码执行的,如果其他域不安全(服务器端不安全),很有可能在响应中夹带一些恶意代码,此时除了完全放弃JSONP调用外没有办法。
  2. 其次,确定JSONP是否请求成功不容易。因为如果动态脚本插入有效那么就执行调用,如果无效就静默失败,失败没有任何提示~
  3. 用eval()解析也是容易出现安全问题

    推荐阅读