本文介紹了跨域資源共享的基本知識,以及如何避免云函式上 Serverless web API 的問題
構建 Web API 是 Serverless 應用中最流行的用例之一,您能在不增加其他操作開銷的情況下,獲得簡單、可擴展的后端優勢,
然而,如果您的網頁正在呼叫后端 API,那么必須處理 跨域資源共享 (CORS) 的問題 ,如果您的網頁向與您當前所在域的不同域發出 HTTP 請求,則它必須是 CORS 友好的,
如果您發現以下錯誤:
No 'Access-Control-Allow-Origin' header is present on the requested resource
那么本文可能對您有所幫助,
接下來,我們將介紹 Serverless + CORS 的相關資訊,目錄如下:
- 預檢請求 (Preflight requests)
- 回應頭 (Response headers)
- CORS with cookie credentials
TL;DR
快速開始 ?? 如果您想快速解決 Serverless 應用中的 CORS,可以執行以下操作:
- 要處理 preflight requests,在每個 HTTP 端點中添加
enableCORS: true和integratedResponse: true標記:
# serverless.yml
service: products-service
provider:
name: tencent
region: ap-guangzhou
runtime: Nodejs8.9 # Nodejs8.9 or Nodejs6.10
plugins:
- serverless-tencent-scf
functions:
getProduct:
handler: handler.getProduct
events:
- apigw:
name: api
parameters:
path: /product
stageName: release
# 修改成你的 API 服務 ID
serviceId: service-xxx
httpMethod: GET
# 開啟集成相應,這里必須開啟,才能自定義回應 headers
integratedResponse: true,
# 開啟 CORS
enableCORS: true
createProduct:
handler: handler.createProduct
events:
- apigw:
name: api
parameters:
path: /product
stageName: release
# 修改成你的 API 服務 ID
serviceId: service-xxx
httpMethod: POST
# 開啟集成相應,這里必須開啟,才能自定義回應 headers
integratedResponse: true,
# 開啟 CORS
enableCORS: false
- 要處理 CORS headers,請在回應中回傳 CORS headers,主要標頭是
Access-Control-Allow-Origin和Access-Control-Allow-Credentials,示例如下:
'use strict';
// mock function
function retrieveProduct(event) {
return {
id: 1,
name: 'good1',
price: 10,
};
}
// mock function
function createProduct(event) {
const { queryString } = event;
return {
id: Number(queryString.id),
name: 'good1',
price: 10,
};
}
module.exports.getProduct = (event, context, callback) => {
const product = retrieveProduct(event);
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({
product: product,
}),
};
callback(null, response);
};
module.exports.createProduct = (event, context, callback) => {
// Do work to create Product
const product = createProduct(event);
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({
product: product,
}),
};
callback(null, response);
};
CORS preflight requests
如果您不是在進行「simple request」,那么瀏覽器會用 OPTIONS 方法,發送 preflight
request 到資源里,您請求的資源將使用 安全發送到資源 的方法回傳,并且可以選擇回傳有效發送的標頭,
我們拆開來看看:
瀏覽器什么時候發送 preflight requests?
您的瀏覽器會對幾乎所有的跨域請求發送一個 preflight requests,(例外是「simple requests」,但這只是請求的一小部分),大體上看,一個簡單請求只是一個 GET request 或者 POST request,如果您不在此范圍內,則需要進行預檢,
對 preflight requests 的回應是什么?
對一個 preflight requests 的 回應包括其允許訪問的資源,它允許在該資源的方法,如 GET, POST,
PUT 等,還可以包括被允許在該資源標頭,如 Authentication,
如何處理 Serverless 中的 preflight requests?
要設定 preflight requests,您只需要在 API Gateway 的端點上配置一個 OPTIONS ,幸運的是,你可以非常簡單地使用 Serverless Framework 來完成,
只需要在 serverless.yml 添加設定 enableCORS: true:
# serverless.yml
service: products-service
provider:
name: tencent
region: ap-guangzhou
runtime: Nodejs8.9 # Nodejs8.9 or Nodejs6.10
plugins:
- serverless-tencent-scf
functions:
getProduct:
handler: handler.getProduct
events:
- apigw:
name: api
parameters:
path: /product
stageName: release
serviceId: service-lanyfiga
httpMethod: GET
# 開啟集成相應,這里必須開啟,才能自定義回應 headers
integratedResponse: true,
# 開啟 CORS
enableCORS: true
createProduct:
handler: handler.createProduct
events:
- apigw:
name: api
parameters:
path: /product
stageName: release
serviceId: service-lanyfiga
httpMethod: POST
# 開啟集成相應,這里必須開啟,才能自定義回應 headers
integratedResponse: true,
# 開啟 CORS
enableCORS: false
CORS Response Headers
盡管 preflight request 僅適用于某些跨域請求,但每個跨域請求中都必須存在 CORS Response Headers,這意味著您必須將 Access-Control-Allow-Origin 添加進 handlers 的回應中,
如果您使用 cookies,還需要添加 Access-Control-Allow-Credentials,
要與上面的 serverless.yml 匹配,handler.js 檔案應該如下設定:
// handler.js
'use strict';
module.exports.getProduct = (event, context, callback) => {
// Do work to retrieve Product
const product = retrieveProduct(event);
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({
product: product,
}),
};
callback(null, response);
};
module.exports.createProduct = (event, context, callback) => {
// Do work to create Product
const product = createProduct(event);
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({
product: product,
}),
};
callback(null, response);
};
這里需要注意 response 的 headers 屬性,其中包含 Access-Control-Allow-Origin 和 Access-Control-Allow-Credentials,
下面是一個簡單的示例:
// hello.js
const middy = require('middy');
const { cors } = require('middy/middlewares');
// This is your common handler, no way different than what you are used to do every day
const hello = (event, context, callback) => {
const response = {
statusCode: 200,
body: 'Hello, world!',
};
return callback(null, response);
};
// Let's "middyfy" our handler, then we will be able to attach middlewares to it
const handler = middy(hello).use(cors()); // Adds CORS headers to responses
module.exports = { handler };
CORS with Cookie credentials
在上面的示例中,我們給定了 "*" 作為 Access-Control-Allow-Origin 的值,但是,如果您使用 request using credentials 則不被允許,為了使瀏覽器能夠回應,Access-Control-Allow-Origin 需要包含發出請求的特定來源,有兩種方法可以解決,
首先,如果只有一個發出請求的原始網站,則可以將其硬編碼到云函式的回應中:
// handler.js
'use strict';
module.exports.getProduct = (event, context, callback) => {
// Do work to retrieve Product
const product = retrieveProduct(event);
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': 'https://myorigin.com', // <-- Add your specific origin here
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({
product: product,
}),
};
callback(null, response);
};
如果有多個原始網站使用您的 API,那么需要采用一種更加動態的方法,你可以檢查 origin header 看看是否在被批準的來源串列中,如果是,則在 Access-Control-Allow-Origin 回傳原點值,
// handler.js
'use strict';
const ALLOWED_ORIGINS = [
'https://myfirstorigin.com',
'https://mysecondorigin.com'
];
module.exports.getProduct = (event, context, callback) => {
const origin = event.headers.origin;
let headers;
if (ALLOWED_ORIGINS.includes(origin) {
headers = {
'Access-Control-Allow-Origin': origin,
'Access-Control-Allow-Credentials': true,
},
} else {
headers = {
'Access-Control-Allow-Origin': '*',
},
}
// Do work to retrieve Product
const product = retrieveProduct(event);
const response = {
isBase64Encoded: false,
statusCode: 200,
headers
body: JSON.stringify({
product: product
}),
};
callback(null, response);
};
在這個示例中,我們檢查 origin header 是否匹配,如果匹配,我們會在 Access-Control-Allow-Origin 包含特定來源,并宣告 Access-Control-Allow-Credentials 允許的來源,如果 origin 不是我們允許的來源之一,則我們將包含標準 headers,如果來源嘗試進行憑據請求,則將被拒絕,
小結
處理 CORS 確實是一件麻煩的事情,但是使用 Serverless Framework 會讓處理步驟變得簡單得多!而這也就意味著再也不會出現 No 'Access-Control-Allow-Origin' header is present on the requested resource 這樣的錯誤啦!??
傳送門:
- GitHub: github.com/serverless
- 官網:serverless.com
歡迎訪問:Serverless 中文網,您可以在 最佳實踐 里體驗更多關于 Serverless 應用的開發!
推薦閱讀:《Serverless 架構:從原理、設計到專案實戰》
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/24784.html
標籤:其他
上一篇:今天我想學什么,
下一篇:我說我的話,你非不聽?
