生存指南之 CORS + API Gateway
本文介绍了跨域资源共享的基本知识,以及如何避免云函数上 Serverless web API 的问题
构建 Web API 是 Serverless 应用中最流行的用例之一,您能在不增加其他操作开销的情况下,获得简单、可扩展的后端优势。
然而,如果您的网页正在调用后端 API,那么必须处理 跨域资源共享 (CORS) 的问题 ,如果您的网页向与您当前所在域的不同域发出 HTTP 请求,则它必须是 CORS 友好的。
如果您发现以下错误:
No 'Access-Control-Allow-Origin' header is present on the requested resource
那么本文可能对您有所帮助。
接下来,我们将介绍 Serverless + CORS 的相关信息,目录如下:
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 应用的开发!
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
【网站搭建】阿里云服务器搭建个人网站详细流程
1. 工具的选择 因为直接网页操作实例的话不是很方便,鉴于有Linux和大数据开发经验。使用以下工具 winscp:文件传输,可以实现本地和远程端的文件传输,也可以直接修改远程端文件,不用在终端里了 XSHELL:安全终端模拟软件,可以连接远程操作终端命令 2. 购买 购买的9.9学生价的centos6.5 ecs服务器有Windows和Linux可选,一般选择Linux(我选择的centos)。可以在 磁盘设置 更换 1 2 安全组 购买后在控制台可查看实例信息,首先需要修改安全组策略。 在服务器-实例页面相应实例上点击管理进入,点击配置规则 出入方向都要配置,我的配置如下 说明:安全组规则也就是默认一些端口是关闭的或者说只有该实例有权访问,需要人为打开权限。如授权对象下的IP:0.0.0.0/0就是所有都可以访问,这个酌情配置 声明:阿里云官网目前可以领取2000元产品代金券,领取后可在官网买云产品时,获得金额减免,领取地址:https://www.aliyun.com/minisite/goods?userCode=28kqeewo ; 本地通过公网ip连接时连不上也ping不通...
- 下一篇
XXL-JOB v2.2.0 发布 | 跨语言特性增强
v2.2.0 Release Notes 1、RESTful API:调度中心与执行器提供语言无关的 RESTful API 服务,第三方任意语言可据此对接调度中心或者实现执行器。 2、任务复制功能:点击复制是弹出新建任务弹框,并初始化被复制任务信息; 3、任务手动执行一次的时候,支持指定本次执行的机器地址,为空则从执行器获取; 4、任务结果丢失处理:调度记录停留在 "运行中" 状态超过10min,且对应执行器心跳注册失败不在线,则将本地调度主动标记失败; 5、调度中心升级springboot2.x;因此,系统要求JDK8+; 6、XxlJob注解扫描方式优化,支持查找父类以及接口和基于类代理等常见情况;修复任务为空时小概率NPE问题; 7、移除旧类注解JobHandler,推荐使用基于方法注解 "@XxlJob" 的方式进行任务开发;(如需保留类注解JobHandler使用方式,可以参考旧版逻辑定制开发); 8、任务告警组件模块化:如果需要新增一种告警方式,只需要新增一个实现 "com.xxl.job.admin.core.alarm.JobAlarm" 接口的告警实现即可,更加灵活...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Hadoop3单机部署,实现最简伪集群
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS关闭SELinux安全模块
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Docker安装Oracle12C,快速搭建Oracle学习环境
- MySQL8.0.19开启GTID主从同步CentOS8
- Linux系统CentOS6、CentOS7手动修改IP地址