iframe跨域+

来源:http://www.cnblogs.com/giggle/archive/2016/06/06/5561443.html
-Advertisement-
Play Games

script、image、iframe的src都不受同源策略的影響。所以我們可以藉助這一特點,實現跨域。如前面所介紹的JSONP跨域,以及燈標(Beacons)。該篇隨筆主要闡述iframe結合一些技術,實現跨域請求。1、iframe+window.name;2、iframe+location.ha... ...


script、image、iframe的src都不受同源策略的影響。所以我們可以藉助這一特點,實現跨域。如前面所介紹的JSONP跨域,以及燈標(Beacons)。

該篇隨筆主要闡述iframe結合一些技術,實現跨域請求。

  1、iframe+window.name;

  2、iframe+location.hash;

  3、iframe+window.postMessage.

另,在最後賦予“燈標”技術闡述。

一、iframe + window.name實現跨域

window對象有個name屬性,該屬性有個牛逼的地方就是:在同一個視窗中,我不管你頁面怎麼變,我window.name的值是一直存在的,在同一個視窗任意讀寫,並且支持非常長的name值(2MB)。

有點含糊?

我們寫個demo看看。

假設我有個頁面a.html,當頁面載入完成後,我將window.name賦值’Monkey’,在3秒後跳轉到另一頁面b.html,併在這個b.html中alert一下window.name,看看結果如何。

a.html代碼如下:

<!DOCTYPE html>
    <head>
        <title>window.name</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            window.name = 'Monkey';
            setTimeout(function(){
                window.location = 'b.html';
            },3000);            
        </script>
    </body>
</html>

b.html代碼如下:

<!DOCTYPE html>
    <head>
        <title>window.name</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            alert(window.name);        
        </script>
    </body>
</html>

運行a.html代碼,等待3秒後,得如下結果:

 

看來,只要在同一個視窗,window.name就是可讀寫的,如,前一個頁面設置了這個屬性,後一個頁面就可以讀取它。

註:window.name傳輸技術,原本是用於解決cookie的一些劣勢(每個功能變數名稱4*20KB的限制、數據只能是字元串、設置和獲取cookie語法的複雜等等)而發明,後來才強化了window.name傳輸,用來解決跨域數據傳輸問題。

假設,當我在一個頁面a中想跨域訪問另一個不在同一域中b的數據時,怎麼利用window.name呢?

顯然,我們不能像上面那樣,將a頁面window.location重定向為b,這樣我豈不是當前頁面已經不再了。所以我們可以藉助於iframe標簽來達到這一目的(因為iframe的src不受同源策略影響,所以可以跨域訪問資源)。

思路:

(1)、在a.html中嵌入iframe,將所需要的文檔b.html載入進來,且b.html利用window.name傳入a.html想要獲取的數據;

(2)、iframe在得到b.html的內容後,必須將src變為a.html的同源域,因為同源策略是會阻止非同源的frame訪問name屬性值,最後a.html通過iframe.contentWindow.name,獲取b.html里window.name的值,從而實現跨域獲得數據。

demo如下:

<!DOCTYPE html>
    <head>
        <title>a.html</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            /*
                addIframeTag:動態創建iframe,通過src獲得相應文檔
                Param: src -->動態給創建iframe的src賦值
            */
            function addIframeTag(src){
                //在loadFn函數中,用於判斷iframe載入情況
                var state = 0;
                //創建iframe
                var iframe = document.createElement('iframe');
                //loadFn:跨域獲取數據,如b.html
                var loadFn = function(){
                    if (state === 1) {
                        //獲取window.name數據
                        var data = iframe.contentWindow.name;    
                        //相關操作,如alert獲取的數據
                        alert(data);
                        //清除動態創建的iframe
                        iframe.contentWindow.document.write('');
                        iframe.contentWindow.close();
                        document.body.removeChild(iframe);
                    } else if (state === 0) {
                        state = 1;
                        // 將src變為a.html的同源域
                        iframe.src = "a.html";    
                    }      
                };
                //給創建的iframe賦予指定的src值
                iframe.src = src;
                //當iframe載入完文件後,觸發onload事件
                if (iframe.attachEvent) {
                    iframe.attachEvent('onload', loadFn);
                } else {
                    iframe.onload  = loadFn;
                }
                //隱藏iframe
                iframe.style.display = 'none';
                //將創建的iframe加入body中
                document.body.appendChild(iframe);
            };
            window.onload = function(){
                addIframeTag('b.html');
            };
        </script>
    </body>
</html>
a.html
<!DOCTYPE html>
    <head>
        <title>b.html</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body> 
        <script>
            window.name = 'Monkey';
        </script>
    </body>
</html>
b.html
二、iframe + location.hash實現跨域

location.hash簡而言之就是錨點,如127.0.0.1#monkey中的#monkey就是錨點。借用大額的一個例子,具體感受下錨點:here

大家點擊了上面的例子,會發現點擊錨點後location.hash改變了,但卻沒有導致頁面刷新。

