使用 Hapi 框架在實例 CORS 場景下測試首部字段做用。這裏不須要你掌握 Hapi 框架的使用,以及任何 Node 知識。前端
你須要懂得哪方面的知識?git
XMLHttpRequest
(Ajax) / Fetch
發起 HTTP 請求下面咱們會對照這 MDN 上 CORS 部分的講解(下面簡稱 講解
),使用具體代碼來測試首部字段的做用。github
主體結構咱們按照 Hapi 官網的示例,修改路由部分。json
const Hapi = require('@hapi/hapi')
const init = async () => {
const server = Hapi.server({
port: 3000,
host: 'localhost'
})
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
console.log(request.info)
console.log(request.headers)
return {
code: 200,
data: {
success: true
}
}
}
})
await server.start()
console.log('Server running on %s', server.info.uri)
}
process.on('unhandledRejection', err => {
console.log(err)
process.exit(1)
})
init()
複製代碼
這樣咱們就有了一個本地 3000 端口的服務,並有一個 /
路徑的 API。下面咱們使用 Fetch
發起一個跨域請求,使用 Chrome 打開任意網站,打開 開發者工具
,在 Console
下進行測試。後端
fetch('http://localhost:3000')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson);
});
複製代碼
收到如下錯誤:api
Access to fetch at 'http://localhost:3000/' from origin 'developer.mozilla.org' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.跨域
直接訪問 http://localhost:3000
是能夠看到結果的。說明瀏覽器限制從腳本內發起的跨源HTTP請求。瀏覽器
下面增長容許跨域源的字段服務器
path: '/',
+++ options: {
+++ cors: {
+++ origin: ['*']
+++ }
+++ },
複製代碼
以上代碼等同於在 response headers 中增長 access-control-allow-origin
字段爲 *
,容許任何源的跨域請求。cookie
再次訪問,成功獲取到內容。對照 MDN 講解並查看 Chrome Network
面板中 headers
部分和服務日誌。
對照講解理解 「簡單請求」 和 」預檢請求「 的區別。
下面發起一個 POST JSON 的請求,並在服務端接收。
--- method: ['GET']
+++ method: ['GET', 'POST'],
// 輸出請求體
+++ console.log(request.payload)
複製代碼
fetch('http://localhost:3000', {
method: 'POST',
body: JSON.stringify({'user': 'kenny'}),
headers: new Headers({
'Content-Type': 'application/json'
})
})
複製代碼
在 Network
中會看到有 2 次請求發起,由於咱們修改了除規定之外的首部字段,因此首先發起了一個 options
的預檢請求。
OPTIONS 是 HTTP/1.1 協議中定義的方法,用以從服務器獲取更多信息。該方法不會對服務器資源產生影響。
一樣咱們查看 Network
中的 headers 部分進行對比和理解。
Fetch 與 CORS 的一個有趣的特性是,能夠基於 HTTP cookies 和 HTTP 認證信息發送身份憑證。通常而言,對於跨域 XMLHttpRequest 或 Fetch 請求,瀏覽器不會發送身份憑證信息。若是要發送憑證信息,須要設置 XMLHttpRequest 的某個特殊標誌位。
首先增長一個 domain 爲 localhost 的 cookie 在測試域下。而後增長下面代碼。
// 攜帶憑證
fetch('http://localhost:3000', {
credentials: 'include'
})
複製代碼
origin: ['*']
+++ credentials: true
// 輸出 cookie
+++ console.log(request.state)
複製代碼
執行後會發現仍是跨域錯誤。這是由於對於附帶身份憑證的請求,服務器不得設置 Access-Control-Allow-Origin 的值爲「*」。
因此修改 origin
--- origin: ['*'],
+++ origin: ['https://developer.mozilla.org'],
複製代碼
如今執行,能夠在日誌中看到剛剛增長的 cookie。
反過來,咱們在服務端設置 cookie,看前端可否生效。
h.state('user', 'kenny', {
isSecure: false,
isHttpOnly: false,
isSameSite: 'false',
domain: 'localhost'
})
複製代碼
一樣可行。
Access-Control-Allow-Headers
其指明瞭實際請求中容許攜帶的首部字段。
Access-Control-Expose-Headers
:
在跨域訪問時,XMLHttpRequest對象的getResponseHeader()方法只能拿到一些最基本的響應頭,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,若是要訪問其餘頭,則須要服務器設置本響應頭。
首先咱們獲取服務器時間
先刪除多餘代碼
--- console.log(request.payload)
--- console.log(request.state)
--- h.state('user', 'kenny', {
--- isSecure: false,
--- isHttpOnly: false,
--- isSameSite: 'false',
--- domain: 'localhost'
--- })
複製代碼
fetch('http://localhost:3000')
.then(function(response) {
console.log(response.headers.get('Date'))
})
// null
複製代碼
後端增長代碼,容許獲取額外的頭部。
+++ additionalExposedHeaders: ['Date']
複製代碼
再次測試後顯示接口獲取時服務器的時間。
如今前端發送一個自定義的頭(Region) 表明當前的地理位置(北京: 52),而後使用後端獲取它。
fetch('http://localhost:3000', {
headers: new Headers({
'Region': 52
})
})
複製代碼
+++ additionalHeaders: ['Region']
複製代碼
能夠從後端日誌中看到 headers 含有 Region
字段
關於 Hapi 框架的 CORS 設置,能夠參考:Hapi route cors
所有代碼:
const Hapi = require('@hapi/hapi')
const init = async () => {
const server = Hapi.server({
port: 3000,
host: 'localhost'
})
server.route({
method: ['GET', 'POST'],
path: '/',
options: {
cors: {
origin: ['https://*.mozilla.org'],
credentials: true,
additionalExposedHeaders: ['Date'],
additionalHeaders: ['Region']
}
},
handler: (request, h) => {
console.log(request.info)
console.log(request.headers)
console.log(request.payload)
console.log(request.state)
// 設置 cookie
// h.state('user', 'kenny', {
// isSecure: false,
// isHttpOnly: false,
// isSameSite: 'false',
// domain: 'localhost'
// })
return {
code: 200,
data: {
success: true
}
}
}
})
await server.start()
console.log('Server running on %s', server.info.uri)
}
process.on('unhandledRejection', err => {
console.log(err)
process.exit(1)
})
init()
複製代碼
跨域問題其實並不複雜,網上教程也很是多,其實對於同源策略和跨域的概念,只要閱讀 MDN 就能夠了,本身動手建立一個服務器,對照調試工具和後端日誌,查看 HTTP 請求和響應,加深理解。但願這個實例教程能幫助你們理解前端跨域 和 Hapi 框架的使用。
附上一個 整理和機翻的 Hapi 中文文檔,以爲有用的小夥伴能夠點點關注。