JSONP、CROS和postMessage跨域

浏览器出于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。

跨域的方式:

JSONP

html中script标签可以引入其他域下的js,比如引入线上的jquery库。利用这个特性,可实现跨域访问接口。需要后端支持。

JSONP看起来和JSON差不多,只不过被包含在函数中调用中的JSON,就像下面这样:

callback({"name":"Jack"});

以请求高德API天气接口为例,本地调用高德API数据接口,通过动态<script>元素来使用,为src属性指定一个跨域URL。<script>可以不受限制的从其他域加载资源。

var script = document.createElement('script');
script.src = "http://restapi.amap.com/v3/weather/weatherInfo?key=你的key&output=JSON&extensions=base&callback=showWeather&city="+$('#cityname').value;
document.head.appendChild(script);
//document.head.removeChild(script);
//可以在创建script元素后将它删除,避免head里script标签过多,删除标签不影响script获取地址中的资源

//我在url都设定了一个回调函数showWeather;在加载API接口时,返回的数据是这样的:
showWeather({"status":"1","count":"1","info":"OK","infocode":"10000","lives":[{"province":"上海","city":"上海市","adcode":"310000","weather":"晴","temperature":"33","winddirection":"西","windpower":"4","humidity":"59","reporttime":"2017-07-16 19:00:00"}]})
//返回的数据被包裹在showWeather中,因此我们就可以把showWeather当成一个函数,里面的内容即为传入函数的内容,只需要将返回的JSON数据解析成JavaScript对象就可以使用了

但是我在将返回数据解析成JavaScript对象时遇到了报错:

function showWeather(data){
    data = JSON.parse(data);
}
Uncaught SyntaxError: Unexpected token o in JSON at position 1
[object Object]

然后我直接打印出data,发现data已经是Object格式的了,并不需要再次解析。

console.log(data);
//{status: "1", count: "1", info: "OK", infocode: "10000", lives: Array(1)}

经查阅资料后发现,Object在作为JSON.parse的参数时会先转为string,默认toString实现会将Object转为"[object Object]"JSON.parse将第一个字符'['理解为数组开始,第二字符'o'就不知道怎么处理了。

如果不确定可以加入这行代码:

if (typeof data === 'string') {
    data = JSON.parse(data);
 }

因此只需要直接使用返回的data就可以了,不需要另外再对data进行解析。

另:jQuery中$.ajax()方法已经对jsonp进行了封装,它会直接在返回的数据上默认加一个函数头,在success:function()中可以直接使用,不需要进行另外的函数设置。

HTML页面代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>请输入需要查询的城市</h1>
<input type="text" id="cityname">
<button id="btn">查询</button>
<div class="today-weather">
    <h1>今天天气</h1>
    <div id="city">城市:</div>
    <div id="weather">天气:</div>
    <div id="temperature">温度:</div>
    <div id="winddirection">风向:</div>
    <div id="windpower">风力:</div>
    <div id="humidity">湿度:</div>
</div>
</body>
<script>
//设置选择器
    function $(id){
        return document.querySelector(id);
    }
//给btn设置监听事件
    $('#btn').addEventListener('click',function(){
        var script = document.createElement('script');
        script.src = "http://restapi.amap.com/v3/weather/weatherInfo?key=你的key&output=JSON&extensions=base&callback=showWeather&city="+$('#cityname').value;
        document.head.appendChild(script);
        document.head.removeChild(script);
    })
    function showWeather(data){
        var weather = data.lives[0];
             for(var prop in weather){
                if($('#'+prop)){
                    var span = document.createElement('span');
                    span.innerHTML = weather[prop];
                    $('#'+prop).appendChild(span);
                }
          }
    }

JSONP的不足之处:

  1. JSONP从其他域加载代码执行,如果其他域不安全,可能会在代码中夹杂恶意代码。
  2. 要确定JSONP请求是否失败并不容易。
  3. 只能支持GET方式获取数据,需要后端支持。

CORS跨域资源共享

CORS 全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 ajax 跨域请求资源的方式,支持现代浏览器,IE支持10以上。 实现方式很简单,当你使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin; 浏览器判断该相应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。所以 CORS 的表象是让你觉得它与同源的 ajax 请求没啥区别,代码完全一样。

以向高德API请求天气数据为例,在发送$.ajax()请求后,我没有使用JSONP的跨域方法,API一样会返回数据,进入响应后查看发现,在请求头中浏览器自动加入了请求头:

Origin:http://127.0.0.1

在响应头中可以看到:

Access-Control-Allow-Origin:*

服务器默认可以给任何请求返回响应,所有看起来代码和普通ajax代码完全相同,没有做跨域操作依然拿到了数据。CORS主要是服务器端的设置。

