javascript跨域实践
上次我转载了一篇 【转】JavaScript最全的10种跨域共享的方法 ,仔细研读,终于对跨域有了一些些了解,做了几个demo加深印象.
1、jsonp 方式跨越:
我首先新建一个服务端页面: jsonp.php ,放在 lovefishss.sinaapp.com 下面,具体路径如下:http://lovefishss.sinaapp.com/cross-domain/jsonp.php,jsonp.php代码如下
$callback = $_GET['callback']; $key = $_GET['key']; $domain = 'lovefishss.sinaapp.com'; $data = '{"domain":"'. $domain .'", "key":"'. $key .'"}'; echo $callback . '('. $data .')';
我这里使用 $callback 变量保存通过 callback 传递过来的值(回调函数的名称),使用 $key 保存 key 值,然后构造 json 格式字符串,最后组合输出。
我再新建一个 jsonp.html 的请求发送页面,我把它放在 lovefishs.sinaapp.com 下面,具体路径:http://lovefishs.sinaapp.com/cross-domain/jsonp.html
这里我要着重说明一下,jsonp跨域方式是可以跨任何域的,不要因为我这里只是跨了子域,就被我误导了,如果有疑惑,可以看一下我转载的文章:【转】JavaScript最全的10种跨域共享的方法 。
jsonp.html 代码:
<!doctype html> <html> <head> <meta charset="utf-8" /> <title>jsonp 跨域</title> <meta name="keywords" content="" /> <meta name="description" content="" /> <style type="text/css"> *{margin: 0;padding: 0;} body {text-align: left;} button {padding: 5px 10px;} .demo, .callbackResult {margin: 10px 0;padding: 0 10px;} </style> </head> <body> <div class="demo"> <p><button type="button" onclick="jsonp('javascript');">click me(javascript)</button></p> </div> <div class="demo"> <p><button type="button" onclick="jsonp('jquery');">click me(jquery)</button></p> </div> <div id="J_DataView" class="callbackResult"></div> </body> <script type="text/javascript" src="../static/lib/jquery.1.7.2.min.js"></script> <script type="text/javascript"> function fRandomBy(under, over){ switch(arguments.length){ case 1: return parseInt(Math.random() * under + 1); case 2: return parseInt(Math.random() * (over-under+1) + under); default: return 0; } } function jsonp(type){ var key = fRandomBy(10000); var url = 'http://lovefishss.sinaapp.com/cross-domain/jsonp.php?callback=callback&key=' + key; switch(type){ case 'javascript': var jsonpScript =document.getElementById('J_JsonpScript'); if(jsonpScript){ jsonpScript.parentNode.removeChild(jsonpScript); } var script =document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.setAttribute('src', url); script.setAttribute('id', 'J_JsonpScript'); document.getElementsByTagName('head')[0].appendChild(script); break; case 'jquery': $.ajax({ type: 'get', url: url, data: {key: key}, dataType: 'jsonp', success: callback, error: function(){ } }); break; } } function callback(data){ var html = [ '<p>domain:'+ data.domain +'</p>', '<p>key:'+ data.key +'</p>' ]; var dataView = document.getElementById('J_DataView'); dataView.innerHTML = dataView.innerHTML + html.join(''); } </script> </html>
我这里写了2种方式,一种javascript,一种借助于jquery类库,javascript方式有助于对 jsonp 跨域方式更了解,大家可以好好的看一下源码
jsonp 跨域方式就是这么简单,我觉得它能够胜任大部分的跨域方式,当然,你需要服务端的配合,如果人家服务端不配合你,不论你怎样,都是没用的.
jsonp 比较适用于单向的数据请求,同时还有个缺点就是,信息传输量太少,因为它是get方式传递请求数据,不及post方式数据量大,如果遇上特殊的场景,可能就不适用了,不过不用担心,我下面还会提到其他几种跨域方式,也会有详细的代码和demo参考.
2、document.domain 方式跨域
前段时间,遇到个跨域问题,就是发送当前页面 html 内容到子域 f.domain.com 下的服务端页面处理(生成当前页的快照),但是当前有可能是子域 a.domain.com,或者是 b.domain.com,更或者是 c.domain.com 等等,开始想用 jsonp 方式,但是因为当前页 html 内容比较大,已经超过了 get 提交方式的最大数据量,所以行不通;后面我用 document.domain 跨域解决了这个问题,基本方法如下:
首先,我们在 lovefishss.sinaapp.com(这里使用这个子域作为服务端处理) 域下新建一个代理文件 proxy.html ,具体地址如下:http://lovefishs.sinaapp.com/cross-domain/document.domain.proxy.html ,代码内容如下:
<!doctype html> <html> <head> <meta charset="utf-8" /> <title>document.domain 跨域 - proxy</title> <meta name="keywords" content="" /> <meta name="description" content="" /> <style type="text/css"> *{margin: 0;padding: 0;} </style> </head> <body> </body> <script type="text/javascript" src="../static/lib/jquery.1.7.2.min.js"></script> <script type="text/javascript"> document.domain = 'sinaapp.com'; </script> </html>
代码很简单,也就做了 2 个事,一个引入 jquery(这个我们后面会用到),一个设置 document.domain 值。这个页面的主要作用就是供需要的页面插入iframe,iframe的src就是这个页面的地址。
然后,我们再在 lovefishss.sinaapp.com 下面新建一个php处理页面,处理接收到的数据,取名 document.domain.php ,具体地址:http://lovefishs.sinaapp.com/cross-domain/document.domain.proxy.php,代买如下:
$html = $_POST['html']; $html = addslashes($html);// addslashes(string) 转义 | StripSlashes(string) 去掉反斜杠 $key = $_POST['key']; $domain = 'lovefishss.sinaapp.com'; $data = '{"domain":"'. $domain .'", "key":"'. $key .'"}'; echo $data;
这个文件主要就是接收数据,返回json字符串。
然后,我们来创建请求的页面 document.domain.html ,该页面处于 lovefishs.sinaapp.com 域下(不是说只能处于这个域下,随便放在哪里都成,当然,需要与 proxy.html 是同一个主域),地址 http://lovefishs.sinaapp.com/cross-domain/document.domain.html 代码如下:
<!doctype html> <html> <head> <meta charset="utf-8" /> <title>document.domain 跨域</title> <meta name="keywords" content="" /> <meta name="description" content="" /> <style type="text/css"> *{margin: 0;padding: 0;} body {text-align: left;} button {padding: 5px 10px;} .demo, .callbackResult {margin: 10px 0;padding: 0 10px;} body .hide {display: none;} .proxyIframe {width: 0;height: 0;border: none;} </style> </head> <body> <div class="demo"> <p><button type="button" id="J_ClickBtn">click me(document.domain iframe)</button></p> </div> <div id="J_DataView" class="callbackResult"></div> <script type="text/javascript" src="../static/lib/jquery.1.7.2.min.js"></script> <script type="text/javascript"> $(document).ready(function(){ function fRandomBy(under, over){ switch(arguments.length){ case 1: return parseInt(Math.random() * under + 1); case 2: return parseInt(Math.random() * (over-under+1) + under); default: return 0; } } function documentDomain(){ // 大前提:当前域 与 目标域 主域相同,且互相设置了相同的 document.domain = 主域 document.domain = 'sinaapp.com'; var $this = $(this); var key = fRandomBy(10000); var ajaxUrl = 'http://lovefishss.sinaapp.com/cross-domain/document.domain.php', html = $('html').html().replace(new RegExp("<script(.|\n|\r|\t)*<\/script>", "ig"), ""); var iframeSrc = 'http://lovefishss.sinaapp.com/cross-domain/document.domain.proxy.html'; var $iframe = $('#J_ProxyIframe'), iframe$; var ajaxData = {html: html, key: key}; if(!$iframe.length){ $iframe = $('<iframe id="J_ProxyIframe" class="proxyIframe" src="'+ iframeSrc +'"></iframe>'); $iframe.on('load', function(){ console.log('iframe loaded'); //$(this)[0].contentWindow.document.domain = 'sinaapp.com';// 异想天开的从这里控制iframe的document对象的domain值,果然是不行的。。。 // 还是需要在 iframe 页面内设置 document.domain 值 // 也就是说想要通过 document.domain 方式跨域的话,必须所跨的域proxy.html文件是你能控制的,要不然你从何处设置proxy.html的document.domian呢? iframe$ = $(this)[0].contentWindow.$; proxyAjax(iframe$, ajaxData, ajaxUrl, $this); }); $('body').append($iframe); }else{ iframe$ = $('#J_ProxyIframe')[0].contentWindow.$; proxyAjax(iframe$, ajaxData, ajaxUrl, $this); } } function proxyAjax(i$, data, url, $btn){ $btn.attr('disabled', true); i$.ajax({ type: 'post', url: url, data: data, dataType: 'json', success: function(data){ $btn.attr('disabled', false); callback(data); }, error: function(){} }); } function callback(data){ var html = [ '<p>domain:'+ data.domain +'</p>', //'<p class="hide">html:'+ data.html +'</p>', '<p>key:'+ data.key +'</p>' ]; $('#J_DataView').html($('#J_DataView').html() + html.join('')); } // 绑定事件 $('#J_ClickBtn').on('click', documentDomain); }); </script> </body> </html>
有点长,但是有注释,仔细想想应该就能懂。这个页面主要是发送数据,发起请求,请求的地址是我们刚才创建好的 http://lovefishs.sinaapp.com/cross-domain/document.domain.proxy.php ,因为不在同一子域下,所以我们改变当前页的 document.domain = 'sinaapp.com'; ,然后创建一个 iframe ,src指向 proxy.html 页面,然后为这个 iframe 绑定 load 事件,在load 后取得iframe页面的 jquery 对象 $ ,我们要用 iframe 的 $ 来发送post请求,这样,我们就解决了跨域发送大数据的难题,同时我们还能接收到服务端返回的json数据,有用吧,哈哈。
(2012-11-16 未完,待续)