JSONProxy – 获取跨域json数据工具

图片 1

JSONProxy – 获取跨域json数据工具

2015/07/10 · JavaScript
· JSON,
JSONProxy

原文出处: 韩子迟   

JSONProxy是一款很好的获取json数据的代理网站,“Enables
cross-domain requests to any JSON
API”。当你苦于无法跨域获取json数据时,不妨一试,说不定能事半功倍。

比如这位朋友,想通过ajax获取必应的每日一图的url(是否可以通过ajax获取“Bing每日一图”?)很显然,这个ajax是跨域的,直接获取会因为跨域报错;服务端也肯定不会有对你本地localhost的“Access-Control-Allow-Origin”的设置,所以CORS策略也是不行的;因为是个json数据,没有方法名包裹,所以jsonp也是不行。楼主暂时还没接触过其他的跨域方法,如果要我去获取url,只能通过服务端的代码,服务端去获取json数据,然后index页面去ajax请求服务端获取的json数据(此时index页面和服务端同源),代码量增加,而要做的仅仅只是获取一个json数据啊!这时JSONProxy就帮你做好了服务端的工作,是不是很爽!

别慌,不就是跨域么!

2017/12/27 · 基础技术 ·
跨域

原文出处:
Neal_yang   

前端开发中,跨域使我们经常遇到的一个问题,也是面试中经常被问到的一些问题,所以,这里,我们做个总结。小小问题,不足担心

跨域:掘金地址——

Easy: JSONP

原生的JavaScript:

XHTML

<script> function myCallback(data){ console.log(data); }
</script> <script
src=”;

1
2
3
4
5
6
<script>
  function myCallback(data){
    console.log(data);
  }
</script>
<script src="https://jsonp.afeld.me/?callback=myCallback&url=http://jsonview.com/example.json"></script>

myCallback函数里的data就是返回的json数据了。很显然,服务器会帮你去请求你需要的json数据,然后包裹在你设置的回调函数名中,这时要注意的代码中的红色两处要保持一致,url后跟的就是需要的json数据地址。

当然JQuery封装好的方法更加简单:

XHTML

<script>
$.getJSON(”,
function(data){ console.log(data); }); </script>

1
2
3
4
5
<script>
  $.getJSON(‘https://jsonp.afeld.me/?callback=?&url=http://jsonview.com/example.json’, function(data){
    console.log(data);
  });
</script>

什么是跨域

跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。

同源策略限制了一下行为:

Cookie、LocalStorage 和 IndexDB 无法读取
DOM 和 JS 对象无法获取
Ajax请求发送不出去

另一篇学习地址:

Easier: Cross-domain AJAX (CORS)

比jsonp更简单的方法是CORS(好吧,也没简单到哪去啊…)

XHTML

<script>
$.get(”,
function(data){ console.log(data); }); </script>

1
2
3
4
5
<script>
  $.get(‘https://jsonp.afeld.me/?url=http://jsonview.com/example.json’, function(data){
    console.log(data);
  });
</script>

这回是真正地发送了ajax请求了,为什么跨域了还能请求?因为服务端设置好了。

图片 1

而请求的json数据也是服务端帮你获取的。也就是说,客户端发送请求,服务端解析请求的url,然后服务器作为代理发送http请求去请求json数据(这时不存在客户端跨域),再返回给客户端作为回调的参数。

常见的跨域场景

所谓的同源是指,域名、协议、端口均为相同。

JavaScript

调用
非跨域 调用
跨域,主域不同
调用
跨域,子域名不同 调用
跨域,端口不同
调用
跨域,协议不同 localhost 调用 127.0.0.1
跨域

1
2
3
4
5
6
7
8
9
10
11
http://www.nealyang.cn/index.html 调用   http://www.nealyang.cn/server.php  非跨域
 
http://www.nealyang.cn/index.html 调用   http://www.neal.cn/server.php  跨域,主域不同
 
http://abc.nealyang.cn/index.html 调用   http://def.neal.cn/server.php  跨域,子域名不同
 
http://www.nealyang.cn:8080/index.html 调用   http://www.nealyang.cn/server.php  跨域,端口不同
 
https://www.nealyang.cn/index.html 调用   http://www.nealyang.cn/server.php  跨域,协议不同
 
localhost   调用 127.0.0.1 跨域

跨域的解决办法
jsonp跨域

jsonp跨域其实也是JavaScript设计模式中的一种代理模式。在html页面中通过相应的标签从不同域名下加载静态资源文件是被浏览器允许的,所以我们可以通过这个“犯罪漏洞”来进行跨域。一般,我们可以动态的创建script标签,再去请求一个带参网址来实现跨域通信

