摘要:由于瀏覽器的同源策略保護機制,瀏覽器不能執行來自其他來源的腳本。然后想要得到所獲取到的數據,也就是想要得到的的值,還必須把這個的設成跟頁面同一個域才行,不然根據前面講的同源策略,是不能訪問到里的屬性的。
由于瀏覽器的同源策略保護機制,瀏覽器不能執行來自其他來源的腳本。通過 js 在不同的域之間進行數據傳輸或通信,比如用 ajax 向一個不同的域請求數據,或者通過 js 獲取頁面中不同域的框架中(iframe)的數據的操作就叫跨域。
所謂同源,就是指協議、域名(IP)、端口三者都相同。只要有其中一者不相同,都是跨域,無法進行數據的交流。例如:
注意的是,localhost 與 本機的 IP 地址也屬于跨域。
瀏覽器執行 javascript 腳本時,會檢查這個腳本屬于哪個頁面,如果不是同源頁面,就不會被執行。
解決方法主要有以下幾種:
方法一:設置 webpack 的服務器代理1、在工程化的前端項目中,在本機進行開發時,后臺的接口可能會出現跨域,可以設置 webpack 的服務器代理,將本機的請求轉發到與接口同源的地址。
首先安裝插件:webpack-dev-server,然后再 config 文件夾里找到 index.js 文件,在 dev 對象下的 proxyTable 字段添加對應的需要替換的地址。
module.exports = { ... devServer: { //本地開發服務器設置 ... port: 8080, //當前的端口號 proxyTable: { //服務器代理設置 "/v3/admin": { //需要代理的接口形式 target: "http://localhost:8700", //將當前端口號代理到8700 changeOrigin: true, secure: false } } } }
以上設置就是將所有包含 "/v3/admin" 字段的接口,從原來的 localhost: 8080 代理到 localhost: 8700 ,變成 "http://localhost: 8700/v3/admin" 從而獲取到 8700 端口的內容。
CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。
它允許瀏覽器向跨源服務器,發出 XMLHttpRequest 請求,從而克服了 AJAX 只能同源使用的限制。
CORS 標準新增了一組 HTTP 首部字段,允許服務器聲明哪些源站有權限訪問哪些資源。另外,規范要求,對那些可能對服務器數據產生副作用的 HTTP 請求方法(特別是 GET 以外的 HTTP 請求,或者搭配某些 MIME 類型的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求。服務器確認允許之后,才發起實際的 HTTP 請求。在預檢請求的返回中,服務器端也可以通知客戶端,是否需要攜帶身份憑證(包括 Cookies和 HTTP 認證相關數據)。
CORS需要瀏覽器和服務器同時支持,瀏覽器需要 ie8 以上。
瀏覽器端的寫法:
function CORSRequest(method,url,opation,callback) { var xhr = new XMLHttpRequest(); if ("withCredentials" in xhr) { // 此時即支持CORS的情況 // 檢查XMLHttpRequest對象是否有“withCredentials”屬性 // “withCredentials”僅存在于XMLHTTPRequest level 2對象里 } else { // 否則檢查是否支持XDomainRequest // XDomainRequest僅存在于IE中,是IE8 和 IE9 用于支持CORS請求的方式 xhr = new XDomainRequest(); } xhr.open(method, url, true); if(method=="POST"){ //可以設置不同請求方法的操作,所有的請求都可以分別設置 xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); //設置請求頭 xhr.send(opation); //傳輸數據到服務器 }else{ xhr.send(); } xhr.onload = function(){ callback(xhr.responseText); } }; function notice(data) { console.log(data) } CORSRequest("POST","http://example.com","傳遞給服務器的數據",notice);
服務器端的寫法:
Apache:
Apache需要使用 mod_headers 模塊來激活 HTTP 頭的設置,它默認是激活的。你只需要在 Apache 配置文件的, , 或的配置里加入以下內容即可:
Header set Access-Control-Allow-Origin *
PHP:
以上的配置的含義是允許任何域發起的請求都可以獲取當前服務器的數據。當然,這樣有很大的危險性,惡意站點可能通過XSS攻擊我們的服務器。所以我們應該盡量有針對性的對限制安全的來源,例如:
?CORS 的優點:
CORS與JSONP相比,無疑更為先進、方便和可靠。
JSONP 只能實現 GET 請求,而 CORS 支持所有類型的HTTP請求。
使用 CORS,開發者可以使用普通的 XMLHttpRequest 發起請求和獲得數據,擁有onerror 和 onabort 方法,比起 JSONP 有更好的錯誤處理。
JSONP 主要被老的瀏覽器支持,它們往往不支持 CORS,而絕大多數現代瀏覽器都已經支持了 CORS。
注意點:
當發送的請求是非簡單請求,瀏覽器會在正式通信之前,增加一次 HTTP 查詢請求,稱為"預檢"請求( preflight )。瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可以使用哪些 HTTP 動詞和頭信息字段。只有得到肯定答復,瀏覽器才會發出正式的 XMLHttpRequest 請求,否則就報錯。
一般前端框架比如"ExtJS"、"AngularJS", 框架監測到訪問的域名可能存在跨域的話會先發送一個 OPTIONS 請求,驗證是否可進行通信,如果返回可通信才會真正發起一個 POST、GET 請求。
下圖是框架發起的 OPTIONS 請求,當如果服務器的 Nginx 并沒有設置允許跨域請示的時候,它會返回一個 405 狀態碼。
axios 框架也會發送 OPTIONS 請求。
關于 CROS 的更多內容,可以進入以下鏈接了解:
https://developer.mozilla.org...
http://www.ruanyifeng.com/blo...
方法三:JSONP在 js 中,我們直接用 XMLHttpRequest 請求不同域上的數據時,是不可以的。但是,在頁面上引入不同域上的 js 腳本文件卻是可以的,jsonp 正是利用這個特性來實現的。
JSONP是一種非正式傳輸協議,該協議的一個要點就是允許用戶傳遞一個約定的參數(一般是 callback )給服務端,然后服務端返回數據時會將這個 callback 參數作為函數名來包裹住JSON數據,這樣客戶端就可以隨意定制自己的函數來自動處理返回數據了。
比如,有個 a.html 頁面,它里面的代碼需要利用 ajax 獲取一個不同域上的 json 數據,假設這個 json 數據地址是 http://example.com/data.php , 那么 a.html 中的代碼就可以這樣:
我們看到獲取數據的地址后面還有一個 callback 參數,按慣例是用這個參數名,但是你用其他的也一樣。當然如果獲取數據的jsonp地址頁面不是你自己能控制的,就得按照提供數據的那一方的規定格式來操作了。關鍵是參數值必須是當前頁面 script 設置的數據處理回調函數的名字。
因為是當做一個js文件來引入的,所以http://example.com/data.php 返回的必須是一個能執行的js文件,所以這個頁面的php代碼可能是這樣的:
最終那個頁面輸出的結果是:
所以通過 http://example.com/data.php?c... 得到的 js 文件,就是我們之前定義的 dosomething 函數,并且它的參數就是我們需要的 json 數據,這樣我們就跨域獲得了我們需要的數據。
這樣 jsonp 的原理就很清楚了,通過 script 標簽引入一個 js 文件,這個 js 文件載入成功后會執行我們在 url 參數中指定的函數,并且會把我們需要的 json 數據作為參數傳入。所以 jsonp 是需要服務器端的頁面進行相應的配合的。
知道 jsonp 跨域的原理后我們就可以用 js 動態生成 script 標簽來進行跨域操作了,而不用特意的手動的書寫那些 script 標簽。
一個跨域獲取淘寶關鍵字搜索建議的例子:
效果:
優點:
不受同源策略的限制;
兼容性更好,在更加古老的瀏覽器中都可以運行,不需要XMLHttpRequest或ActiveX的支持;
請求完畢后可以通過調用callback的方式回傳結果。
缺點:
只支持GET請求而不支持POST等其它類型的HTTP請求;
只支持跨域HTTP請求這種情況,不能解決不同域的兩個頁面之間如何進行JavaScript調用的問題。
方法四:設置代理服務器例如 www.123.com/index.html 需要調用 www.456.com/server.php,可以寫一個接口 www.123.com/server.php ,由這個接口在后端去調用 www.456.com/server.php 并拿到返回值,然后再返回給 index.html,這就是一個代理的模式。相當于繞過了瀏覽器端,自然就不存在跨域問題。
例如:
a.php 后臺獲取跨域的數據
?前端只要將 XMLHttpRequest 的請求地址設置為 "a.php" 就可以。
方法五:使用 window.namewindow 對象有個 name 屬性,該屬性有個特征:即在一個窗口( window )的生命周期內,窗口載入的所有的頁面都是共享一個 window.name 的,每個頁面對 window.name 都有讀寫的權限,window.name 是持久存在一個窗口載入過的所有頁面中的,并不會因新頁面的載入而進行重置。
比如:有一個頁面 a.html,它里面有這樣的代碼:
再看看 b.html 頁面的代碼:
a.html 頁面載入后 3 秒,跳轉到了 b.html 頁面,結果為:
我們看到在 b.html 頁面上成功獲取到了它的上一個頁面 a.html 給 window.name 設置的值。如果在之后所有載入的頁面都沒對 window.name 進行修改的話,那么所有這些頁面獲取到的 window.name 的值都是 a.html 頁面設置的那個值。當然,如果有需要,其中的任何一個頁面都可以對 window.name 的值進行修改。注意,window.name 的值只能是字符串的形式,這個字符串的大小最大能允許 2M 左右甚至更大的一個容量,具體取決于不同的瀏覽器,但一般是夠用了。
上面的例子中,我們用到的頁面 a.html 和 b.html 是處于同一個域的,但是即使 a.html 與 b.html 處于不同的域中,上述結論同樣是適用的,這也正是利用 window.name 進行跨域的原理。
此方法需要與 iframe 配合使用。
比如有一個 www.example.com/a.html 頁面,需要通過 a.html 頁面里的 js 來獲取另一個位于不同域上的頁面 www.cnblogs.com/data.html 里的數據。
data.html 頁面里的代碼很簡單,就是給當前的 window.name 設置一個 a.html 頁面想要得到的數據值。data.html 里的代碼:
那么在 a.html 頁面中,我們怎么把 data.html 頁面載入進來呢?顯然我們不能直接在 a.html 頁面中通過改變 window.location 來載入 data.html 頁面,因為我們想要即使 a.html 頁面不跳轉也能得到 data.html 里的數據。答案就是在 a.html 頁面中使用一個隱藏的 iframe 來充當一個中間人角色,由 iframe 去獲取 data.html 的數據,然后 a.html 再去得到iframe獲取到的數據。
充當中間人的 iframe 想要獲取到 data.html 的通過 window.name 設置的數據,只需要把這個 iframe 的 src 設為 www.cnblogs.com/data.html 就行了。然后 a.html 想要得到 iframe 所獲取到的數據,也就是想要得到 iframe的window.name 的值,還必須把這個 iframe 的 src 設成跟 a.html 頁面同一個域才行,不然根據前面講的同源策略,a.html 是不能訪問到 iframe 里的 window.name 屬性的。這就是整個跨域過程。
看下 a.html 頁面的代碼:
上面的代碼只是最簡單的原理演示代碼,你可以對使用 js 封裝上面的過程,比如動態的創建 iframe ,動態的注冊各種事件等等,當然為了安全,獲取完數據后,還可以銷毀作為代理的 iframe。
方法六:使用 window.postMessagewindow.postMessage(message,targetOrigin) 方法是 html5 新引進的特性,可以使用它來向其它的 window 對象發送消息,無論這個 window 對象是屬于同源或不同源,目前 IE8+、FireFox、Chrome、Opera 等瀏覽器都已經支持 window.postMessage 方法。
調用 postMessage 方法的 window 對象是指要接收消息的那一個 window 對象,該方法的第一個參數 message 為要發送的消息,類型只能為字符串;第二個參數 targetOrigin 用來限定接收消息的那個 window 對象所在的域,如果不想限定域,可以使用通配符 * 。
需要接收消息的 window對 象,可是通過監聽自身的 message 事件來獲取傳過來的消息,消息內容儲存在該事件對象的 data 屬性中。
上面所說的向其他 window 對象發送消息,其實就是指一個頁面有幾個框架的那種情況,因為每一個框架都有一個 window 對象。下面看一個簡單的示例,有兩個頁面
我們運行 a 頁面后得到的結果:
我們看到 b 頁面成功的收到了消息。
使用 postMessage 來跨域傳送數據還是比較直觀和方便的,但是缺點是 IE6、IE7 不支持,所以用不用還得根據實際需要來決定。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/54468.html
摘要:前言由于自己平時只做做,并沒有遇到太多跨域問題,今天通過幾個樣例模擬實現了幾種跨域方式。 前言 由于自己平時只做做demo,并沒有遇到太多跨域問題,今天通過幾個樣例模擬實現了幾種跨域方式。原文地址 傳送門 本文所有樣例靜態服務器基于nodejs實現,代碼親測可用。測試步驟如下: 1.為了實現跨域訪問的效果,需要下載http-server 作為一個服務器 npm install http...
摘要:前言騰訊一面,相比阿里一面來說,騰訊一面先給打電話預定時間,這也給了我們這些面試者去準備的時間。其實閉包也就是指有權訪問另一個函數作用域的函數而已。常用的創建閉包的方法就是在函數內部創建另一個函數。 前言 騰訊一面,相比阿里一面來說,騰訊一面先給打電話預定時間,這也給了我們這些面試者去準備的時間。但是也正是因為這種確定性,也有在等待電話的時候的心情的忐忑。 背景 我是一名大三學生,大一...
閱讀 2404·2021-10-14 09:43
閱讀 2435·2021-09-09 09:34
閱讀 1601·2019-08-30 12:57
閱讀 1198·2019-08-29 14:16
閱讀 718·2019-08-26 12:13
閱讀 3201·2019-08-26 11:45
閱讀 2282·2019-08-23 16:18
閱讀 2652·2019-08-23 15:27