<script>
$(function(){
    $('#btn').click(function(){
        var span = $('span');
        var city = $('#cityname').val();
        if(span){
            span.remove();
        }
        if(city){
            searchWeather();
        }
    });
    function searchWeather(){
        $.ajax({
            type:'GET',
            url:"http://restapi.amap.com/v3/weather/weatherInfo?key=你的key&output=JSON&extensions=base&city="+$('#cityname').val(),
            success:function(data){
                for(var prop in data.lives[0]){
                    $('#'+prop).append('<span>'+data.lives[0][prop]+'</span>');
                }
            },
            error:function(jqXHR){
                console.log(jqXHR.status);
            }
        });
    }
});
</script>

postMessage

由于同源策略的限制,JavaScript 跨域的问题,一直是一个颇为棘手的问题。HTML5 提供了在网页文档之间互相接收与发送信息的功能。使用这个功能,只要获取到网页所在窗口对象的实例,不仅同源(域 + 端口号)的 Web 网页之间可以互相通信,甚至可以实现跨域通信。

要想接收从其他窗口发送来的信息,必须对窗口对象的 onmessage 事件进行监听,其它窗口可以通过 postMessage 方法来传递数据。

语法:
otherWindow.postMessage(message, targetOrigin);

otherWindow
其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。

该方法使用两个参数:第一个参数为所发送的消息文本,但也可以是任何 JavaScript 对象(通过 JSON 转换对象为文本),第二个参数为接收消息的对象窗口的 URL 地址,可以在 URL 地址字符串中使用通配符'*'指定全部地。

为了模拟本地跨域,我在hosts里添加了两个不同的域名:

127.0.0.1   a.com
127.0.0.1   b.com
127.0.0.1   c.com

在父页面里嵌入了一个子页面,调用postMessage方法向子页面发送数据。在子窗口监听onmessage事件,并显示到input框内。

父页面代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>PostMessage A</title>
</head>
<body>
  <h1>实现PostMessage实现跨域</h1>
  <div class="parent">
    <input type="text" placeholder="http://a.com:8080/a.html">
  </div>
  <iframe src="http://b.com:8080/b.html" frameborder="0"></iframe>
  <iframe src="http://c.com:8080/c.html" frameborder="0"></iframe>
</body>
<script>
  $('.parent input').addEventListener('input',function(){
    //给input输入框添加事件,当输入框内容发生变化时就进行监听,把输入框中的内容发送到b.html和c.html中
    
    console.log(this.value);
    window.frames[0].postMessage(this.value,'http://b.com:8080');
    window.frames[1].postMessage(this.value,'http://c.com:8080');
    
    //如果将http://b.com:8080改为*,则表示对所以地址发送信息
  })
  
  /*
  * frameList是一个frame对象的集合,它类似一个数组,有length属性且可以使用索引([i])来访问。

  * frameList === window 计算结果为true。
  
  * 在window.frames类数组中的每一项都代表了窗口对应给定对象的<frame>或<iframe>的内容,
  而不是(i)frame DOM元素(即window.frames[ 0 ]与document.getElementsByTagName( "iframe" )[ 0 ].contentWindow是相同的)。
*/

  function $(id){
    return document.querySelector(id);
  }
</script>
</html>

子页面代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>PostMessage B</title>
</head>
<body>
  <input type="text" placeholder="http://b.com:8080/b.html" id="input">
  <script>
    function $(id){
      return document.querySelector(id);
    }
    //接收信息时对window的message进行监听才可以收到信息
    window.addEventListener('message',function(e){
      $('#input').value = e.data;
    })
  </script>
</body>
</html>
postMessage跨域

在进行跨域的时候,设置好hosts后,启用本地服务器后,打开a.com时需要加上端口号!!
即在地址栏输入:a.com:8080/a.html

因为访问网页时都是要通过IP地址及端口号进行访问的,平时我们经常打开的网页绝大多数是超文本传输协议,即http,不要求用户输入网页“http://”的部分。同样,“80”是超文本传输协议文件的常用端口号,因此一般也不必写明。一般来说用户只要键入统一资源定位符的一部分,比如"www.baidu.com",此处的端口号是默认的80端口。

而我在本地启用的服务器需要访问8080端口,因此需要手动输入,如果不输入就无法正常访问。

不输入端口号情况
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 1. 跨域和同源 首先来看摘自MDN上对于跨域,较为标准的解释: 当一个资源从与该资源本身所在的服务器不同的域或端...
    晓风残月1994阅读 213评论 0 0
  • Section1、为什么要跨域? 自古以来(1995年起),为了用户的信息安全,浏览器就引入了同源策略。那么同源策...
    不去解释阅读 540评论 0 0
  • 跨域是什么 同源策略 在讲解什么是跨域之前先要清楚什么是同源策略,“同源政策”(same-origin polic...
    JRG_Orange阅读 951评论 0 52
  • 一、浏览器的同源策略 1.什么是同源? 所谓“同源”指的是”三个相同“。相同的域名、端口和协议,这三个相同的话就视...
    徐国军_plus阅读 833评论 1 3
  • 四七 感谢好哥们的慷慨 感谢给我的这次实践的机会,认识自己,接触陌生,感受童真。 感谢新认识的哥们,一起共事才感情...
    XBIN阅读 209评论 0 0