摘要:數據仍然是通過頁面獲得,頁面僅需向頁面傳遞即可,頁面拿到函數名,并通過傳遞從服務器拿到的數據,在頁面執行頁面的回調函數,回調函數執行完畢后移除頁面以及該函數。以上是我所知道的解決跨域請求的兩種方法。
iframe跨域的基本前提是,一個頁面可以嵌套非同源站點的html文件,以及某一個域名下的html頁面可以通過腳本向同域名服務器發出ajax請求。當一個域名為domain1下的頁面A想要向domain2發出ajax請求時,由于同源策略的限制無法直接請求到數據,但是可以在頁面A中動態添加一個display設置為none的iframe,該iframe的src為domain2下的html頁面B,由頁面B來發出ajax請求,因為頁面B是domain2下的所以可以成功發出請求。當B頁面拿到數據之后再傳遞給A頁面。
可以利用HTML5的postMessage來向A頁面傳遞數據。
將通過一個demo來具體說明如何操作,該demo涉及到兩個域名 http://localhost:3000 以及 http://127.0.0.1:3001。向 http://localhost:3000/a.html中嵌入 http://127.0.0.1:3001/b.html頁面,在 http://127.0.0.1:3001/b.html發出本域名下的ajax請求。
先舉一個簡單的例子。
a.html:
Title
b.html:
Title
a頁面中直接嵌入b頁面,b頁面加載后執行腳本,向同源服務器發出請求,拿到數據后發送給a頁面。
這里強調一下:
1.代碼中寫的是window.parent而非window
2.代碼中的第二個參數寫為"*",也可以指定精確的目標origin
當然實際開發中一個頁面要發出很多個請求,并且根據用戶操作不同可能要發出不一樣的請求,所以需要進一步的封裝。
a頁面通過b頁面發出請求,這需要將發送請求的url以及請求的類型和請求參數等信息傳遞到b頁面,可以直接通過嵌入的iframe的src來傳遞,然后b頁面根據location.search的值來獲取以上的信息。
我們的目標是構建一個形如iframeAjax(params,cb)的函數。
其中params中,包括了b頁面的url,ajax請求的最終目標url,ajax請求的種類,以及ajax請求的參數。
格式如下:
let {targetUrl, queryUrl, type, data = {}} = params
剛才已經提到過,a頁面通過設置iframe的src來向b頁面傳遞各種參數。
我們將type以及queryUrl合并到data中:
Object.assign(data, {type, queryUrl});
接下來就可以構建完整的iframe的src
let url = targetUrl + "?" + serialize(data); //雖然叫url但實際是iframe的src
然后我們封裝一個建立frame的方法。
function createIframe(url, cb) { let iframe = document.createElement("iframe"); iframe.style.display = "none"; iframe.src = url; document.body.appendChild(iframe); function handleIframe(e) { cb(e.data); document.body.removeChild(iframe); window.removeEventListener("message", handleIframe); } window.addEventListener("message", handleIframe); }
在這個方法中,我們創建了一個iframe設置其display為none使其不可見,設置了其src,并且把它添加到了頁面上。又給window添加了事件監聽,當收到b頁面傳來的數據之后,調用數據處理的函數cb并且傳入數據作為其參數。當cb執行完畢之后,移除iframe,并移除事件監聽。
b頁面中我們的任務是,拿到location.search的并正確的解析為一個對象,該對象包含了{type,queryUrl,data} 其中type是ajax的類型,queryUrl是ajax的目標url,data是請求的參數。
通過一個parseSearch的方法解析location.search。
function parseSearch() { let search = location.search.slice(1); let keyValuePairArr = search.split("&"); let obj = {}; keyValuePairArr.forEach(pair => { let [key, value] = pair.split("="); obj[decodeURIComponent(key)] = decodeURIComponent(value); }); return obj; }
我們還需要一個方法來移除其中的type和queryUrl,只剩下ajax請求的參數。
function getParams(obj) { delete obj.type; delete obj.queryUrl; return obj; }
然后我們就可以解析location.search得到信息對象,并且發出ajax請求來拿到服務器的數據了。
$.ajax({ type: obj.type, url: obj.queryUrl, data: getParams(obj) }).done(data => { window.parent.postMessage(data, "*"); });
再次鞏固一下整個流程,domain1下的a頁面向domain2發出動態ajax請求的中間過程:
在a頁面下封裝b頁面完整url,ajax請求的type,ajax請求的最終url,以及ajax請求的數據等信息。通過設置iframe的src將這些信息帶到b頁面,b頁面解析這些信息,最后向服務器發出ajax請求并將結果通過postMessage的API帶回給a頁面,a頁面收到數據后處理數據,處理完畢移除iframe,移除事件監聽。
除了利用postMessage API之外,還可以通過iframe + window.name來實現跨域ajax請求。
當a頁面中有一個b頁面的iframe時,兩個頁面共享window.name。在b頁面拿到數據后賦值給window.name。但是由于不使用postMessage API,數據無法傳輸給a頁面。我們可以將location.href轉為domain1(a頁面所在的域名)下的c頁面,由c頁面調用a頁面的回調方法。
先舉一個簡單的例子:
a頁面:
Title
b頁面:
Title
c頁面:
Title
很簡單的例子,a頁面中定義一個方法print,并嵌入一個frame,frame為b頁面,b頁面向服務器拿到數據后,賦值給window.name,然后再打開c頁面,在c頁面調用父頁面a中的print方法,并將拿到的數據作為參數傳入。
不知道為啥,ajax請求返回的對象不經處理直接賦值最終輸出結果是[object Object]。因此在這里先stringify之后再parse。
封裝之后的代碼如下:
aa頁面:
Title
bb頁面:
Title
c頁面:
Title
不同于使用postMessage,當通過window.name通信時,由于傳入的回調函數是在頁面c執行,因此需要把回調函數的函數名作為參數傳過來,并且也要把頁面c的url地址作為參數傳入。數據仍然是通過bb頁面獲得,bb頁面僅需向c頁面傳遞funcName即可,c頁面拿到函數名,并通過window.name傳遞從服務器拿到的數據,在c頁面執行aa頁面的回調函數,回調函數執行完畢后移除aa頁面iframe以及該函數。
注意:不能在bb頁面直接調用parent頁面aa的函數 由于aa與bb是非同源的,因此會報錯。必須由一個與aa頁面同源的頁面cc頁面,來執行在aa頁面中注冊的函數。
以上是我所知道的iframe解決跨域ajax請求的兩種方法。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93179.html
摘要:如何利用網頁請求暴露出來的接口去抓取網頁數據很多爬蟲都能實現這個功能。跨域通信時,瀏覽器會報如下錯誤其實這兩個問題都是由于跨域造成的。結果這些數據可以在請求成功會傳回本地。 如何利用網頁ajax請求暴露出來的接口去抓取網頁數據?很多爬蟲都能實現這個功能。不過今天要來和大家八一八單從前端的角度,利用js解決這個問題。 大家都知道,在不同域的情況下是不能發送ajax請求的,瀏覽器會報如下錯...
閱讀 553·2023-04-26 02:59
閱讀 691·2023-04-25 16:02
閱讀 2154·2021-08-05 09:55
閱讀 3543·2019-08-30 15:55
閱讀 4640·2019-08-30 15:44
閱讀 1797·2019-08-30 13:02
閱讀 2193·2019-08-29 16:57
閱讀 2288·2019-08-26 13:35