系列文章:
- 【小哥哥, 跨域要不要了解下】JSONP
- 【小哥哥, 跨域要不要了解下】CORS 基礎篇
- 【小哥哥, 跨域要不要了解下】CORS 進階篇
- 【小哥哥, 跨域要不要了解下】NGINX 反向代理
- 【小哥哥, 跨域要不要了解下】ServerProxy
在前一篇文章中, 我們一起學習了第一種跨域處理方案
JSONP
. 這種方法相對比較原始, 優點是相容性好, 就連現代前端沒怎麼聽說過的IE 6
上跑起來都是妥妥的. 然鵝, 它也就這一點優點了. 其缺點有: 只支援 GET 請求, 配置繁瑣(前後端都需要調整程式碼), 在 window 上註冊各種回撥函式, 開發體驗差....
CORS
由於 JSONP 的方案存在諸多缺點且老舊, 這裡我們一起學習一種比較現代的跨域問題解決方案---CORS
相容性
從 mdn 官網粘來的相容性列表如下:

ie 10 都可以跑, 足以滿足現代前端開發者的需求了.
概念 ?
概念性的東西在這兒 MDN 偶爾需要梯子, 自備哈.
搭建跨域的環境
我們先建立一個跨域的環境, 程式碼基於我們 jsonp 時候的示例專案 cross-domain, 首先, 在 fe 和 be 目錄下建立 cors 目錄. 其次, 分別新增 index.html
和 index.js
. 修改以後的專案目錄如下圖.

編寫前端程式碼 fe/cors/index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>CORS 實現跨域</title>
</head>
<body>
<h3>CORS 實現跨域</h3>
<script>
var xhr = new XMLHttpRequest()
xhr.open('GET', 'http://localhost:8888')
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText)
}
}
xhr.send()
</script>
</body>
</html>
複製程式碼
一個灰常簡單的 ajax 請求, 有木有.
後端程式碼 be/cors/index.js
const http = require('http');
const PORT = 8888;
// 建立一個 http 服務
const server = http.createServer((request, response) => {
response.end("{name: 'quanquan', friend: 'guiling'}");
});
// 啟動服務, 監聽埠
server.listen(PORT, () => {
console.log('服務啟動成功, 正在監聽: ', PORT);
});
複製程式碼
此時的專案程式碼
找點苗頭
程式碼環境準備完成後
- 首先啟動後端程式碼
node ./be/cors/index.js
- 其次啟動前端 web 容器
live-server ./fe/cors
- 開啟瀏覽器, 訪問 http://localhost:8080/
- 開啟控制檯, 切換到 Console tab
- 重新整理瀏覽器

'Access-Control-Allow-Origin' header is present on the requested resource.
, 這麼明顯的暗示, 難道我們就不試試???

響應頭新增 Access-Control-Allow-Origin
針對瀏覽器的報錯, 我們分析出他是要我們在響應頭上新增Access-Control-Allow-Origin
這個欄位,
那麼我們修改我們的後端程式碼.
const http = require('http');
const PORT = 8888;
// 建立一個 http 服務
const server = http.createServer((request, response) => {
response.setHeader('Access-Control-Allow-Origin', '*');
response.end("{name: 'quanquan', friend: 'guiling'}");
});
// 啟動服務, 監聽埠
server.listen(PORT, () => {
console.log('服務啟動成功, 正在監聽: ', PORT);
});
複製程式碼
改動後的程式碼.
瀏覽器重新整理一下, 我了個乖乖. 好了 ?

開心過後, 我們想一下, jsonp 的缺點是隻能支援 GET 請求, 作為現代
的跨域請求方式. cors 能不能支援其他的請求方式呢?
其他請求方式的支援
作為現代
的跨域問題解決方案, 應該是能解決多種請求方式的. 光說不練假把式. 我們們試試 ?
改動前端程式碼
// 改動前
xhr.open('GET', 'http://localhost:8888')
// 改動後
xhr.open('POST', 'http://localhost:8888')
複製程式碼
修改後程式碼來瀏覽器上看一下?

木有任何問題, 返回的資料順利的列印. 沒有任何的報錯.
趁著興頭試試PUT
請求
再次改動前端程式碼
// 改動前
xhr.open('POST', 'http://localhost:8888')
// 改動後
xhr.open('PUT', 'http://localhost:8888')
複製程式碼
修改後程式碼來瀏覽器上看一下?

哎呀我滴媽? 很眼熟的錯誤, 但是不要認錯人哈, 這次的報錯和之前的報錯長的很像, 但是關鍵詞不一樣了. 根據之前的經驗, 後端新增 Access-Control-Allow-Methods
響應頭應該好使.
後端程式碼
const http = require('http');
const PORT = 8888;
// 建立一個 http 服務
const server = http.createServer((request, response) => {
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Allow-Methods', 'PUT');
response.end("{name: 'quanquan', friend: 'guiling'}");
});
// 啟動服務, 監聽埠
server.listen(PORT, () => {
console.log('服務啟動成功, 正在監聽: ', PORT);
});
複製程式碼
修改後程式碼來瀏覽器上看一下?

成功了 ?

其他的 http 方法和 PUT
方法處理的方式是一樣的. 舉一反三即可.
後端說, 你的請求要加一個 token 呀
既然是現代的開發, 那麼會話的管理一般是會用 jwt(後續可能會寫相關的文章), jwt 一個閃耀的標誌就是請求頭新增了 jwt token. 明人不說暗話.

修改前端程式碼:
// 新增了一行
xhr.setRequestHeader('token', 'quanquanbunengshuo')
複製程式碼
修改後程式碼來瀏覽器上看一下?

相信大家已經摸清了我的套路, 閒話不扯.
後端程式碼
// 新增了一行
response.setHeader('Access-Control-Allow-Headers', 'token');
複製程式碼
修改後程式碼來瀏覽器上看一下?

目前為止, 跨域請求成功了, 請求方式相容了, 自定義請求頭好使了. 是不是大吉大利, 可以吃雞了呢?
致, 被打入冷宮的 Network tab
我們自始至終都在檢視瀏覽器的 Console tab, 作為一個通訊性質的文章, 不看一下 Network 明顯有點說不過去辣.

既然存在, 那肯定是要看看的, 我們把 tab 切換到 Network.

哎呦喂? 兩個請求, 一個 OPTIONS 一個 PUT, 這是什麼鬼?
下集預告: 剛剛看到了 Network 就出現了血案. 當然如果僅僅是停留在會用 CORS 實現跨域上, 到目前為止已經沒有什麼問題了, 用來面試也是槓槓滴. 下一步, 我們一起探討 CORS 條件下, 預檢請求
和 Cookie
攜帶那些事兒. 週五碼字感覺好累...... 約小姐姐去辣 ?
