1. 程式人生 > 其它 >[1.6W字]瀏覽器跨域請求的原理, 以及解決方法(可以純前端實現) #flight.Archives011

[1.6W字]瀏覽器跨域請求的原理, 以及解決方法(可以純前端實現) #flight.Archives011

[1.6W字]瀏覽器跨域請求的原理, 以及解決方法(可以純前端實現) #flight.Archives011

Title/ 瀏覽器跨域(CrossOrigin)請求的原理, 以及解決方案詳細指南 #flight.Archives011

序:
最近看到又有一波新的創作活動了, 官方給出的話題中有一個"為什麼XHR不能跨域請求資源", 看起來像一道很簡單的面試題哈哈哈
作為前端愛好者, 不妨藉此機會研究一下跨域, 總結一篇文章出來. 於是我就安排了flight.A011的FocusList#寫作計劃.

簡介:
一篇最簡潔高效的跨域請求指南 made with ❤ by 忘我思考

注:

  1. 本文中XHR表示 XMLHttpRequest 物件.
  2. CORS表示Cross-origin resource sharing
    , 譯為跨域資源共享.
  3. 請求頭即 Request Headers, 響應頭即 Response Headers.

Tag/ 瀏覽器跨域(CORS)限制介紹: 為什麼要限制XHR跨域請求, 如何觸發跨域限制

大佬們可以直接看下一章解決方案, 這裡會解釋得詳細一點方便新手入門(誰都曾是萌新嘛), 不瞭解CORS的來這補補課吧~

跨域, 就是站A的請求要訪問站B的伺服器.
如果 協議(protocol), 域名(domain), (port) 三者有任意一處不同, 瀏覽器就認為這是兩個不同的網站, 會觸發跨域安全限制.