So,我們就可以藉助這一特性,實現數據傳遞。 但,因為我是藉助於location.hash來實現數據傳遞,所以缺點也很明顯:數據是直接暴露在URL中,且傳遞的數據容量有限。

那麼,我們怎麼通過location.hash來實現跨域訪問數據呢?

假設:a.html想跨域訪問b.html中的數據

思路:因為改變a.html的location.hash值不會刷新頁面,所以我們可以藉助iframe去訪問b.html(因為iframe的src不受同源策略影響,所以可以跨域訪問資源),然後在iframe中動態改變它父視窗a.html中的location.hash值。但是,個別瀏覽器不允許非同源的視窗修改parent.location.hash的值,所以我們還需要藉助一個代理,在iframe獲得b.html後,再在iframe里嵌套一個與a.html同源的html,如c.html,並通過c.html中的parent.parent.location.hash改變a.html中的location.hash值,從而達到跨域獲取數據。

 

拋磚引玉,我的具體實現如下:

註:由於我是在本地跑的程式,所以我將c.html 直接換成a.html,並加入錨點判斷,只是為了體驗一把iframe+location.hash的跨域。

<!DOCTYPE html>
    <head>
        <title>a.html</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            function startRequest(){
                //獲得a.html的錨點
                var _hash = location.hash ? location.hash.substring(1):'';
                //如果錨點是b.html中的代理ifrmae傳來的,則賦值,如somedata
                if(_hash === 'proxy'){
                    parent.parent.location.hash = 'somedata';
                    return;
                }
                else{
                    //創建一個iframe
                    var ifr = document.createElement('iframe');
                    ifr.style.display = 'none';
                    //想b.html獲取信息
                    ifr.src = 'b.html#paramdo';
                    document.body.appendChild(ifr);
                    //獲得b.html的數據
                    setInterval(function(){
                        try{
                            var data = location.hash ? location.hash.substring(1):'';
                            if(console.log){
                                console.log('Now data is '+ data);
                            }
                        }catch(e){
                            console.log(e);
                        }
                    },2000);
                }
            };
            startRequest();
        </script>
    </body>
</html>
a.html 
<!DOCTYPE html>
    <head>
        <title>b.html</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body> 
        <script>
            //如果發現傳過來的location.hash是#paramdo,做callBack操作
            if(location.hash === '#paramdo'){
                callBack();
            }
            function callBack(){
                try{
                    parent.location.hash = 'somedata';
                }catch(e){
                    // ie、chrome的安全機制無法修改parent.location.hash,
                    // 所以要利用一個代理iframe,src與a.html同源
                    var ifrproxy = document.createElement('iframe');
                    ifrproxy.style.display = 'none';
                    //這裡我指向a.html只是為了模擬同源
                    ifrproxy.src='a.html#proxy';
                    document.body.appendChild(ifrproxy);
                }
            }
        </script>
    </body>
</html>
b.html
三、iframe + window.postMessage實現跨域

HTML5引入了一個跨域文檔API(Cross-document messaging),這個API為window對象,新增了個window.postMessage方法,允許跨視窗通信,且不必同源。

語法如下:

otherwindow.postMessage(message, targetOrigin);

  (1)、otherwindow:對接收信息頁面的window引用,可以是頁面中iframe的contentWindow屬性;window.open返回值;通過name或下標從window.frames取到的值。

  (2)、message:要發送的數據,string類型。

  (3)、targetOrigin:用於接收消息的視窗的源(origin),即“協議 + 功能變數名稱 + 埠”。也可以設為*,表示不限制功能變數名稱,向所有視窗發送。

postMessage是向視窗發送信息,當然相應視窗得接收信息咯。

怎麼接收信息呢?

通過message事件,監聽對方的信息。一旦有postMessage傳送過來的信息就可以做相應處理了。

如下:

Window.addEventListener(‘message’,function(e){console.log(e.data);},false);

且message中的e(即event對象),通過三屬性:

  (1)、event.source:發送消息的視窗對象;

  (2)、event.origin:發送消息視窗的源(協議+主機+埠號);

  (3)、event.data:發送的消息內容。

拋磚迎玉Demo(a.html與b.html通信)如下:

<!DOCTYPE html>
    <head>
        <title>a.html</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <iframe id="ifr" src='b.html'></iframe>
        <script>
            window.onload = function(){
                var ifr = document.getElementById('ifr');
                ifr.contentWindow.postMessage('I was there','/');
            };
        </script>
    </body>
</html>
a.html
<!DOCTYPE html>
    <head>
        <title>b.html</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body> 
        <script>
            window.addEventListener('message', function(event){
                console.log('origin: '+event.origin);
                console.log('data: '+event.data);
                console.log('source'+event.source);
            });
        </script>
    </body>
</html>
b.html

運行上述a.html代碼,得下:

四、拓展--燈標