JavaScript

//原生的实现方式 let script = document.createElement(‘script’);
script.src =
”;
document.body.appendChild(script); function callback(res) {
console.log(res); }

1
2
3
4
5
6
7
8
9
10
//原生的实现方式
let script = document.createElement(‘script’);
 
script.src = ‘http://www.nealyang.cn/login?username=Nealyang&callback=callback’;
 
document.body.appendChild(script);
 
function callback(res) {
  console.log(res);
}

当然,jquery也支持jsonp的实现方式

JavaScript

$.ajax({ url:”, type:’GET’,
dataType:’jsonp’,//请求方式为jsonp jsonpCallback:’callback’, data:{
“username”:”Nealyang” } })

1
2
3
4
5
6
7
8
9
$.ajax({
    url:’http://www.nealyang.cn/login’,
    type:’GET’,
    dataType:’jsonp’,//请求方式为jsonp
    jsonpCallback:’callback’,
    data:{
        "username":"Nealyang"
    }
})

虽然这种方式非常好用,但是一个最大的缺陷是,只能够实现get请求
document.domain + iframe 跨域

这种跨域的方式最主要的是要求主域名相同。什么是主域名相同呢?
www.nealyang.cn aaa.nealyang.cn ba.ad.nealyang.cn
这三个主域名都是nealyang.cn,而主域名不同的就不能用此方法。

假设目前a.nealyang.cn 和 b.nealyang.cn 分别对应指向不同ip的服务器。

a.nealyang.cn 下有一个test.html文件

JavaScript

<!DOCTYPE html> <html lang=”en”> <head> <meta
charset=”UTF-8″> <title>html</title> <script
type=”text/javascript” src = “jquery-1.12.1.js”></script>
</head> <body> <div>A页面</div> <iframe style
= “display : none” name = “iframe1” id = “iframe”
src=”” frameborder=”0″></iframe>
<script type=”text/javascript”> $(function(){ try{ document.domain
= “nealyang.cn” }catch(e){} $(“#iframe”).load(function(){ var jq =
document.getElementById(‘iframe’).contentWindow.$
jq.get(” console.log(data);
}); }) }) </script> </body> </html>

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
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>html</title>
    <script type="text/javascript" src = "jquery-1.12.1.js"></script>
</head>
<body>
    <div>A页面</div>
    <iframe
    style = "display : none"
    name = "iframe1"
    id = "iframe"
    src="http://b.nealyang.cn/1.html" frameborder="0"></iframe>
    <script type="text/javascript">
        $(function(){
            try{
                document.domain = "nealyang.cn"
            }catch(e){}
            $("#iframe").load(function(){
                var jq = document.getElementById(‘iframe’).contentWindow.$
                jq.get("http://nealyang.cn/test.json",function(data){
                    console.log(data);
                });
            })
        })
    </script>
</body>
</html>

利用 iframe 加载 其他域下的文件(nealyang.cn/1.html), 同时
document.domain 设置成 nealyang.cn ,当 iframe 加载完毕后就可以获取
nealyang.cn 域下的全局对象, 此时尝试着去请求 nealyang.cn 域名下的
test.json (此时可以请求接口),就会发现数据请求失败了~~
惊不惊喜,意不意外!!!!!!!

数据请求失败,目的没有达到,自然是还少一步:

JavaScript

<!DOCTYPE html> <html lang=”en”> <head> <meta
charset=”UTF-8″> <title>html</title> <script
type=”text/javascript” src = “jquery-1.12.1.js”></script>
<script type=”text/javascript”> $(function(){ try{ document.domain
= “nealyang.com” }catch(e){} }) </script> </head>
<body> <div id = “div1”>B页面</div> </body>
</html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>html</title>
    <script type="text/javascript" src = "jquery-1.12.1.js"></script>
    <script type="text/javascript">
        $(function(){
            try{
                document.domain = "nealyang.com"
            }catch(e){}
        })
    </script>
</head>
<body>
    <div id = "div1">B页面</div>
</body>
</html>

此时在进行刷新浏览器,就会发现数据这次真的是成功了~
window.name + iframe 跨域

window.name属性可设置或者返回存放窗口名称的一个字符串。他的神器之处在于name值在不同页面或者不同域下加载后依旧存在,没有修改就不会发生变化,并且可以存储非常长的name(2MB)

假设index页面请求远端服务器上的数据,我们在该页面下创建iframe标签,该iframe的src指向服务器文件的地址(iframe标签src可以跨域),服务器文件里设置好window.name的值,然后再在index.html里面读取改iframe中的window.name的值。完美~

