JavaScript >> Javascript 文檔 >  >> Tags >> Ajax

具有跨域資源共享的跨域 Ajax

幾年前,Web 開發人員在 Ajax 的第一面牆上撞了頭:同源策略。雖然我們驚嘆於對 XMLHttpRequest 的跨瀏覽器支持所帶來的巨大進步 對象,我們很快就哀嘆沒有辦法從 JavaScript 向不同的域發出請求。每個人都在他們的網站上設置代理,這是一系列新的開放重定向問題的開始,作為繞過限制的一種方式。儘管開發人員正在使用服務器端代理以及其他技術來解決此限制,但社區強烈反對允許本地跨域 Ajax 請求。很多人不知道目前幾乎所有的瀏覽器(Internet Explorer 8+、Firefox 3.5+、Safari 4+ 和 Chrome)都通過一種稱為跨域資源共享的協議支持跨域 Ajax。

跨域資源共享 (CORS)

跨域資源共享 (CORS) 是一個 W3C 工作草案,它定義了瀏覽器和服務器在跨域訪問資源時必須如何通信。 CORS 背後的基本思想是使用自定義 HTTP 標頭,讓瀏覽器和服務器能夠充分了解彼此,從而確定請求或響應是成功還是失敗。

對於一個簡單的請求,使用 GET 或 POST 且沒有自定義標頭且正文為 text/plain , 請求發送時帶有一個名為 Origin 的額外標頭 . Origin 標頭包含請求頁面的來源(協議、域名和端口),以便服務器可以輕鬆確定是否應該提供響應。一個例子 Origin 標題可能如下所示:

Origin: https://humanwhocodes.com

如果服務器決定允許該請求,它會發送一個 Access-Control-Allow-Origin 標頭回顯與發送的相同來源或“*”,如果它是公共資源。例如:

Access-Control-Allow-Origin: https://humanwhocodes.com

如果缺少此標頭,或者來源不匹配,則瀏覽器將拒絕該請求。如果一切正常,則瀏覽器會處理該請求。請注意,請求和響應都不包含 cookie 信息。

前面提到的所有瀏覽器都支持這些簡單的請求。 Firefox 3.5+、Safari 4+ 和 Chrome 都支持通過 XMLHttpRequest 使用 目的。當嘗試打開不同來源的資源時,此行為會自動觸發,無需任何額外代碼。例如:

var xhr = new XMLHttpRequest();
xhr.open("get", "https://humanwhocodes.com/some_resource/", true);
xhr.onload = function(){  //instead of onreadystatechange
    //do something
};
xhr.send(null);

要在 Internet Explorer 8 中執行相同操作,您需要使用 XDomainRequest 以同樣的方式對象:

var xdr = new XDomainRequest();
xdr.open("get", "https://humanwhocodes.com/some_resource/");
xdr.onload = function(){
    //do something
};
xdr.send();

Mozilla 團隊在他們關於 CORS 的帖子中建議您應該檢查 withCredentials 是否存在 屬性來確定瀏覽器是否通過 XHR 支持 CORS。然後你可以加上 XDomainRequest 的存在 對象覆蓋所有瀏覽器:

function createCORSRequest(method, url){
    var xhr = new XMLHttpRequest();
    if ("withCredentials" in xhr){
        xhr.open(method, url, true);
    } else if (typeof XDomainRequest != "undefined"){
        xhr = new XDomainRequest();
        xhr.open(method, url);
    } else {
        xhr = null;
    }
    return xhr;
}

var request = createCORSRequest("get", "https://humanwhocodes.com/");
if (request){
    request.onload = function(){
        //do something with request.responseText
    };
    request.send();
}

XMLHttpRequest Firefox、Safari 和 Chrome 中的對象具有與 IE XDomainRequest 足夠相似的接口 反對這種模式運作良好。常用的接口屬性/方法有:

  • abort() – 用於停止正在進行的請求。
  • onerror – 使用代替 onreadystatechange 檢測錯誤。
  • onload – 使用代替 onreadystatechange 檢測成功。
  • responseText – 用於獲取響應內容。
  • send() – 用於發送請求。

預檢請求