此技術與動態iframe標簽插入非常類似,用JavaScript創建一個新的Image對象,將src設置為伺服器上一個腳本文件的URL。此URL包含我們打算通過GET格式傳回的鍵值對數據。

註意並沒有創建img元素或者將它們插入到DOM中。

如下:

var url = '/status_tracker.php';
var params = [
    'step = 2',
    'time = 1465141496244'
];
(new Image()).src = url + '?' + params.join('&');

伺服器取得此數據並保存下來,而不必向客戶端返回什麼,因此沒有實際的圖像顯示。這是將信息發回伺服器最有效的方法。其開銷很小,而且任何伺服器端錯誤都不會影響客戶端。

簡單的圖像燈標意味著你所能做的受到限制。你不能發送POST數據,所以你被URL長度限制在一個相當小的字元數量上。你可以用非常有限的方法接收返回數據。可以監聽Image對象的load事件,它可以告訴你伺服器端是否成功接收了數據,你還可以檢查伺服器返回圖片的寬度和高度(如果返回了一張圖片)並用這些數字通知你伺服器的狀態。例如,寬度為1表示‘成功’,2表示‘重試’。

如果你不需要為此響應返回數據,那麼你應當發送一個204 No Content響應代碼,無消息正文。它將阻止客服端繼續等待永遠不會到來的消息體:

var url = '/status_tracker.php';
var params = [
    'step = 2',
    'time = 1465141496244'
];
var beacon = new Image();
beacon.src = url + '?' + params.join('&');
beacon.onload = function(){
    if(this.width == 1){
        //Success
    }else if(this.width == 2){
        //Failure;create another beacon and try again.    
    }
}
beacon.onerror = function(){
    //Error;wait a bit, then create another beacon and try again.
}

燈標是向伺服器回送數據最快和最有效的方法。伺服器根本不需要發回任何響應正文,所以你不必擔心客戶端下載數據。唯一的缺點是接收到的響應類型是受限的。如果你需要向客戶端返回大量數據,那麼使用XHR。如果你只關心將數據發送到服務端(可能需要極少的回覆),那麼使用圖像燈標。

五、參考文獻

[1]、大額

[2]、無雙

[3]、RainMan

[4]、MDN

[5]、阮一峰


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • DCI in C++ 本文講解的C++的DCI編程框架,目前作為 "ccinfra" 的一個組件提供,可訪問 "https://github.com/MagicBowen/ccinfra" 獲取具體源碼。ccinfra中的DCI框架原創者是袁英傑先生(Thoughtworks),我們在兩個大型電信系 ...
  • × 目錄 [1]原始表達式 [2]複雜表達式 前面的話 一般,關於javascript基礎語法,人們聽得比較多的術語是操作符和語句。但,其實還有一個術語經常使用,卻很少被提到,這就是javascript表達式(expression)。本文將詳細介紹javascript表達式,表達式分為原始表達式和復 ...
  • javascript之數組 學習要點: 數組的介紹 定義數組 數組元素 數組的方法 一、數組的介紹 數組中的元素類型可以是數字型、字元串型、布爾型等,甚至也可以是一個數組。 二、定義數組 1、通過數組的構造函數來定義數組: var arr=new Array(); var arr=new Array ...
  • 先說一下我遇到的問題吧,我之前的一個函數想調用上一個函數的返回值,但是它的返回值一直為空,後來翻了一些資料才明白是非同步請求在作怪,不多說,看例子,這是我之前有返回值函數的代碼: Java代碼 function get_no_order_array() { var order_info = show_ ...
  • JavaScript 函數 方法&函數 區別 1. function 是更通用的概念,如數學、編程 2. method 是面向對象中的概念,一般與類或對象成對出現 關係 1. 對象的屬性可以是任意類型 2. 對象的屬性如果是函數類型,它就叫做這個對象的方法 3. 所以方法的本質還是函數 函數的調用 ...
  • 先說邏輯與(&&),它可以從三個層次進行理解 第一個層次最簡單,就是簡單的布爾值之間的邏輯與,就是左值和右值都是true時,返回true,兩邊都是false或者兩邊的值其中一邊是fasle,就返回false;(AND操作); 第二個層次,(false,null,indefined,0,-0,NaN和 ...
  • 剛從南方回來就分了一個刮刮卡效果的頁面,特麽的我在煩惱怎麼用H5去實現這個效果呢,好不容易寫出來了,產品居然說:“既然你可以寫出來這個效果那當然好了,開始我只是打算讓你實現點擊就出現呢!”… … 尼瑪幹嘛不早說呢?????真是自找麻煩。既然寫了就分享給大家吧,這個效果其實很早就有了,只是我們不常用到 ...
  • 通過一條命令用Npm安裝gulp-htmlmin: 安裝完畢後,打開gulpfile.js文件,我們裡面編寫一個task用來專門壓縮html,並對html進行一系列的處理: 我們看到task裡面有個設置選項,分別介紹一下他們的屬性的作用: 1.collapseWhitespace:從字面意思應該可以 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...