JavaScript

<body> <script type=”text/javascript”> iframe =
document.createElement(‘iframe’), iframe.src =
”; document.body.appendChild(iframe);
iframe.onload = function() { console.log(iframe.contentWindow.name) };
</script> </body>

1
2
3
4
5
6
7
8
9
10
<body>
  <script type="text/javascript">
    iframe = document.createElement(‘iframe’),
    iframe.src = ‘http://localhost:8080/data.php’;
    document.body.appendChild(iframe);
    iframe.onload = function() {
      console.log(iframe.contentWindow.name)
    };
  </script>
</body>

当然,这样还是不够的。

因为规定如果index.html页面和和该页面里的iframe框架的src如果不同源,则也无法操作框架里的任何东西,所以就取不到iframe框架的name值了,告诉你我们不是一家的,你也休想得到我这里的数据。
既然要同源,那就换个src去指,前面说了无论怎样加载window.name值都不会变化,于是我们在index.html相同目录下,新建了个proxy.html的空页面,修改代码如下:

 

JavaScript

<body> <script type=”text/javascript”> iframe =
document.createElement(‘iframe’), iframe.src =
”; document.body.appendChild(iframe);
iframe.onload = function() { iframe.src =
”;
console.log(iframe.contentWindow.name) }; </script> </body>

1
2
3
4
5
6
7
8
9
10
11
<body>
  <script type="text/javascript">
    iframe = document.createElement(‘iframe’),
    iframe.src = ‘http://localhost:8080/data.php’;
    document.body.appendChild(iframe);
    iframe.onload = function() {
      iframe.src = ‘http://localhost:81/cross-domain/proxy.html’;
      console.log(iframe.contentWindow.name)
    };
  </script>
</body>

理想似乎很美好,在iframe载入过程中,迅速重置iframe.src的指向,使之与index.html同源,那么index页面就能去获取它的name值了!但是现实是残酷的,iframe在现实中的表现是一直不停地刷新,
也很好理解,每次触发onload时间后,重置src,相当于重新载入页面,又触发onload事件,于是就不停地刷新了(但是需要的数据还是能输出的)。修改后代码如下:

JavaScript

<body> <script type=”text/javascript”> iframe =
document.createElement(‘iframe’); iframe.style.display = ‘none’; var
state = 0; iframe.onload = function() { if(state === 1) { var data =
JSON.parse(iframe.contentWindow.name); console.log(data);
iframe.contentWindow.document.write(”); iframe.contentWindow.close();
document.body.removeChild(iframe); } else if(state === 0) { state = 1;
iframe.contentWindow.location =
”; } }; iframe.src =
”; document.body.appendChild(iframe);
</script> </body>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
  <script type="text/javascript">
    iframe = document.createElement(‘iframe’);
    iframe.style.display = ‘none’;
    var state = 0;
    
    iframe.onload = function() {
      if(state === 1) {
          var data = JSON.parse(iframe.contentWindow.name);
          console.log(data);
          iframe.contentWindow.document.write(”);
          iframe.contentWindow.close();
        document.body.removeChild(iframe);
      } else if(state === 0) {
          state = 1;
          iframe.contentWindow.location = ‘http://localhost:81/cross-domain/proxy.html’;
      }
    };
 
    iframe.src = ‘http://localhost:8080/data.php’;
    document.body.appendChild(iframe);
  </script>
</body>

所以如上,我们就拿到了服务器返回的数据,但是有几个条件是必不可少的:

iframe标签的跨域能力
window.names属性值在文档刷新后依然存在的能力

location.hash + iframe 跨域

此跨域方法和上面介绍的比较类似,一样是动态插入一个iframe然后设置其src为服务端地址,而服务端同样输出一端js代码,也同时通过与子窗口之间的通信来完成数据的传输。

关于锚点相信大家都已经知道了,其实就是设置锚点,让文档指定的相应的位置。锚点的设置用a标签,然后href指向要跳转到的id,当然,前提是你得有个滚动条,不然也不好滚动嘛是吧。

而location.hash其实就是url的锚点。比如

基础知识补充完毕,下面我们来说下如何实现跨域

如果index页面要获取远端服务器的数据,动态的插入一个iframe,将iframe的src执行服务器的地址,这时候的top
window
和包裹这个iframe的子窗口是不能通信的,因为同源策略,所以改变子窗口的路径就可以了,将数据当做改变后的路径的hash值加载路径上,然后就可以通信了。将数据加在index页面地址的hash上,
index页面监听hash的变化,h5的hashchange方法

JavaScript

