CORS 另外一種跨域方式

轉載自:http://www.adobe.com/cn/devnet/html5/articles/understanding-cross-origin-resource-sharing-cors.html


CORS代表跨域資源共享,是 HTML5 的一項特性,它允許一個站點訪問不同域名下另一個站點的資源。 我將在下文進行詳細介紹。 在 CORS 出現之前,人們採用一種名爲 Same-Origin Policy(同源策略)的 Web 瀏覽器安全限制來防止 Web 應用程序調用外部 API。 只有在兩個來源使用同一協議(http 和 https)、同一端口及同一域(甚至不同的子域都會導致失敗)時,瀏覽器纔會將這兩種資源視爲同源。

在 CORS 出現之前,您可以通過創建某種形式的服務器端組件傳送 API 請求來繞過這個常常過度複雜並且不必要的安全限制。 您也可以在支持 JSONP (帶填充的 JSON)的 API 中使用 JSONP,但大部分不支持這種做法,並且即使支持,JSONP 也僅限於 GET 請求。

有了 CORS 之後,我在一個域的應用程序就可以自由地與您另一個域上的 API 通信,甚至是使用POSTPUTDELETE,前提是您的 API 安全限制指定支持這些做法,並且您已經通過 CORS 規範建立了通信。 這就意味着您可以消除使用服務端組件的需求,並利用 JavaScript 在客戶端開展所有 API 通信。

在本文中,您將通過一個簡單的代碼示例來了解 CORS 規範,即如何啓用它,如何在特定的瀏覽器中使用它,以及如何利用 JSON 對其進行簡化等等,這個代碼能利用 GET 方法從其他域上的 API 檢索信息。

CORS 服務器端設置

本文的重點是利用 CORS 進行客戶端通信,但我們首先來快速瀏覽一下可用來理解 CORS 客戶端設置的資源。 這些設置都是通過瀏覽器翻譯的 response 頭文件來確定的。

CORS 的 W3C 規範實際上做得非常好,它提供了一些簡單的 response 頭文件示例,比如 key 頭文件、Access-Control-Allow-Origin,以及其他啓動 Web 服務器上的 CORS 所必需的頭文件。 可以理解這個規範並沒有詳細介紹任何特定的 Web 服務器(比如 IIS、Apache 等等)。它還討論了 ”預檢請求“ 的概念, 它必須用在諸如 PUT 或DELETE此類非 ”簡單方法“(簡單方法專門定義爲 GET、 HEAD和 POST)的請求上。

在提供一些有關跨域請求的 安全建議後,規範中的 語法 部分將詳細介紹您可以在服務器上指定的各種類型的頭文件,包括每個頭文件的作用。 還有各種各樣能確保您優化安全性的頭文件,比如 Access-Control-Allow-Credentials 頭文件,如果啓用,它將允許您共享像 cookie 和 HTTP 身份驗證信息這類內容。 另一個示例是Access-Control-Max-Age 頭文件,它指定了緩存一個非簡單方法請求的預檢請求所需的時間。

如果您正在尋找有關如何在各種常見的 Web 服務器上建立 CORS 的具體信息,請查看 支持 CORS 共享 網站。 網站解釋瞭如何在各種 Web 服務器(從 Apache到 IIS 再到 ExpressJS和其他服務器)上建立Access-Control-Allow-Origin response 頭文件。 但並未詳細介紹如何建立其他 response 頭文件或如何處理預檢請求。

最後,我推薦大家閱讀Monsur Hossain編寫的一個優秀的CORS 使用教程,在HTML5 Rocks 網站上可以找到該教程。 雖然主要着眼於客戶端通信,但 Monsur 瀏覽了各種各樣的 response 頭文件類型以及如何處理像PUT DELETE 這樣的複雜請求。 本教程並不是針對任何 Web 服務器,但結合 enable-cors.org中提供的說明應當能夠正常啓動和運行。

瀏覽器支持

爲了檢查瀏覽器支持情況,我構建了一個簡單的示例,用於從跨域 API 執行基本 GET。 儘管 CORS 的瀏覽器支持面已經相當廣泛,並且有越來越多的 API 支持 CORS,但仍有很多API 不支持 CORS。 不久,GitHub API也將支持CORS。 在下面的示例中,我將使用GET 請求調用 GitHub API 並基於 "Javascript" 關鍵字來檢索一系列資源庫,然後利用結果填充頁面。