詳細的說, 就是:
[https://][example.com][:80]/path?query#heading-1
只有打方括號內的內容完全一致才是"同源", 也就是說同源請求URL中只有埠後的內容是可以不一致的, 否則就是跨域
請注意: https協議的預設埠是443, http協議預設埠是80. 預設埠可以省略不寫.
也就是說 https://example.com:443/... 和 https://example.com/... 是同源, http同理.

->> 為什麼瀏覽器要有跨域限制

設想這樣的場景:
我是不知名小Blog主, 你現在在訪問我的部落格網站, 然後我突發奇想, 想要用自動JS程式傳送請求到 weibo 的伺服器, 讓你自動關注我的微博.
如果這能實現, 那瀏覽器就太不安全了, 網站主可以在後臺做很多危險請求(如獲取你在 weibo 的登入資訊等).
所以瀏覽器都有一個跨域限制, 阻止不安全的第三方內容請求.

舉個例子:

如果我在Segmentfault官網的Console訪問我的部落格園首頁, 就會觸發XHR跨域限制, 禁止讀取返回內容.

而另一個情景:
你是一個小站長, 在網站使用Vue.js, 於是添加了一個CDN指令碼(如 unpkg

等)
結果瀏覽器錯誤的因為跨域限制自動攔截請求導致你無法使用CDN指令碼.
這顯然是不合理的, 所以現在的瀏覽器都會智慧根據對方(網站B)的 響應頭 中的 Access-Control-Allow-Origin屬性, 來判斷你是否可以讀取對方返回的內容.
比如 unpkg 就把 Access-Control-Allow-Origin 屬性設為了 *, 方便外域呼叫CDN.

像React也建議你驗證CDN的請求頭屬性, 避免使用出現跨域限制問題.

也就是說, 你能不能訪問第三方資源, 其實是由對方(響應頭)決定的(這其實也是合理的, 按道理請求別人的東西就要經過允許呀).
對方伺服器可以通過你的 cookie referer請求頭 引數來判斷請求的安全性, 決定是否返回對應內容.
注意: 純前端是不能纂改 referer 請求頭的, 否則會報錯.

->> 瀏覽器對請求的詳細劃分方法: 簡單請求和複雜請求

不過瀏覽器的跨域限定其實有詳細的劃分, 這裡有必要提到一下:
請求分為 簡單請求複雜請求 兩種.
對於 簡單請求, 要滿足以下所有條件("與"關係).

  1. 請求方法是 GET, POST, HEAD 之一.
  2. 人為設定的請求頭不超過以下屬性: Accept, Accept-Language, Content-Language.
  3. Content-Type 屬性值是 text/plain, multipart/form-data, application/x-www-form-urlencoded 之一.
  4. 請求中的任意 XMLHttpRequestUpload 物件均沒有註冊任何事件監聽器; XMLHttpRequestUpload 物件可以使用 XMLHttpRequest.upload 屬性訪問.
  5. 請求中沒有使用 ReadableStream 物件.

其中前3點比較常用. 如果請求不滿足以上5個條件的任意之一, 則是複雜請求.

瀏覽器區分 簡單請求複雜請求 是為了兼容表單(form), 因為歷史上表單一直可以發出跨域請求.

->> XHR請求在瀏覽器裡是如何被髮出去的? 瀏覽器對簡單請求和複雜請求的不同處理方式

這裡我覺得阮一峰的文章已經寫得很好了(在文末有連結), 所以, 如有雷同(不對, 就是雷同)... 不要覺得我是在抄襲, 只是按照他的創意共享3.0許可證合法借鑑哈.

  1. 瀏覽器對簡單請求的處理方式!
    如果一個XHR滿足簡單請求的所有判定, 那它會被這樣發出去:

    // 直接傳送包含Origin請求頭的XHR請求到對方伺服器
    瀏覽器會自動在頭資訊之中, 新增一個Origin欄位.
    如: Origin: http://api.gold.xitu.io (現在掘金被位元組收取就變味了, 好懷念Ming在的時間啊...)
    Origin是"起源"的意思, 包含協議 + 域名 + 埠, 會告訴對方請求是從哪個源發出的.
    如果Origin指定的源不在許可範圍內(比如在cnblogs中新增訪問Bilibili的程式碼), 對方會返回一個不包含Access-Control-Allow-Origin的響應頭, 
    瀏覽器就知道出錯了, 從而丟擲一個錯誤, 被XMLHttpRequest的onerror回撥函式捕獲. 
    注意: 這種錯誤無法通過狀態碼識別, 因為HTTP迴應的狀態碼有可能是200.
    
    如果Origin指定的域名在許可範圍內, 伺服器返回的響應, 會多出幾個頭資訊欄位:
    Access-Control-Allow-Origin: http://api.bbb.com
    Access-Control-Allow-Credentials: true
    Access-Control-Expose-Headers: FooBar
    上面這些以 Access-Control- 開頭的欄位, 都表示對方伺服器對請求的訪問限制.
    
    下面介紹一下這三個屬性: 
    1. Access-Control-Allow-Origin(必須屬性): 可以是URL, 表示對請求時Origin欄位的值的限制, 也可以是 *, 表示接受任意域名的請求, 像CDN程式碼儲存服務之類的.
    2. Access-Control-Allow-Credentials(可選): 一個布林值, 只能為true, 表示是否允許傳送Cookie. 預設情況下(不填該屬性), 則Cookie不包括在跨域請求之中.
    3. Access-Control-Expose-Headers(可選): 跨域請求時, XHR物件的getResponseHeader()方法只能拿到6個基本欄位: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma. 如果想拿到其他欄位, 必須在Access-Control-Expose-Headers中指定. 上面的例子指定getResponseHeader('FooBar')可以返回FooBar欄位的值.
    
    ->> Details: withCredentials 屬性
    上面說到, 跨域請求預設不傳送Cookie和HTTP認證資訊. 如果要把Cookie發到伺服器, 一方面要伺服器同意, 指定Access-Control-Allow-Credentials欄位為true.
    另一方面, 網站主必須在AJAX請求中開啟withCredentials屬性. 像這樣:
    
    var xhr = new XMLHttpRequest();
    xhr.withCredentials = true;
    
    否則, 即使伺服器同意傳送Cookie, 瀏覽器也不會發送. 這種情況下伺服器即使要求設定Cookie, 瀏覽器也不會處理. 
    
    但是, 如果省略withCredentials設定, 有的瀏覽器還是會一起傳送Cookie. 這時, 可以顯式關閉withCredentials:
    xhr.withCredentials = false;
    
    需要注意的是, 如果要傳送Cookie, Access-Control-Allow-Origin就不能設為星號, 必須指定明確的, 與請求網頁一致的域名. 同時, Cookie依然遵循同源政策, 只有用伺服器域名設定的Cookie才會上傳, 其他域名的Cookie並不會上傳, 且(跨域的)原網頁程式碼中的document.cookie也無法讀取對方伺服器域名下的Cookie.
    
  2. 瀏覽器對複雜請求的處理方式!

    // 先發送預檢請求(preflight), 請求通過後, 傳送XHR到對方伺服器
    瀏覽器會先詢問伺服器, 當前網頁所在的域名是否在伺服器的許可名單之中, 以及可以使用哪些HTTP動詞和頭資訊欄位. 只有得到肯定答覆, 瀏覽器才會發出正式的XMLHttpRequest請求, 否則就報錯.
    
    來了, 這是一段JS程式碼!
    var url = 'http://api.aaa.com/cors';
    var xhr = new XMLHttpRequest();
    xhr.open('PUT', url, true);
    xhr.setRequestHeader('X-Custom-Header', 'value');
    xhr.send();
    
    可以看到使用了 PUT 方法, 還人為設定了X-Custom-Header請求頭, 所以這是個複雜請求qwq...
    
    所以瀏覽器會發送一個預檢請求, 預檢請求的請求頭像這樣:
    OPTIONS /cors HTTP/1.1
    Origin: http://api.bbb.com
    Access-Control-Request-Method: PUT
    Access-Control-Request-Headers: X-Custom-Header
    Host: api.aaa.com
    ...(以及一些更多的無關請求頭)
    
    預檢請求使用了 OPTIONS 方法, 表示這個請求是用來詢問的. 請求頭中, 關鍵欄位是Origin, 表示請求來自哪個源.
    而 Access-Control-Request-Method 和 Access-Control-Request-Headers 你一看就明白是什麼了, 分別是請求方法和人為設定了哪些屬性.
    
    伺服器受到預檢請求後, 判斷請求頭是否OK, 如果OK就可以做出迴應啦!
    響應頭中關鍵資訊是這幾個:
    Access-Control-Allow-Origin: http://api.bbb.com
    Access-Control-Allow-Methods: GET, POST, PUT
    Access-Control-Allow-Headers: X-Custom-Header
    
    解釋一下~
    1. Access-Control-Allow-Origin: 表示允許的請求源, 也可以是*表示隨便什麼網站.
    2. Access-Control-Allow-Methods: 就是允許的方法啊!
    3. Access-Control-Allow-Headers: 允許設定的請求頭引數.
    
    好了, 那如果伺服器不同意預檢請求怎麼辦呢~
    
    會返回一個正常的HTTP迴應, 但是沒有任何CORS相關的頭資訊欄位. 這時,瀏覽器就認為, 伺服器不同意預檢請求.
    因此觸發一個錯誤, 被XMLHttpRequest物件的onerror回撥函式捕獲. 
    控制檯會打印出如下的報錯資訊:
    
    XMLHttpRequest cannot load http://api.aaa.com.
    Origin http://api.bbb.com is not allowed by Access-Control-Allow-Origin.
    
    伺服器迴應的其他CORS相關欄位如下:
    Access-Control-Allow-Methods: GET, POST, PUT //所有支援的方法
    Access-Control-Allow-Headers: X-Custom-Header //如果瀏覽器請求包括Access-Control-Request-Headers欄位, 則Access-Control-Allow-Headers欄位是必需的, 表示支援的所有請求頭欄位.
    Access-Control-Allow-Credentials(可選): true //也是表示Cookie
    Access-Control-Max-Age(可選): 1728000 //表示本次預檢請求的有效期, 單位為秒. 比如這個就設定了本次請求有效期是20天(1728000秒), 期間不再次傳送請求.
    

->> 案例: 解決一個部落格音樂播放器的跨域問題

瀏覽器的安全限制其實也不是完美無缺的, 前端的話如果不涉及驗證碼, 登入, 加密之類的複雜操作, 通過一些技術手段想要突破跨域限制, 也不是沒有可能.
無後端支援也是可以實現跨域的, 不過一般要在使用者知情的情況下配合操作.
其實就是你可以實現一些跨域請求, 但是是否能夠獲取請求內容, 本來是由對方伺服器請求頭決定的, 現在決定權在於使用者.
比如你想要在你的部落格中新增一個音樂播放器, 播放QQ音樂的內容.
而眾所周知, 騰訊那邊的盜鏈(可以理解為跨域)限制是很強的!
所以一般我的實現是配合後端, 也就是訪問QQ音樂網址, 把音樂通過瀏覽器控制檯Network欄獲取請求資源URL下載儲存到自己的伺服器.
然後直接訪問自己的伺服器獲取音樂資源(QQ音樂一般是m4a, 網易一般是mp3, 不過順便一提網易取消了盜鏈保護, 所以一般可以直接用網易伺服器, 只是有的音樂有版權保護就不能外域播放了).
而如果要純前端實現跨域音樂播放則可以使用 <iframe>, Chrome引數纂改, Chrome外掛等方法來實現盜鏈.

這些《神奇》的方法也就是下一章會詳細介紹的內容啦~ [奸笑]

Tag/ 重頭戲開始了! 突破跨域限制的9種解決方案

  1. 使用 iframe 實現
    iframe 標籤可以將跨域內容嵌入你的網頁, 比如在部落格中新增一個Bilibili視訊 或者 Codepen Demo的 iframe.
    不過 iframe 有兩個缺點:

    1. 很多伺服器會直接拒絕跨域 iframe 請求,
    2. 跨域訪問 iframe 中的內容會遭到限制qwq.
      放幾張圖體會一下:

    如果在阮一峰的Blog中插入 iframe(cnblogs博文), 會被拒絕qwq...

    而如果在站內插入iframe則沒有問題, 看來部落格園為了安全考慮目前關閉了跨域請求.

    而Bilibili就比較開放了, 目前可以插入iframe.

    不過如果你想通過iframe讀取或者篡改Bilibili網頁的內容也是不可以的啦~

    所以這終究只是一個向用戶"展示內容"的方法而已, 限制很多, 如果要讀取/篡改內容, 還是得同源才行~

  2. 使用CORS方法(跨域資源共享)
    這個方法也就是第一章中介紹的, 很詳細了. 不用再重複一遍啦.

    但是這種方法也是受到對方伺服器的種種限制, 不能為所欲為qwq...

  3. 搞一個瀏覽器外掛配合(終極大招, 直接無視跨域限制)
    嘿嘿嘿! 沒想到吧! 竟然還有這種方法?!
    網上這方面的文章一般都不會想到有這種神奇也是非常強大的方法, 不過這也是非常實用的.
    如果要我來, 很可能會採用這種方法, 可以不用後端支援進行跨域!
    如果你的網站面向的物件主要是愛好破解的極客/有探索精神的玩家, 可以向他們推薦你的Chrome外掛, 或者是一段油猴指令碼.
    比如讓使用者簡單的copy&paste新增一段你的油猴(TemperMonkey)指令碼, 就可以實現跨域請求啦!
    (現在油猴/Chrome外掛在極客/開發者手裡也已經很普及了吧~, 想想你裝了多少外掛...
    (當然一個都沒裝的勿噴!

    不過現在Chrome外掛那麼多, 可是你... 能不能獨立開發一個擴充套件外掛呢? 恐怕很難吧...
    (其實如果你懂前端, 外掛開發也不難, 只要瞭解Chrome外掛專門的一些方法就行了.
    那麼這裡就簡單介紹一下實現方法, 如果你想開發一個完整實用的外掛, 網上也有不少文件了. (Chrome Developer中也有詳細的介紹

    不如先學習一下別人是怎麼玩的. 我們隨便開啟一個別人開發的Chrome外掛的資料夾(我開啟的是Tempermonkey), 是這幅景象:

    其中有一個 manifest.json 檔案, 和你的外掛支不支援跨域密切相關, 比如油猴的長這樣:

    {
    "background": {
    "page": "background.html"
    },
    "browser_action": {
    "default_icon": {
    "16": "images/icon_grey16.png",
    "19": "images/icon_grey19.png",
    "24": "images/icon_grey24.png",
    "32": "images/icon_grey32.png",
    "38": "images/icon_grey38.png"
    },
    "default_popup": "action.html",
    "default_title": "Tampermonkey"
    },
    "commands": {
    "open-dashboard": {
    "description": "Open dashboard"
    },
    "open-dashboard-with-running-scripts": {
    "description": "Open dashboard with the current tab's URL used as filter"
    },
    "open-new-script": {
    "description": "Open new script tab"
    },
    "toggle-enable": {
    "description": "Toggle enable state"
    }
    },
    "content_scripts": [ {
    "all_frames": true,
    "js": [ "rea/common.js", "content.js" ],
    "matches": [ "file:///*", "http://*/*", "https://*/*" ],
    "run_at": "document_start"
    } ],
    "content_security_policy": "script-src 'self'; object-src 'self';",
    "default_locale": "en",
    "description": "The world's most popular userscript manager",
    "differential_fingerprint": "1.2ae827c5b75f9e167b3ed0bf65d0c330028dd0acf93a84a8f0f2d7e096024ba3",
    "icons": {
    "128": "images/icon128.png",
    "32": "images/icon.png",
    "48": "images/icon48.png"
    },
    "incognito": "split",
    "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwuYtg7kY2YyNieOkV9pK/qcXwUXu0CFUO0zU6DLAGAJZK7zxrHlwg9a+zFH7CXqgH7zSfRce9KiYOHJaLPBXM66uPCliiQ6Q+bFaNZx1FxLXkZTFnlyPh8kkwwohLJeSQ9NQXqEfeTepDj5BRufAR48az0MC5aUTEj+fFXbzX7QIDAQAB",
    "manifest_version": 2,
    "minimum_chrome_version": "64.0.0.0",
    "name": "Tampermonkey BETA",
    "offline_enabled": true,
    "optional_permissions": [ "downloads" ],
    "options_page": "options.html",
    "options_ui": {
    "chrome_style": false,
    "open_in_tab": true,
    "page": "options.html"
    },
    "permissions": [ "notifications", "unlimitedStorage", "tabs", "idle", "webNavigation", "webRequest", "webRequestBlocking", "storage", "contextMenus", "chrome://favicon/", "clipboardWrite", "cookies", "declarativeContent", "\u003Call_urls>" ],
    "short_name": "TM BETA",
    "update_url": "https://clients2.google.com/service/update2/crx",
    "version": "4.14.6142"
    }
    

    這個是Chrome外掛的靈魂, 設定了各種屬性, 包括Chrome外掛的圖示, 點選Chrome外掛圖示開啟的popup頁面等...
    這裡和跨域相關的只有一個: 就是 permissions 屬性.

    這個屬性設定了外掛有哪些許可權, 比如可以隨意訪問哪些網站.

    比如油猴的, 就有一個 "\u003Call_urls>"permissions 屬性裡.
    其中 \u003C 其實就是 < 的Unicode編碼, = <all_urls>, 也就是這個外掛定義的JS程式碼可以隨意訪問各個網站

    而在使用者眼中就是這幅景象:

    關於Chrome外掛配合網站實現跨域的文件, 可以參考這篇 https://wizardforcel.gitbooks.io/chrome-doc/content/23.html, 總結得很完整了.

    而我搜索的時候, 驚喜的發現, 還有人開發了這樣一個外掛(不是我開發的啊):
    名字叫 "Allow CORS", 挺好用的, 初衷是幫助開發者跨域除錯網站.

    使用者評價也挺不錯的.
    網址就是 https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf
    用途當然就是和名字一樣, 可以解除跨域限制. 所以你只要讓使用者輕輕一點去裝上這個外掛, 就可以無視跨域限制啦!
    不過Chrome因為Google原因, 在國內有限制, 面對一般的使用者Chrome也支援上傳本地包來新增外掛, 也很簡單, 下載->一點開啟開發者模式->一拖新增外掛 就OK啦!

    好了這個方法是真的tql.

  4. 配合客戶端本地代理VPN(終極大招)
    看完第三個方法, 我覺得其他的都要退下了, 畢竟第3個方法應該是最簡單的了.
    第四個原理差不多, 不過開發起來是真的麻煩啊.
    要讓使用者裝一個系統應用, 本地VPN, 就像Charles, Fiddler這樣.
    然後在本地自動修改 Referer 和 Origin 等請求頭, 假裝一個正常的使用者請求.
    總之不是一個優秀的解決方案吧.

  5. 配合後端(終極大招)
    本文著重純前端開發, 我也不太懂後端(基本的php除外, 用來建部落格要用到)
    這個方法也是很實用的, 但是不詳細介紹了.
    就是針對不涉及使用者在瀏覽器中cookie的情況, 通過後端語言(如Java)正常請求來獲取資源.
    再返回到前端, 請求一個本站資源(其實轉自對方站).

    比如第一章中提到的音樂播放器, Github上就有人用配合後端實現了! 還有1.9k個star!
    不過這個解析下載音樂因為侵權了, 所以2018年起暫停維護了, 不過下載下來依然可以使用.

    不過只能下載免費音樂, 付費聽的音樂會ErrorHappensQwQ.
    因為只是後端搜尋解析一下, 音樂資源依然是來自對方伺服器(所以QQ音樂盜鏈保護還是生效, 不能聽啊... 網易就沒事, 畢竟網易一向開放)
    想了解一下的, 可以啪的點進去: https://github.com/maicong/music

  6. JSONP方法
    對於 script 標籤的資源, 瀏覽器是沒有跨域限制的! (對方按照referer/origin屬性不返回資料的情況除外)

    比如網上就有這樣的實現:

    <!-- 原生JS實現 -->
    <script type="text/javascript">
      window.jsonpCallback = function(res) {
        console.log(res);
      };
    </script>
    <script
      src="http://localhost:80/api/jsonp?&cb=jsonpCallback"
      type="text/javascript"
    ></script>
    <!-- JQuery Ajax實現 -->
    <script src="https://cdn.bootcss.com/jquery/3.5.0/jquery.min.js"></script>
    <script>
      $.ajax({
        url: "http://localhost:80/api/jsonp",
        dataType: "jsonp",
        type: "get",
        data: {
          msg: "hello"
        },
        jsonp: "cb",
        success: function(data) {
          console.log(data);
        }
      });
    </script>
    

    嗯, 就是這樣用的, 很簡單獲取Script內容, 不過也只支援Script qwq...

  7. WebSocket實現
    WebSocket是HTML5的一個特性, 可以讓瀏覽器化被動為主動傳送內容, 適合實時直播等場景(比如位元組的掘金直播就用到了這種方法)

    WebSocket其實雖然有用(本來就沒有CORS的跨域限制), 但其實也沒有辦法獲取HTTP資源QAQ.

    所以這個方法也不推薦吧!

  8. window.postMessage() 方法
    這個方法可以安全地實現跨源通訊!

    不瞭解這個方法, 可以去這裡補補課~ ->> https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage

    在《HTML5CSS3權威指南》中也有介紹, 但是在實戰中這個方法使用也比較少哈~

    這個方法可以進行不同介面的訊息傳遞:
    比如多視窗之間, 網頁和其iframe之間.

    比如QQ音樂就用到了這種方法.
    如果你開啟了多個播放頁面, QQ音樂會通過這個方法多介面之間通訊, 自動暫停之前的播放頁, 避免同時播放多個音樂.

    當然正因為它可以 "安全地" 實現跨源通訊, 所以這個通訊方法也是需要對方伺服器認可的. 所以如果要做不正經的事(對方不允許的情況下強行獲取資源)也沒有用啊...

  9. 使用"允許跨域"方法開啟瀏覽器
    既然是瀏覽器搞的限制, 能不能關閉這個限制呢?
    是可以的!

    就以這種方法開啟(Windows, xxx是你的data目錄):

    C:/.../.../path/chrome.exe --disable-web-security --user-data-dir=xxxx
    

    Mac也可以! 也是一樣的.

    --disable-web-security --user-data-dir=~/Downloads/chrome-data
    

    就是這樣, 不過要重啟Chrome才行, 使用者可能不願意, 所以還是外掛這個方法我最推薦.

當然, 如果不正當獲取了第三方內容是可能會有律師函警告的~
所以說, 本文只是對跨域的介紹和技術上的探索與研究, 僅此而已. (你懂的[doge]

->> Details

附錄 - CORS請求與JSONP的比較

CORS與JSONP的使用目的相同, 但是比JSONP更強大.

JSONP只支援GET請求, CORS支援所有型別的HTTP請求. JSONP的優勢在於支援老式瀏覽器, 以及可以向不支援CORS的網站請求資料.

MDN中文文件 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS

MDN 英文文件 https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

阮一峰 https://www.ruanyifeng.com/blog/2016/04/cors.html

秋風的筆記 https://segmentfault.com/a/1190000022398875

思否 - 安靜de沉澱 https://segmentfault.com/a/1190000011145364

掘金 - 小銘子 https://juejin.cn/post/6844903882083024910

掘金 - JackySummer https://juejin.cn/post/6861553339994374157

Chrome Developers https://developer.chrome.com/docs/extensions/mv3/xhr/

Html5Rocks https://www.html5rocks.com/en/tutorials/cors//

部落格園 - 小火柴的藍色理想 https://www.cnblogs.com/xiaohuochai/p/6036475.html

知乎 - 單純前端能否解決跨域問題? https://www.zhihu.com/question/302245173

MDN - XHR介紹 https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest

MDN - XHR介紹(英文) https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest

MDN - Access-Control-Allow-Origin 屬性介紹 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Allow-Origin

MDN - Access-Control-Allow-Origin 屬性介紹(英文) https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin

StackOverflow - How does Access-Control-Allow-Origin header work? https://stackoverflow.com/questions/10636611/how-does-access-control-allow-origin-header-work

StackOverflow - CORS with XMLHttpRequest not working https://stackoverflow.com/questions/25296455/cors-with-xmlhttprequest-not-working

W3 - CORS Enabled https://www.w3.org/wiki/CORS_Enabled

->> Version History

現在版本為V1.0
詳見 Github(@flightmakers)

2021.8.23 釋出V1.0