CORS 允許通過稱為預檢請求的透明服務器驗證機制使用自定義標頭、GET 或 POST 以外的方法以及不同的正文內容類型。當您嘗試使用高級選項之一發出請求時,會向服務器發出“預檢”請求。此請求使用 OPTIONS 方法並發送以下標頭:

  • Origin – 與簡單請求相同。
  • Access-Control-Request-Method – 請求要使用的方法。
  • Access-Control-Request-Headers –(可選)使用的自定義標頭的逗號分隔列表。

假設 POST 請求帶有名為 NCZ 的自定義標頭的示例 :

Origin: https://humanwhocodes.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: NCZ

在這個請求期間,服務器可以確定它是否允許這種類型的請求。服務器通過在響應中發送以下標頭將其傳達給瀏覽器:

  • Access-Control-Allow-Origin – 與簡單請求相同。
  • Access-Control-Allow-Methods – 允許方法的逗號分隔列表。
  • Access-Control-Allow-Headers – 服務器將允許的標頭的逗號分隔列表。
  • Access-Control-Max-Age – 應緩存此預檢請求的時間量(以秒為單位)。

示例:

Access-Control-Allow-Origin: https://humanwhocodes.com
Access-Control-Allow-Methods: POST, GET
Access-Control-Allow-Headers: NCZ
Access-Control-Max-Age: 1728000

一旦發出預檢請求,結果將在響應中指定的時間段內緩存;您只會在第一次發出這種類型的請求時產生額外的 HTTP 請求的費用。

Firefox 3.5+、Safari 4+ 和 Chrome 都支持預檢請求; Internet Explorer 8 沒有。

憑據請求

默認情況下,跨域請求不提供憑據(cookie、HTTP 身份驗證和客戶端 SSL 證書)。您可以通過設置 withCredentials 來指定請求應發送憑據 屬性為真。如果服務器允許有憑據的請求,那麼它會使用以下 HTTP 標頭進行響應:

Access-Control-Allow-Credentials: true

如果發送了一個憑據請求並且此標頭未作為響應的一部分發送,則瀏覽器不會將響應傳遞給 JavaScript (responseText 是一個空字符串,status 為 0,onerror() 被調用)。請注意,服務器也可以將此 HTTP 標頭作為預檢響應的一部分發送,以指示允許源發送憑據請求。

Internet Explorer 8 不支持 withCredentials 財產; Firefox 3.5、Safari 4 和 Chrome 都支持它。

結論

在現代 Web 瀏覽器中有很多對跨域 Ajax 的可靠支持,但是大多數開發人員仍然沒有意識到這種強大的功能。使用只需要一點額外的 JavaScript 工作和一點額外的服務器端工作,以確保發送正確的標頭。 IE8 的實現在允許高級請求和憑據請求方面落後於其他一些,但希望對 CORS 的支持將繼續改進。如果您想了解更多信息,我強烈建議您查看 Arun Ranganathan 的示例頁面。

更新(2010 年 5 月 25 日): 修正了示例代碼中的錯字。

更新(2010 年 5 月 27 日): 從 Origin 標頭中刪除了尾部斜杠。


Tutorial JavaScript 教程
  1. TypeScript:空值合併

  2. 多選 get Selected options 按順序選擇

  3. A-Frame 資產管理系統阻止 JavaScript 代碼

  4. 將 ReactJS 組件轉換為 VueJS

  5. 如何遍歷js元素

  6. 一步一步:一個簡單的 Node.js、Docker 和 Kubernetes 設置

  7. 為 Vuetify 組件創建包裝器組件

  1. 提升你的變量! (JavaScript 中的變量提升)

  2. 谷歌地圖:在markerclusterer上方渲染標記

  3. Aura Theme v2.0.0 現已推出! 🥳🎉

  4. 您對績效評估會議有何期待?

  5. 開發者聚焦:Cosmic JS 社區中的 Sumit Kharche

  6. 設計令牌和主題

  7. 16 個未充分利用的 Web 平台功能

  1. 向 JavaScript 添加管道

  2. 以下是 JavaScript 中新的內置方法和函數

  3. 我是如何顛覆我的(職業)生活的

  4. 使用lazysizes 延遲加載Web 上的圖像