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閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容

  • 1. 跨域和同源 首先來看摘自MDN上對于跨域,較為標準的解釋: 當一個資源從與該資源本身所在的服務器不同的域或端...
    曉風殘月1994閱讀 227評論 0 0
  • Section1、為什么要跨域? 自古以來(1995年起),為了用戶的信息安全,瀏覽器就引入了同源策略。那么同源策...
    不去解釋閱讀 570評論 0 0
  • 跨域是什么 同源策略 在講解什么是跨域之前先要清楚什么是同源策略,“同源政策”(same-origin polic...
    JRG_Orange閱讀 973評論 0 52
  • 一、瀏覽器的同源策略 1.什么是同源? 所謂“同源”指的是”三個相同“。相同的域名、端口和協議,這三個相同的話就視...
    徐國軍_plus閱讀 860評論 1 3
  • 四七 感謝好哥們的慷慨 感謝給我的這次實踐的機會,認識自己,接觸陌生,感受童真。 感謝新認識的哥們,一起共事才感情...
    XBIN閱讀 222評論 0 0