<body> <script type=”text/javascript”> function getData(url,
fn) { var iframe = document.createElement(‘iframe’);
iframe.style.display = ‘none’; iframe.src = url; iframe.onload =
function() { fn(iframe.contentWindow.location.hash.substring(1));
window.location.hash = ”; document.body.removeChild(iframe); };
document.body.appendChild(iframe); } // get data from server var url =
”; getData(url, function(data) { var
jsondata = JSON.parse(data); console.log(jsondata.name + ‘ ‘ +
jsondata.age); }); </script> </body>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<body>
  <script type="text/javascript">
    function getData(url, fn) {
      var iframe = document.createElement(‘iframe’);
      iframe.style.display = ‘none’;
      iframe.src = url;
 
      iframe.onload = function() {
        fn(iframe.contentWindow.location.hash.substring(1));
        window.location.hash = ”;
        document.body.removeChild(iframe);
      };
 
      document.body.appendChild(iframe);
    }
 
    // get data from server
    var url = ‘http://localhost:8080/data.php’;
    getData(url, function(data) {
      var jsondata = JSON.parse(data);
      console.log(jsondata.name + ‘ ‘ + jsondata.age);
    });
  </script>
</body>

补充说明:其实location.hash和window.name都是差不多的,都是利用全局对象属性的方法,然后这两种方法和jsonp也是一样的,就是只能够实现get请求

postMessage跨域

这是由H5提出来的一个炫酷的API,IE8+,chrome,ff都已经支持实现了这个功能。这个功能也是非常的简单,其中包括接受信息的Message时间,和发送信息的postMessage方法。

发送信息的postMessage方法是向外界窗口发送信息

JavaScript

otherWindow.postMessage(message,targetOrigin);

1
otherWindow.postMessage(message,targetOrigin);

otherWindow指的是目标窗口,也就是要给哪一个window发送消息,是window.frames属性的成员或者是window.open方法创建的窗口。
Message是要发送的消息,类型为String,Object(IE8、9不支持Obj),targetOrigin是限定消息接受范围,不限制就用星号
*

接受信息的message事件

JavaScript

var onmessage = function(event) { var data = event.data; var origin =
event.origin; } if(typeof window.addEventListener != ‘undefined’){
window.addEventListener(‘message’,onmessage,false); }else if(typeof
window.attachEvent != ‘undefined’){ window.attachEvent(‘onmessage’,
onmessage); }

1
2
3
4
5
6
7
8
9
10
var onmessage = function(event) {
  var data = event.data;
  var origin = event.origin;
}
 
if(typeof window.addEventListener != ‘undefined’){
    window.addEventListener(‘message’,onmessage,false);
}else if(typeof window.attachEvent != ‘undefined’){
    window.attachEvent(‘onmessage’, onmessage);
}

举个栗子

a.html()

JavaScript

<iframe id=”iframe” src=””
style=”display:none;”></iframe> <script> var iframe =
document.getElementById(‘iframe’); iframe.onload = function() { var data
= { name: ‘aym’ }; // 向neal传送跨域数据
iframe.contentWindow.postMessage(JSON.stringify(data),
”); }; // 接受domain2返回数据
window.addEventListener(‘message’, function(e) { alert(‘data from neal
—> ‘ + e.data); }, false); </script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<iframe id="iframe" src="http://www.neal.cn/b.html" style="display:none;"></iframe>
<script>      
    var iframe = document.getElementById(‘iframe’);
    iframe.onload = function() {
        var data = {
            name: ‘aym’
        };
        // 向neal传送跨域数据
        iframe.contentWindow.postMessage(JSON.stringify(data), ‘http://www.neal.cn’);
    };
 
    // 接受domain2返回数据
    window.addEventListener(‘message’, function(e) {
        alert(‘data from neal —> ‘ + e.data);
    }, false);
</script>

b.html()

JavaScript

<script> // 接收domain1的数据 window.addEventListener(‘message’,
function(e) { alert(‘data from nealyang —> ‘ + e.data); var data =
JSON.parse(e.data); if (data) { data.number = 16; //
处理后再发回nealyang window.parent.postMessage(JSON.stringify(data),
”); } }, false); </script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
    // 接收domain1的数据
    window.addEventListener(‘message’, function(e) {
        alert(‘data from nealyang —> ‘ + e.data);
 
        var data = JSON.parse(e.data);
        if (data) {
            data.number = 16;
 
            // 处理后再发回nealyang
            window.parent.postMessage(JSON.stringify(data), ‘http://www.nealyang.cn’);
        }
    }, false);
</script>

讲的比较细:
跨域笔记
    有些东西不要太较真,因为没啥用

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图