今天在聯調接口的時候,發現了一個奇怪的錯誤Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response
javascript
OPTIONS
請求,得到瀏覽器支持的請求類型,那麼什麼時候會進行預檢請求呢?
只有在瀏覽器發送非簡單請求的狀況下,纔會發送預檢請求html
那麼什麼樣叫作非簡單請求,什麼又是簡單請求呢?前端
只要同時知足如下兩大條件,就屬於簡單請求。java
凡是不一樣時知足上面兩個條件,就屬於非簡單請求。node
這是該問題所引出的相關名詞,本着好奇的原則,繼續往下走...linux
跨域是針對瀏覽器端而產生的,服務器端的通訊並不會產生這樣的問題,這就引出了第一個解決跨域的思路----代理服務器(瀏覽器請求本地起的服務器—同源服務器,再由後者請求外部服務)而且跨域並不是瀏覽器限制了發起跨站請求,而是跨站請求能夠正常發起,可是返回結果被瀏覽器攔截了。ios
咱們利用node能夠實現代理服務器,筆者是利用了http-proxy-middlewaregit
var proxy = require('http-proxy-middleware');
var apiProxy = proxy('/api', {target: 'http://www.example.org', changeOrigin: true});
複製代碼
在這裏changeOrigin: true
引發了個人興趣,思考爲何會存在該項配置?這裏提到了虛擬主機與獨立主機的概念github
在咱們ping baidu.com的時候會獲得IP地址,瀏覽器經過這個IP地址是能夠訪問網站的(獨立主機),在咱們ping apple.com的時候獲得的IP地址是不能經過瀏覽器的方式訪問的(共享主機或者虛擬主機) 虛擬主機是一種在單一主機或主機羣上,實現多網域服務的方法,能夠運行多個網站或服務的技術,所以它能夠共享服務器的內存、CPU等資源web
虛擬主機主要有如下幾種類型
在http-proxy-middleware
中使用的changeOrigin
配置項正式因爲使用了網址名稱對應的虛擬主機。
在本篇博客中,該種方式會進行詳細介紹,利用AJAX進行跨域資源共享的時候,咱們就會引出文章剛開始所遇到的問題,簡單請求和非簡單請求。
'use strict';
var express = require('express'),
app = express();
app.get('/auth/:id/', function (req, res) {
res.send({ id:req.params.id });
});
app.listen(3000);
複製代碼
而後利用backend.js
指向一個html
文件,在html
裏面發送ajax
請求,模仿跨域(端口號不一樣)
'use strict';
var express = require('express'),
app = express();
app.use(express.static("./"))
app.get('/', function (req, res) {
res.render(__dirname + '/index.html');
});
app.listen(4000);
複製代碼
最後是html
文件,引入了axios
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
</body>
<script type="text/javascript">
axios.get('http://localhost:3000/auth/1')
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
</script>
</html>
複製代碼
當發起請求時,咱們發現
即便在返回結果是200的狀況下,仍然時跨域提醒,這就與前面的觀點是一致的 跨域並不是瀏覽器限制了發起跨站請求,而是跨站請求能夠正常發起,可是返回結果被瀏覽器攔截了。 服務器會返回一個正常的HTTP迴應。瀏覽器發現,這個迴應的頭信息沒有包含Access-Control-Allow-Origin
字段(詳見下文),就知道出錯了,從而拋出一個錯誤。 那麼解決方法也很簡單,在後端加上容許跨域請求。
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
複製代碼
這樣就能夠正常訪問
Content-Type
爲application/json
時,代碼以下:<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
</body>
<script type="text/javascript">
axios.post('http://localhost:3000/auth', {
name: 'gao'
}, {
headers: {
'Content-Type': 'application/json'
}
})
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
</script>
</html>
複製代碼
會發送如圖所示的預檢請求:
當頭部Content-Type
爲
application/x-www-form-urlencoded
時,代碼以下:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
</body>
<script type="text/javascript">
axios.post('http://localhost:3000/auth', {
name: 'gao'
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
</script>
</html>
複製代碼
會發現,瀏覽器不會發送預檢請求而且返回成功
那麼如何保證在發送預檢請求時,瀏覽器不會報錯呢?後端代碼以下:
'use strict';
var express = require('express'),
app = express();
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "content-type");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
app.get('/auth/:id/', function (req, res) {
res.send({ id:req.params.id });
});
app.post('/auth', function (req, res) {
res.send({ id:req.params.id });
});
app.listen(3000);
複製代碼
會發現加入了這兩行代碼便可。
res.header("Access-Control-Allow-Headers", "content-type"); // 這裏的content-type是默認的,根據客戶端請求來設定
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
複製代碼
此時POST會發送兩次請求:
當預檢請求經過後,再次發送POST
請求。
像其餘的技術,包括JSONP、LocalStorage、cookie、postMessage在此很少作介紹,能夠參考阮大的博客進行參考。
在有些時候,後端的接口因爲各類不能知足咱們所須要的跨域支持,那麼就須要前端本身下功夫,咱們思考一下,既然瀏覽器因爲安全策略阻止了返回的結果,那麼就能夠在瀏覽器的安全策略上進行修改。
設置Chrome瀏覽器的 disable-web-security
, 實現跨域訪問後端的接口。
"C:\Users\UserName\AppData\Local\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir
open -a "Google Chrome" --args --disable-web-security --user-data-dir
chromium-browser --disable-web-security