跨域 && 解決方法
為什麼會出現跨域問題 —— 同源策略
跨域限制僅僅是瀏覽器的行為,伺服器沒有跨域限制。
同時滿足以下三個條件才有可能發生跨域問題:
-
瀏覽器限制
-
跨域
-
XMLHttpRequest 請求
跨域的概念:協議、域名、埠都相同才叫同域,否則都叫跨域
出於安全考慮,伺服器不允許ajax跨域獲取資料,但是可以跨域獲取檔案內容。
-
舉個簡單的例子:在編寫網頁的時候,
<img src = ‘www.xxxx.xxxx/’ >
URL 不是本域的也可以獲取資料 -
當我們傳送 XMLHttpRequest 請求的時候,如果請求的是別的域,那麼就會產生跨域問題,客戶端無法獲取伺服器端返回的資料
-
跨域的問題是發生在 XMLHttpRequest 請求的,也就是說,如果不是 XMLHttpRequest 請求,是不會產生跨域問題的。
解決跨域的方法
從瀏覽器出發
使用相關的引數啟動瀏覽器,解決跨域
通用性極低,不推薦
JSONP 解決跨域
什麼是 JSONP ?
script 標籤中的 src 屬性是可以跨域的。jsonp 是瀏覽器端傳送請求之後,伺服器端包裝好一段 json 資料,並放在一個 callback 函式中,返回一個 js 檔案。瀏覽器端動態引入這個 js 檔案,就會呼叫這個 callback 函式,獲取到資料。
原理:
首先在客戶端註冊一個 callback ,然後把 callback 的名字傳給伺服器。此時,伺服器先生成 json 資料,然後以 javascript 語法的方式,生成 function,function 名字就是傳遞上來的 callback 名稱。最後將 json 資料直接以入參的方式,放置 function 中,這樣就生成 js 語法的文件,返回給客戶端。客戶端瀏覽器,解析 script變遷,並執行返回 javascript 文件,此時資料作為引數,傳入了客戶端預先定義好的 callback 函式裡。
例子:
跨域服務端提供的js指令碼動態生成,這樣呼叫者可以傳一個引數過去告訴跨域服務端“我想要一段呼叫XXX函式的js程式碼,請你返回給我”,於是跨域伺服器就可以按照客戶端的需求來生成js指令碼並響應了。
//跨域伺服器
//檔案:flightResult.php
flightHandler({
"code":"CA1998",
"price": 1780,
"tickets": 5
});
//本地
<script type="text/javascript">
// 得到航班資訊查詢結果後的回撥函式
var flightHandler = function(data){
alert('你查詢的航班結果是:票價 ' + data.price + ' 元,' + '餘票 ' + data.tickets + ' 張。');
};
// 提供jsonp服務的url地址(不管是什麼型別的地址,最終生成的返回值都是一段javascript程式碼)
var url = "跨域伺服器/flightResult.php?code=CA1998&callback=flightHandler";
// 建立script標籤,設定其屬性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script標籤加入head,此時呼叫開始
document.getElementsByTagName('head')[0].appendChild(script);
</script>
缺點:
-
只支援 GET 請求而不支援 POST 等其它型別的 HTTP 請求 – 因為 script 標籤,只支援GET
-
只支援跨域 HTTP 請求這種情況,不能解決不同域的兩個頁面之間如何進行 JavaScript 呼叫的問題。
CORS 解決跨域問題
一、CORS 簡介:
CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。它允許瀏覽器向跨源伺服器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。
CORS需要瀏覽器和伺服器同時支援。目前,所有瀏覽器都支援該功能,IE瀏覽器不能低於IE10。
整個CORS通訊過程,都是瀏覽器自動完成,不需要使用者參與。對於開發者來說,CORS通訊與同源的AJAX通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動新增一些附加的頭資訊,有時還會多出一次附加的請求,但使用者不會有感覺。
因此,實現CORS通訊的關鍵是伺服器。只要伺服器實現了CORS介面,就可以跨源通訊。
二、兩種請求
瀏覽器將 CORS 請求分為兩類:簡單請求和非簡單請求
只要同時滿足一下兩個條件,就屬於簡單請求,反之為非簡單請求
- 請求方法是以下三種方法之一:
- HEAD
- GET
- POST
- HTTP的頭資訊不超出以下幾種欄位:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
三、簡單請求
對於簡單請求,瀏覽器直接發出 CORS 請求 :在頭部資訊中,直接加入一個 Origin 欄位
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
Origin 欄位用來說明本次請求來自哪個源(協議+域名+埠)。伺服器根據這個值,決定是否同意這次請求。
- 如果 Origin 指定的源不在許可範圍內,伺服器會返回一個正常的 http 迴應。這個迴應的頭資訊沒有包含 Access-Control-Allow-Origin 欄位,瀏覽器就會知道出錯了,從而丟擲一個錯誤,被 XMLHttpRequest 的 onerror 回撥函式捕獲。
注意,這種錯誤無法通過狀態碼識別,因為返回的狀態碼可能是 200 。
- 在許可範圍之內,伺服器返回的響應,會多出幾個頭資訊的欄位
Access-Control-Allow-Origin: http://api.bob.com //請求時 Origin 欄位的值或者 * ,表示接受對應域名的請求
Access-Control-Allow-Credentials: true //表示是否允許傳送 cookie
Access-Control-Expose-Headers: FooBar //拿到指定欄位
Content-Type: text/html; charset=utf-8