<html><head> <title>CORS Example</title><script>   function onloadHandler() {      var xhr = new XMLHttpRequest();      xhr.open('GET', 'https://api.github.com/legacy/repos/search/javascript', true);      // Response handlers.      xhr.onload = function () {         var repos = JSON.parse(xhr.response), i, reposHTML = "";         for (i = 0; i < repos.repositories.length; i++) {            reposHTML += "<p><a href='" + repos.repositories[i].url + "'>" + repos.repositories[i].name + "</a><br>"  + repos.repositories[i].description + "</p>";         }         document.getElementById("allRepos").innerHTML = reposHTML;      };         xhr.onerror = function () {         alert('error making the request.');      };         xhr.send();   }</script></head><body οnlοad="onloadHandler()"> <div id="allRepos"></div></body></html>

注意: 這是我爲了方便說明而創建的一個簡單示例, 並且故意沒有使用任何框架。

這個 onLoadHandler() 函數創建了一個 XMLHttpRequest, 並通過GET請求將它打開到  GitHub API URL。Open 方法的第三個參數設置爲 true,並將該請求指定爲異步。

接下來,代碼片段要爲請求創建事件處理程序。 我們只處理 onload 和 onerror  事件,但還有很多其他事件使用  CORS,包括onloadstartonprogressonabortontimeout 和onloadend 事件。 在這個代碼片段的 onload 方法中,我解析了 JSON 響應並將幾個非常簡單的 HTML 填入div標籤。 在您爲 CORS 請求啓動 GitHub API,利用代碼來創建這些事件處理程序併發送請求後,將不會再遇到安全問題。

Chrome、FireFox、Opera 和 Safari 瀏覽器

從版本 3(這似乎是很久以前了)開始,Chrome 已經通過 XMLHttpRequest level 2 來支持 CORS  了。 在本教程中,您可以瞭解更多有關 XMLHttpRequest2 的新技巧(由  Eric Bidelman  編寫)。以上示例可以在 Chrome 上很好地運行。 Firefox 3.5 及以上的版本均支 CORS,並且這些示例在當前版本中也可以很好地運行。 Opera 支持添加的較晚,直到 12版本(當前版本是12.1)時才支持 CORS,但這個示例在當前版本中也可以很好地運行。 我無法在  Safari 瀏覽器(我現在所在的 Windows 平臺已經棄用 Safari )上測試示例代碼,但看到版本 4 中已經添加了支持,我想這個簡單的示例應該也能很好地運行。

Internet Explorer

遺憾的是, Internet Explorer  是唯一一種有效瀏覽器。 從技術上講, Internet Explorer 9(當前版本)支持  CORS,但不是通過 XMLHttpRequest 對象支持它。 相反,IE  使用的是 XDomainRequest  對象,對於我們的簡單示例,其工作原理幾乎完全相同,但調用 open()  時不接受異步參數。 下面是利用  XDomainRequest  爲 Internet Explorer  編寫的代碼。

function onloadHandler() {   var xhr = new XDomainRequest();   xhr.open('GET', 'https://api.github.com/legacy/repos/search/javascript');   // Response handlers.   xhr.onload = function () {      var repos = JSON.parse(xhr.response), i, reposHTML = "";      for (i = 0; i < repos.repositories.length; i++) {         reposHTML += "<p><a href='" + repos.repositories[i].url + "'>" + repos.repositories[i].name + "</a><br>"  + repos.repositories[i].description + "</p>";      }      document.getElementById("allRepos").innerHTML = reposHTML;   };      xhr.onerror = function () {      alert('error making the request.');   };   xhr.send();}

如果測試這個代碼示例,會發現它仍然無法運行。 爲什麼呢? 原來,正如  Eric Law 的博客:XDomainRequest – 限制、侷限和變通方案 中所描述的那樣,XDomainRequest 包含很多額外的安全限制。 其中一個安全限制認爲不同安全協議間(只針對 HTTP,不包括 HTTPS)的訪問限制“過於寬泛”。 雖然從一個安全頁面發送不安全的 HTTP 訪問會不受歡迎,並且人們可能會加以阻止,但 Internet Explorer 還是會阻止來自不安全頁面的安全請求。 因此,由於 GitHub 的 API 調用均通過 HTTPS 實現,您不能從使用不安全 HTTP 的某個頁面進行訪問。 還有一種 複雜的解決方案 ,但現在看來這些問題將最終在 Explorer 10 中得到解決,它通過 XMLHttpRequest 支持 CORS。在 Internet Explorer 博客 IE10 中的 XHR CORS  中可以瞭解更多與此相關的信息。

使用 jQuery

您可以通過依靠 jQuery 進一步簡化 CORS 使用,因爲它通過  jquery.ajax()  方法支持 CORS 請求。 顯然,同樣的限制也適用,雖然它不會在 Internet Explorer 9 中運行,但是它可以大大簡化您的代碼。 例如,下面的 jQuery 代碼創建了與以上代碼等效的功能,但所用的代碼行數少了很多。

<html><head>   <title>CORS Example</title><script src="jquery-1.7.2.min.js"></script><script>   $(function() {      $.ajax("https://api.github.com/legacy/repos/search/javascript").done(function(data) {         var i, repo;         $.each(data.repositories, function (i, repo) {            $("#allRepos").append("<p><a href='" + repo.url + "'>" + repo.name + "</a><br>"+ repo.description + "</p>");         });      });   });</script></head><body>   <div id="allRepos"></div></body></html>

如您所見,到 GitHub API 和處理結果的 CORS 請求大大簡化了。

下一步閱讀方向

我的代碼示例只通過 CORS 研究了一個簡單的 GET 請求。 很顯然,這與您今天通過 JSONP 所達到的效果差不多,但我們並不需要依賴 JSONP 格式。 然而,CORS 真正的優勢在於它執行 POSTPUT 和其他類型請求的能力。 例如,Google 的 YouTube API 早在 5 月份就在博客 利用 CORS 開發 JavaScript 的潛能中宣佈支持 CORS,其中包括 使用 CORS 完成 YouTube 上傳,從而讓用戶將視頻上傳到 YouTube 上。 視頻所有的身份認證和發佈都通過 Javascript(您也可以查看示例的源代碼)在客戶端完成。 有關如何創建和處理這類請求的更多信息,強烈推薦查看  HTML5Rocks 網站上的 CORS 使用教程。

Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License+Adobe Commercial Rights

本產品經 Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License 許可。Adobe 提供超出該許可範圍、與本產品包含的代碼示例相關的權限。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章