關於跨域

前言

轉眼就是秋招季啦。經歷了幾場筆試面試,多次被問到關於如何實現跨域。老實說,以前都是紙上談兵,也沒有項目須要跨域,甚至以爲這個東西沒什麼意義。直到今天項目中遇到了跨域問題,看了很多資料才理解跨域的廣泛性和意義。特寫此篇文章整理本身所得。
轉自我的博客: 關於跨域html

什麼是跨域

通常來講,若是你在開發中須要進行跨域操做(從一個非同源網站發送請求獲取數據),通常而言,你在瀏覽器控制檯看到的結果爲:前端

XMLHttpRequest cannot load http://external-domain/service. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://my-domain’ is therefore not allowed access.git

同源策略

說到跨域就不得不提「同源策略」。
同源策略是Web瀏覽器針對惡意的代碼所進行的措施,爲了防止世界被破壞,爲了保護世界的和平,Web瀏覽器,採起了同源策略,只容許腳本讀取和所屬文檔來源相同的窗口和文檔的屬性。
那麼,怎麼判斷文檔來源是否相同呢?很簡單,看三個部分: 協議主機端口號。只要其中一個部分不一樣,則不一樣源。github

跨域的應用場景

  1. 來自 home.example.com 的文檔裏的腳本讀取 developer.example.com載入的文檔的屬性。面試

  2. 來自 home.example.com 的文檔裏的腳本讀取 text.segmentfault.com載入的文檔的屬性ajax

如何跨域

設置domain屬性

針對上述應用場景的第一種狀況,能夠設置Document對象的domain屬性。可是設置時使用的字符串必須具備有效的域前綴或者它自己。
PS: domain值中必須有一個點號。
PS: domain不能由鬆散的變爲緊繃的。json

//初始值 "home.example.com" 
document.domain = "example.com"; //OK
document.domain = "home.example.com"; //NO,不能由鬆散變緊繃
document.domain = "example"; //NO,必須有一個點號
document.domain = "another.com"; //NO, 必須是有效域前綴或其自己

JSONP

JSONP由兩部分組成: 回調函數和數據。
原理:經過動態<script>元素來使用,能夠經過src屬性指定一個跨域URL。segmentfault

function handler(data){
    console.log(data);
}

var script = document.createElement("script");
script.src = "https://segmentfault.com/json/?callback=handler";
document.body.insertBefore(script, document.body.firstChild);

除此以外,還能夠利用jQuery來實現。api

function jsonCallback(json){
  console.log(json);
}

$.ajax({
  url: "http://run.plnkr.co/plunks/v8xyYN64V4nqCshgjKms/data-2.json",
  dataType: "jsonp"
});

運行結果以下:
結果跨域

某些API(例如Github API)容許你定義一個回調函數,當請求返回時執行該函數。

function logResults(json){
  console.log(json);
}

$.ajax({
  url: "https://api.github.com/users/jeresig",
  dataType: "jsonp",
  jsonpCallback: "logResults"
});

運行結果以下:
結果

優勢:

  1. 兼容性強。

  2. 簡單易用,能之間訪問響應文本,支持瀏覽器與服務器之間雙向通訊。

不足:

  1. 只能用GET方法,不能使用POST方法

  2. 沒法判斷請求是否失敗,沒有錯誤處理。

跨域資源共享CORS

須要瀏覽器和服務器同時支持。
原理:使用"Origin:"請求頭和"Access-Control-Allow-Origin"響應頭來擴展HTTP。其實就是利用新的HTTP頭部來進行瀏覽器與服務器之間的溝通。

針對前端代碼而言,變化的地方在於相對路徑需改成絕對路徑

//之前的方式
var xhr = new XMLHttpRequest(); 
xhr.open("GET", "/test", true); 
xhr.send(); 
//CORS方式
var xhr = new XMLHttpRequest(); 
xhr.open("GET", "http://segmentfault.com/test", true); 
xhr.send();

針對服務器代碼而言,須要設置Access-Control-Allow-Origin,顯式地列出源或使用通配符來匹配全部源。

優勢:

  1. CORS支持全部類型的HTTP請求。

  2. 使用CORS,開發者可使用普通的XMLHttpRequest發起請求和得到數據

不足:

  1. 不能發送和接收cookie
    更新:服務端能夠經過設置Access-Control-Allow-Credentials該字段來表示是否容許發送Cookie。發送ajax請求時,需配置withCredentials屬性。(感謝sf小夥伴@lloyd_zhou 指正) 具體可查看 阮一峯大大的博客

  2. 不能使用setRequestHeader()設置自定義頭部

  3. 兼容IE10+

postMessage

postMessge()是HTML5新定義的通訊機制。全部主流瀏覽器都已實現。該API定義在Window對象。

otherWindow.postMessage(message, targetOrigin);

message: 要傳遞的消息。
targetOrigin: 指定目標窗口的源。在發送消息的時候,若是目標窗口的協議、主機地址或端口這三者的任意一項不匹配targetOrigin提供的值,那麼消息就不會被髮送;只有三者徹底匹配,消息纔會被髮送。這個機制用來控制消息能夠發送到哪些窗口;

當源匹配時,調用postMessage()方法時,目標窗口的Window對象會觸發一個message事件。在進行監聽事件時,應先判斷origin屬性,忽略來自未知源的消息。

//<http://example.com:8080>上的腳本:
var popup = window.open(...popup details...);
popup.postMessage("The user is 'bob' and the password is 'secret'",
              "https://secure.example.net");  
popup.postMessage("hello there!", "http://example.org");

function receiveMessage(event)
{
  if (event.origin !== "http://example.org")
    return;
  // event.source is popup
  // event.data is "hi there yourself!  the secret response is: rheeeeet!"【見下面一段代碼可知】
}
window.addEventListener("message", receiveMessage, false);

針對上面的腳本進行接受數據的操做:

/*
 * popup的腳本,運行在<http://example.org>:
 */

//當postMessage後觸發的監聽事件
function receiveMessage(event)
{
  //先判斷源
  if (event.origin !== "http://example.com:8080")
    return;

  // event.source:window.opener
  // event.data:"hello there!"

  event.source.postMessage("hi there yourself!  the secret response " +
                           "is: rheeeeet!",
                           event.origin);
}

window.addEventListener("message", receiveMessage, false);

後續

收到了不少小夥伴的建議和指正,不勝感激,我會慢慢豐富這篇文章的內容的。請多多指教~

參考文章

  1. 前端跨域請求原理及實踐

  2. Window.postMessage()|MDN

  3. 老生常談的跨域處理

  4. JavaScript跨域問題總結

  5. 實現跨域的幾種方式

  6. jQuery’s JSONP Explained with Examples

相關文章
相關標籤/搜索