Serverless Framework API Gateway AWS 服务代理集成(Service Proxy)配置实战指南
本文基于开源仓库 GitHub_Trending/se/serverless 中官方指南文档 api-gateway-aws-proxy.md 展开。该内置能力允许你在 serverless.yml 中声明式地把 API Gateway 与 Kinesis、SQS、S3、SNS、DynamoDB、EventBridge 等 AWS 托管服务直接打通,全程不编写、不部署任何 Lambda 函数。读完本文,你将掌握 custom.apiGatewayServiceProxies 配置的完整语法、每种服务的代理参数、请求/响应映射模板的深度定制(CORS、鉴权、私有 API Key、自定义 IAM Role、Path Override、VTL 模板等),并能结合仓库源码理解其在 package 编译阶段的底层实现。
一、什么是 API Gateway AWS 服务代理集成
API Gateway 本身支持把 REST API 方法直接“代理”到 AWS 服务 API,这一模式称为 AWS Service Proxy Integration。它不需要 Lambda 中转:
- 客户端请求到达 API Gateway 的 REST 端点;
- API Gateway 将请求映射并转发到目标 AWS 服务的指定 API 动作(如
PutRecord、SendMessage、PutObject、Publish、PutItem、PutEvents); - 使用 IAM 执行角色(integration credentials)完成服务调用并回传响应。
这一模式的收益非常直观:没有函数冷启动、没有按毫秒计的 Lambda 计费、基础设施更少。Serverless Framework 将其封装为声明式配置 custom.apiGatewayServiceProxies,在运行 serverless deploy 时自动生成对应的 AWS::ApiGateway::Method、AWS::ApiGateway::Resource、IAM 角色与 Deployment 等 CloudFormation 资源。
从源码入口 index.js 可以确认该插件的工作时序:它注册了 package:compileEvents 与 after:deploy:deploy 两个钩子,前者在打包阶段校验并编译代理资源,后者在部署结束后打印生成的端点信息。同时 shouldLoad() 通过读取 serverless.service.custom.apiGatewayServiceProxies 决定是否加载本能力。
二、来源与从社区插件迁移
该能力最初来自社区插件 serverless-apigateway-service-proxy(由 serverless-operations 团队及社区贡献者维护),如今已作为 Serverless Framework 的内置能力开箱即用,无需额外安装任何东西。
如果你正在从社区插件迁移:
- 从
serverless.yml的plugins段移除该插件; - 从项目依赖中卸载对应 npm 包;
- 保留现有的
custom.apiGatewayServiceProxies配置——内置集成会继续读取并遵循这一配置结构。
在源码文件头部的注释 index.js 中可以看到其衍生于 serverless-apigateway-service-proxy(Copyright 2019 Takahiro Horike and contributors,MIT License),完整许可文本位于仓库根目录的 THIRD_PARTY_LICENSES。
三、当前支持的服务清单
根据仓库校验 schema schema.js 中 allowedProxies 数组,内置集成当前支持六类代理:
| 服务 | 配置键 | 支持的 API 动作 |
|---|---|---|
| Kinesis Streams | kinesis |
PutRecord(默认)、PutRecords |
| SQS | sqs |
SendMessage |
| S3 | s3 |
GetObject、PutObject、DeleteObject |
| SNS | sns |
Publish |
| DynamoDB | dynamodb |
PutItem、GetItem、DeleteItem |
| EventBridge | eventbridge |
自定义事件发布 |
这些动作枚举同样在 schema.js 中逐项校验。例如 Kinesis 的 action 仅允许 PutRecord / PutRecords,DynamoDB 仅允许三种 Item 级操作,S3 仅允许三个 Object 级操作。
四、使用方式与整体配置结构
所有服务代理统一声明在 serverless.yml 的 custom.apiGatewayServiceProxies 下,它是一个数组,每个元素是形如 { <serviceKey>: { path, method, ... } } 的对象。getServiceName() 与 getAllServiceProxies()(见 utils.js)通过取对象第一个键来区分服务类型并遍历全部代理。
配置完成后直接执行:
serverless deploy
Framework 在 package:compileEvents 阶段按顺序编译:先编译 RestApi/资源/CORS,随后分别调用 compileKinesisServiceProxy()、compileSqsServiceProxy()、compileS3ServiceProxy()、compileSnsServiceProxy()、compileDynamodbServiceProxy()、compileEventBridgeServiceProxy(),最后 mergeDeployment() 把生成的方法逻辑 ID 合并进 AWS::ApiGateway::Deployment(见 index.js)。
每个代理对象还共享一组公共字段,它们的取值在 schema.js 中被统一定义为 proxy 模式:
path(必填):REST 资源路径,支持{param}与贪心{param+};method(必填):get/post/put/patch/options/head/delete/any(大小写不敏感);cors:布尔或对象,见下文 CORS 章节;private:布尔,默认false,是否要求 API Key;authorizationType/authorizerId/authorizationScopes:鉴权配置;roleArn:自定义集成执行 IAM 角色;acceptParameters:声明方法接受的请求参数;requestParameters:集成请求参数映射;request:包含template、contentHandling、passThrough的请求定制对象;response:响应定制(简化模板或完整对象数组两种形态)。
五、Kinesis 代理
Kinesis 代理的常见写法如下(示例完整保留自原文档):
custom:
apiGatewayServiceProxies:
- kinesis: # partitionkey 默认取 apigateway requestid
path: /kinesis
method: post
streamName: { Ref: 'YourStream' }
cors: true
- kinesis:
path: /kinesis
method: post
partitionKey: 'hardcordedkey' # 使用静态 partitionkey
streamName: { Ref: 'YourStream' }
cors: true
- kinesis:
path: /kinesis/{myKey} # 使用路径参数
method: post
partitionKey:
pathParam: myKey
streamName: { Ref: 'YourStream' }
cors: true
- kinesis:
path: /kinesis
method: post
partitionKey:
bodyParam: data.myKey # 使用请求体参数
streamName: { Ref: 'YourStream' }
cors: true
- kinesis:
path: /kinesis
method: post
partitionKey:
queryStringParam: myKey # 使用查询字符串参数
streamName: { Ref: 'YourStream' }
cors: true
- kinesis: # 批量写入 PutRecords
path: /kinesis
method: post
action: PutRecords
streamName: { Ref: 'YourStream' }
cors: true
resources:
Resources:
YourStream:
Type: AWS::Kinesis::Stream
Properties:
ShardCount: 1
要点说明:
- 若不指定
partitionKey,Framework 默认使用 API Gateway 的requestId作为分区键(见原文档注释); partitionKey支持四种取值形态:字符串(静态值)、pathParam(从路径参数取值)、bodyParam(从请求体 JSON 取值,支持点路径如data.myKey)、queryStringParam(从查询参数取值)。schema 中以xor约束这三种动态来源只能选其一;streamName既可以是普通字符串,也可以是{ Ref: 'YourStream' }这样的 CloudFormation 内建引用;action不填时默认PutRecord,填PutRecords可批量写入。
部署完成后,可以这样发送测试请求:
curl https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/kinesis -d '{"message": "some data"}' -H 'Content-Type:application/json'
六、SQS 代理
将 API Gateway 直接连接到 SQS 队列,请求体会被作为 SendMessage 的消息体发送:
custom:
apiGatewayServiceProxies:
- sqs:
path: /sqs
method: post
queueName: { 'Fn::GetAtt': ['SQSQueue', 'QueueName'] }
cors: true
resources:
Resources:
SQSQueue:
Type: 'AWS::SQS::Queue'
从实现看(compileMethodsToSqs.js),生成的集成使用 Type: AWS 与 IntegrationHttpMethod: POST,URI 形如 arn:${AWS::Partition}:apigateway:${AWS::Region}:sqs:path//${AWS::AccountId}/${queueName},默认注入两个集成请求参数:integration.request.querystring.Action='SendMessage' 与 integration.request.querystring.MessageBody=method.request.body。测试请求:
curl https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/sqs -d '{"message": "testtest"}' -H 'Content-Type:application/json'
6.1 定制集成请求参数
如果你需要向集成请求附带额外数据(例如把调用方身份写入 SQS 消息属性),可以通过 requestParameters 直接声明 API Gateway 请求参数映射:
custom:
apiGatewayServiceProxies:
- sqs:
path: /queue
method: post
queueName: !GetAtt MyQueue.QueueName
cors: true
requestParameters:
'integration.request.querystring.MessageAttribute.1.Name': "'cognitoIdentityId'"
'integration.request.querystring.MessageAttribute.1.Value.StringValue': 'context.identity.cognitoIdentityId'
'integration.request.querystring.MessageAttribute.1.Value.DataType': "'String'"
'integration.request.querystring.MessageAttribute.2.Name': "'cognitoAuthenticationProvider'"
'integration.request.querystring.MessageAttribute.2.Value.StringValue': 'context.identity.cognitoAuthenticationProvider'
'integration.request.querystring.MessageAttribute.2.Value.DataType': "'String'"
其中单引号包裹的值(如 'cognitoIdentityId')表示字符串字面量,不带引号的(如 context.identity.cognitoIdentityId)则是 VTL 上下文表达式。另一种传递消息属性 MessageAttribute 的方式是通过请求体映射模板(见第十一节 SQS 小节)。
6.2 定制响应
内置集成默认把 SQS 集成响应归类为 200/400/500 三档(源码见 compileMethodsToSqs.js 中默认的 IntegrationResponses)。你可以用以下两种方式改写响应。
简化响应模板定制——response.template 默认按 application/json 处理,分别对应 200 / 400 / 500 状态码:
custom:
apiGatewayServiceProxies:
- sqs:
path: /queue
method: post
queueName: !GetAtt MyQueue.QueueName
cors: true
response:
template:
# `success` 用于集成响应为 200 的情况
success: |-
{ "message": "accepted" }
# `clientError` 用于集成响应为 400 的情况
clientError: |-
{ "message": "there is an error in your request" }
# `serverError` 用于集成响应为 500 的情况
serverError: |-
{ "message": "there was an error handling your request" }
完整响应定制——如果需要对集成响应做更细粒度的控制,response 可以是一个对象数组,每个对象对应一个 API Gateway 集成响应(IntegrationResponse),键名与 CloudFormation AWS::ApiGateway::Method 的 IntegrationResponse 属性一一对应(statusCode、selectionPattern、responseParameters、responseTemplates):
custom:
apiGatewayServiceProxies:
- sqs:
path: /queue
method: post
queueName: !GetAtt MyQueue.QueueName
cors: true
response:
- statusCode: 200
selectionPattern: '2\d{2}'
responseParameters: {}
responseTemplates:
application/json: |-
{ "message": "accepted" }
在源码中,当检测到 http.response.template.success 存在时走简化分支;当 http.response 为数组时走完整分支,逐个生成 IntegrationResponses(selectionPattern 缺省时回落到 statusCode,responseParameters/responseTemplates 缺省时为空对象)。
七、S3 代理
S3 代理可以把 API 端点映射为对桶内对象的读写删操作:
custom:
apiGatewayServiceProxies:
- s3:
path: /s3
method: post
action: PutObject
bucket:
Ref: S3Bucket
key: static-key.json # 使用静态 key
cors: true
- s3:
path: /s3/{myKey} # 使用路径参数
method: get
action: GetObject
bucket:
Ref: S3Bucket
key:
pathParam: myKey
cors: true
- s3:
path: /s3
method: delete
action: DeleteObject
bucket:
Ref: S3Bucket
key:
queryStringParam: key # 使用查询字符串参数
cors: true
resources:
Resources:
S3Bucket:
Type: 'AWS::S3::Bucket'
与 Kinesis 类似,key 支持静态字符串、pathParam、queryStringParam 三种形态。schema 中的约束表明:使用请求映射模板时 key 变为可选;当 requestParameters 中包含 integration.request.path.object 时 key 反而禁止设置;否则 key 必填。
测试示例:
curl https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/s3 -d '{"message": "testtest"}' -H 'Content-Type:application/json'
7.1 定制请求参数
custom:
apiGatewayServiceProxies:
- s3:
path: /s3
method: post
action: PutObject
bucket:
Ref: S3Bucket
cors: true
requestParameters:
# 若 requestParameters 中设置了 'integration.request.path.object',则应移除 key 配置
'integration.request.path.object': 'context.requestId'
'integration.request.header.cache-control': "'public, max-age=31536000, immutable'"
这里通过 context.requestId 让对象键等于请求 ID,并给集成请求写死了一个缓存控制响应头对应的请求头(注意是请求头而非响应头,实际用途是控制经 S3 返回的缓存行为时通常还需配合响应参数)。
7.2 定制请求模板
如果需要自定义请求映射模板,使用 request.template:
custom:
apiGatewayServiceProxies:
- s3:
path: /s3
method: get
action: GetObject
bucket:
Ref: S3Bucket
request:
template:
application/json: |
#set ($specialStuff = $context.request.header.x-special)
#set ($context.requestOverride.path.object = $specialStuff.replaceAll('_', '-'))
{}
该模板读取客户端传来的 x-special 请求头,将其中的下划线替换为连字符,并通过 requestOverride.path.object 改写实际访问的 S3 对象键。需要留意:如果客户端没有携带 Content-Type 请求头,API Gateway 默认按 application/json 处理并命中模板。
7.3 自定义 API Gateway Path Override
默认情况下 Path Override 为 {bucket}/{object}。通过 pathOverride 可以自定义组合路径,该参数可选,未设置时回落到默认值。Framework 会自动在 Path Override 前面加上 {bucket}/。请注意:在目前版本中 key(或 path.object)仍需设置。
双路径参数 + 固定扩展名的场景(例如按 {folder}/{file} 取对象并强制 .xml 后缀):
custom:
apiGatewayServiceProxies:
- s3:
path: /s3/{folder}/{file}
method: get
action: GetObject
pathOverride: '{folder}/{file}.xml'
bucket:
Ref: S3Bucket
cors: true
requestParameters:
# 若 requestParameters 中设置了 'integration.request.path.object',则应移除 key 配置
'integration.request.path.folder': 'method.request.path.folder'
'integration.request.path.file': 'method.request.path.file'
'integration.request.path.object': 'context.requestId'
'integration.request.header.cache-control': "'public, max-age=31536000, immutable'"
部署后 API Gateway 的 Path Override 会被设置为 {bucket}/{folder}/{file}.xml。例如访问端点 /language/en,实际拉取的是 S3 中 {bucket}/language/en.xml。
贪心路径支持更深层目录:用 {myPath+} 捕获多段路径,配合 pathOverride 直接透传并拼上扩展名:
custom:
apiGatewayServiceProxies:
- s3:
path: /s3/{myPath+}
method: get
action: GetObject
pathOverride: '{myPath}.xml'
bucket:
Ref: S3Bucket
cors: true
requestParameters:
# 若 requestParameters 中设置了 'integration.request.path.object',则应移除 key 配置
'integration.request.path.myPath': 'method.request.path.myPath'
'integration.request.path.object': 'context.requestId'
'integration.request.header.cache-control': "'public, max-age=31536000, immutable'"
这样访问 /s3/a/b/c 会被翻译成读取对象键 a/b/c.xml。
7.4 定制 S3 响应
与 SQS 相同的简化模板语法:
custom:
apiGatewayServiceProxies:
- s3:
path: /s3
method: post
action: PutObject
bucket:
Ref: S3Bucket
key: static-key.json
response:
template:
# `success` 用于集成响应为 200 的情况
success: |-
{ "message": "accepted" }
# `clientError` 用于集成响应为 400 的情况
clientError: |-
{ "message": "there is an error in your request" }
# `serverError` 用于集成响应为 500 的情况
serverError: |-
{ "message": "there was an error handling your request" }
7.5 允许二进制类型
如果端点需要返回图片等二进制内容、并希望浏览器正确识别 Content-Type,需要在 provider 层开启二进制媒体类型:
# provider.apiGateway.binaryMediaTypes
provider:
apiGateway:
binaryMediaTypes: '*/*'
八、SNS 代理
把 API 端点直连 SNS 主题发布消息:
custom:
apiGatewayServiceProxies:
- sns:
path: /sns
method: post
topicName: { 'Fn::GetAtt': ['SNSTopic', 'TopicName'] }
cors: true
resources:
Resources:
SNSTopic:
Type: AWS::SNS::Topic
测试请求:
curl https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/sns -d '{"message": "testtest"}' -H 'Content-Type:application/json'
注意 topicName 在 schema 中被约束为字符串或 { 'Fn::GetAtt': ['<ResourceId>', 'TopicName'] } 的形态。
8.1 定制 SNS 响应
简化模板(默认 application/json):
custom:
apiGatewayServiceProxies:
- sns:
path: /sns
method: post
topicName: { 'Fn::GetAtt': ['SNSTopic', 'TopicName'] }
cors: true
response:
template:
# `success` 用于集成响应为 200 的情况
success: |-
{ "message": "accepted" }
# `clientError` 用于集成响应为 400 的情况
clientError: |-
{ "message": "there is an error in your request" }
# `serverError` 用于集成响应为 500 的情况
serverError: |-
{ "message": "there was an error handling your request" }
完整响应定制(对象数组):
custom:
apiGatewayServiceProxies:
- sns:
path: /sns
method: post
topicName: { 'Fn::GetAtt': ['SNSTopic', 'TopicName'] }
cors: true
response:
- statusCode: 200
selectionPattern: '2\d{2}'
responseParameters: {}
responseTemplates:
application/json: |-
{ "message": "accepted" }
8.2 Content Handling 与 PassThrough 定制
当需要处理二进制数据时,可以在 request 对象内配置 contentHandling 与 passThrough:
custom:
apiGatewayServiceProxies:
- sns:
path: /sns
method: post
topicName: { 'Fn::GetAtt': ['SNSTopic', 'TopicName'] }
request:
contentHandling: CONVERT_TO_TEXT
passThrough: WHEN_NO_TEMPLATES
contentHandling允许值为CONVERT_TO_BINARY、CONVERT_TO_TEXT;passThrough允许值为WHEN_NO_MATCH、NEVER、WHEN_NO_TEMPLATES。
这两者分别对应 API Gateway Method Integration 的 ContentHandling 与 PassthroughBehavior 属性,schema 中同样做了枚举校验。
九、DynamoDB 代理
DynamoDB 代理当前支持 PutItem、GetItem、DeleteItem 三种操作。与前面的服务不同,它要求用 hashKey/rangeKey 显式声明分区键(哈希键)与可选的排序键来源,且必须指定 attributeType:
custom:
apiGatewayServiceProxies:
- dynamodb:
path: /dynamodb/{id}/{sort}
method: put
tableName: { Ref: 'YourTable' }
hashKey: # 以 pathParam 或 queryStringParam 作为分区键
pathParam: id
attributeType: S
rangeKey: # 若同时使用排序键则必填;支持 pathParam 或 queryStringParam
pathParam: sort
attributeType: S
action: PutItem # 指定对表的操作
condition: attribute_not_exists(Id) # 可选的 ConditionExpression
cors: true
- dynamodb:
path: /dynamodb
method: get
tableName: { Ref: 'YourTable' }
hashKey:
queryStringParam: id # 使用查询字符串参数
attributeType: S
rangeKey:
queryStringParam: sort
attributeType: S
action: GetItem
cors: true
- dynamodb:
path: /dynamodb/{id}
method: delete
tableName: { Ref: 'YourTable' }
hashKey:
pathParam: id
attributeType: S
action: DeleteItem
cors: true
resources:
Resources:
YourTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: YourTable
AttributeDefinitions:
- AttributeName: id
AttributeType: S
- AttributeName: sort
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
- AttributeName: sort
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
值得注意的两个特性:
condition为可选参数,对应 DynamoDB 的 ConditionExpression(示例attribute_not_exists(Id)可保证只在主键不存在时写入);- schema 对
hashKey/rangeKey的要求是pathParam/queryStringParam二选一(xor),且attributeType必填;表名tableName支持字符串或{ Ref: ... }。
写入测试(PUT 携带 DynamoDB 原生 attribute map):
curl -XPUT https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/dynamodb/<hashKey>/<sortkey> \
-d '{"name":{"S":"john"},"address":{"S":"xxxxx"}}' \
-H 'Content-Type:application/json'
9.1 定制 DynamoDB 响应
简化模板定制。注意原文档说明:DynamoDB 的简化模板会对 application/json 与 application/x-www-form-urlencoded 两类响应类型返回同一种结果。下面用 VTL 从集成结果中提取 Item 并原样返回:
custom:
apiGatewayServiceProxies:
- dynamodb:
path: /dynamodb
method: get
tableName: { Ref: 'YourTable' }
hashKey:
queryStringParam: id # 使用查询字符串参数
attributeType: S
rangeKey:
queryStringParam: sort
attributeType: S
action: GetItem
cors: true
response:
template:
# `success` 用于集成响应为 200 的情况
success: |-
#set($item = $input.path('$.Item')){ "Item": $item }
# `clientError` 用于集成响应为 400 的情况
clientError: |-
{ "message": "there is an error in your request" }
# `serverError` 用于集成响应为 500 的情况
serverError: |-
{ "message": "there was an error handling your request" }
完整响应定制:
custom:
apiGatewayServiceProxies:
- dynamodb:
path: /dynamodb
method: get
tableName: { Ref: 'YourTable' }
hashKey:
queryStringParam: id # 使用查询字符串参数
attributeType: S
rangeKey:
queryStringParam: sort
attributeType: S
action: GetItem
cors: true
response:
- statusCode: 200
selectionPattern: '2\d{2}'
responseParameters: {}
responseTemplates:
application/json: |-
#set($item = $input.path('$.Item')){ "Item": $item }
十、EventBridge 代理
EventBridge 代理让 API 端点直接向事件总线投递事件。source 与 detailType 都支持字符串或从请求中提取,detail 默认取 POST 请求体:
custom:
apiGatewayServiceProxies:
- eventbridge: # source 与 detailType 为硬编码;detail 默认取 POST body
path: /eventbridge
method: post
source: 'hardcoded_source'
detailType: 'hardcoded_detailType'
eventBusName: { Ref: 'YourBusName' }
cors: true
- eventbridge: # source 与 detailType 作为路径参数
path: /eventbridge/{detailTypeKey}/{sourceKey}
method: post
detailType:
pathParam: detailTypeKey
source:
pathParam: sourceKey
eventBusName: { Ref: 'YourBusName' }
cors: true
- eventbridge: # source、detail、detailType 都来自请求体
path: /eventbridge/{detailTypeKey}/{sourceKey}
method: post
detailType:
bodyParam: data.detailType
source:
bodyParam: data.source
detail:
bodyParam: data.detail
eventBusName: { Ref: 'YourBusName' }
cors: true
resources:
Resources:
YourBus:
Type: AWS::Events::EventBus
Properties:
Name: YourEventBus
EventBridge 的事件字段来源在 schema 中做了差异化约束:source/detailType 支持字符串或 pathParam/queryStringParam/bodyParam 三选一,而 detail 仅支持 bodyParam(其结构必须是事件负载本身)。
测试请求:
curl https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/eventbridge -d '{"message": "some data"}' -H 'Content-Type:application/json'
十一、通用 API Gateway 特性
11.1 开启 CORS
为 HTTP 端点开启 CORS 最简单的写法:
custom:
apiGatewayServiceProxies:
- kinesis:
path: /kinesis
method: post
streamName: { Ref: 'YourStream' }
cors: true
cors: true 等价于如下默认配置:
custom:
apiGatewayServiceProxies:
- kinesis:
path: /kinesis
method: post
streamName: { Ref: 'YourStream' }
cors:
origin: '*'
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
allowCredentials: false
配置 cors 属性后,预检(preflight)响应中会设置 Access-Control-Allow-Origin、Access-Control-Allow-Headers、Access-Control-Allow-Methods、Access-Control-Allow-Credentials 头。源码 utils.js 的 addCors() 会把 origin(若配置 origins 数组则拼接为逗号分隔字符串)逐一写入每条集成响应的 Access-Control-Allow-Origin 响应参数。
需要控制预检结果缓存时,使用 maxAge:
custom:
apiGatewayServiceProxies:
- kinesis:
path: /kinesis
method: post
streamName: { Ref: 'YourStream' }
cors:
origin: '*'
maxAge: 86400
如果 API Gateway 前有 CloudFront 等 CDN,可以通过 cacheControl 让 OPTIONS 预检请求可被缓存,减少额外一跳:
custom:
apiGatewayServiceProxies:
- kinesis:
path: /kinesis
method: post
streamName: { Ref: 'YourStream' }
cors:
origin: '*'
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
allowCredentials: false
cacheControl: 'max-age=600, s-maxage=600, proxy-revalidate' # 浏览器与代理均缓存 10 分钟,且禁止代理返回过期内容
schema 对 cors 对象的字段(headers、origin、origins、methods、maxAge(最小 1)、cacheControl、allowCredentials)做了校验,并且 origin 与 origins 二选一,同时出现会直接报错。
11.2 添加鉴权
支持传入任意 API Gateway 鉴权类型:
custom:
apiGatewayServiceProxies:
- sqs:
path: /sqs
method: post
queueName: { 'Fn::GetAtt': ['SQSQueue', 'QueueName'] }
cors: true
# 可选 - 默认 'NONE'
authorizationType: 'AWS_IAM' # 可选值 ['NONE', 'AWS_IAM', 'CUSTOM', 'COGNITO_USER_POOLS']
# 使用 'CUSTOM' 类型时必须指定 authorizerId
# authorizerId: { Ref: 'AuthorizerLogicalId' }
# 使用 'COGNITO_USER_POOLS' 类型时可指定 authorizationScopes
# authorizationScopes: ['scope1','scope2']
resources:
Resources:
SQSQueue:
Type: 'AWS::SQS::Queue'
schema 中的条件逻辑更严谨:一旦设置了 authorizerId,则 authorizationType 只能是 CUSTOM 或 COGNITO_USER_POOLS;设置了 authorizationScopes 时则必须为 COGNITO_USER_POOLS。
11.3 启用 API Key 认证
通过 private 标志要求客户端提交合法的 API Key(与 Framework 中 HTTP 事件的语法一致):
custom:
apiGatewayServiceProxies:
- sqs:
path: /sqs
method: post
queueName: { 'Fn::GetAtt': ['SQSQueue', 'QueueName'] }
cors: true
private: true
resources:
Resources:
SQSQueue:
Type: 'AWS::SQS::Queue'
在 compileMethodsToSqs.js 生成的 AWS::ApiGateway::Method 中可以看到,private 被映射为 ApiKeyRequired: Boolean(http.private)。更多 API Key 的用法可参考 API Gateway 事件文档。
11.4 使用自定义 IAM Role
默认情况下,Framework 会为每种配置了代理的服务类型生成一个具备所需权限的角色。若需使用自己的角色,设置 roleArn:
custom:
apiGatewayServiceProxies:
- sqs:
path: /sqs
method: post
queueName: { 'Fn::GetAtt': ['SQSQueue', 'QueueName'] }
cors: true
roleArn: # 可选。未配置时会创建默认角色
Fn::GetAtt: [CustomS3Role, Arn]
resources:
Resources:
SQSQueue:
Type: 'AWS::SQS::Queue'
CustomS3Role:
# 自定义 Role 定义
Type: 'AWS::IAM::Role'
实现上,集成默认使用形如 { 'Fn::GetAtt': ['ApigatewayToSqsRole', 'Arn'] } 的自动生成角色作为 Credentials,只有当显式给出 roleArn 时才覆盖(见 compileMethodsToSqs.js)。同时 utils.js 的 shouldCreateDefaultRole() 会判断:只要同类代理中存在任何一个未指定 roleArn,就仍需创建默认角色。roleArn 在 schema 中被约束为字符串或 { 'Fn::GetAtt': ['<ResourceId>', 'Arn'] }。
11.5 定制 API Gateway 方法接受的参数
通过 acceptParameters 声明方法接受哪些请求参数,配合 requestParameters 把方法参数映射进集成请求。常见场景是把自定义数据附加到集成请求。下面的例子把客户端 Custom-Header 请求头的值写入 SQS 消息属性:
custom:
apiGatewayServiceProxies:
- sqs:
path: /sqs
method: post
queueName: { 'Fn::GetAtt': ['SqsQueue', 'QueueName'] }
cors: true
acceptParameters:
'method.request.header.Custom-Header': true
requestParameters:
'integration.request.querystring.MessageAttribute.1.Name': "'custom-Header'"
'integration.request.querystring.MessageAttribute.1.Value.StringValue': 'method.request.header.Custom-Header'
'integration.request.querystring.MessageAttribute.1.Value.DataType': "'String'"
resources:
Resources:
SqsQueue:
Type: 'AWS::SQS::Queue'
这样任何发布到 SQS 的消息都会携带名为 Custom-Header 的消息属性,值为客户端传入的请求头。acceptParameters 生成的正是 Method 资源的 RequestParameters(值必须为布尔),最终体现在 AWS::ApiGateway::Method.Properties.RequestParameters 上。
十二、自定义请求体映射模板(按服务展开)
如果要新增 Content-Type 或改写默认的请求映射模板,可以在 request.template 下按媒体类型配置 VTL 模板。模板必须返回该服务约定的合法请求格式字符串(Kinesis/SNS 需返回表单串或 JSON,SQS 需返回 application/x-www-form-urlencoded 风格表单)。
12.1 Kinesis 请求模板
下面示例(源自社区实践)演示了如何用 Fn::Sub 注入流 ARN,把请求体 base64 编码为 Kinesis Data 并透传自定义消息 ID 作为 PartitionKey:
# 使用 Fn::Sub 需要该插件
plugins:
- serverless-cloudformation-sub-variables
custom:
apiGatewayServiceProxies:
- kinesis:
path: /kinesis
method: post
streamName: { Ref: 'MyStream' }
request:
template:
text/plain:
Fn::Sub:
- |
#set($msgBody = $util.parseJson($input.body))
#set($msgId = $msgBody.MessageId)
{
"Data": "$util.base64Encode($input.body)",
"PartitionKey": "$msgId",
"StreamName": "#{MyStreamArn}"
}
- MyStreamArn:
Fn::GetAtt: [MyStream, Arn]
关键点:映射模板最终必须返回一段合法的
application/json字符串。
12.2 SQS 请求模板
SQS 的特殊之处在于:自定义请求模板要求请求体为 application/x-www-form-urlencoded 风格。Framework 会替你设置 Content-Type 头为 application/x-www-form-urlencoded,但 API Gateway 仍会在 application/json 这个请求模板类型下查找模板,因此你的模板也要写在该键下:
custom:
apiGatewayServiceProxies:
- sqs:
path: /{version}/event/receiver
method: post
queueName: { 'Fn::GetAtt': ['SqsQueue', 'QueueName'] }
request:
template:
application/json: |-
#set ($body = $util.parseJson($input.body))
Action=SendMessage##
&MessageGroupId=$util.urlEncode($body.event_type)##
&MessageDeduplicationId=$util.urlEncode($body.event_id)##
&MessageAttribute.1.Name=$util.urlEncode("X-Custom-Signature")##
&MessageAttribute.1.Value.DataType=String##
&MessageAttribute.1.Value.StringValue=$util.urlEncode($input.params("X-Custom-Signature"))##
&MessageBody=$util.urlEncode($input.body)
使用自定义 SQS 请求模板需要注意以下几点:
- 每行末尾的
##是空注释。在 VTL 中注释会把行尾换行吞掉,使 API Gateway 把模板所有行读成一行,保证表单串拼接正确; - 自定义模板必须自行设置
Action与MessageBody,内置集成不会替你补这两个参数; - 使用自定义请求体后,
PassthroughBehavior会被自动置为NEVER,客户端发来的请求头不再自动透传给 SQS,需要你在模板中显式搬运; - 小心不要把额外的
requestParameters混进 SQS 端点:你可能覆盖掉integration.request.header.Content-Type,导致模板无法被正确解析;requestParameters中自定义的查询参数在模板模式下会被忽略(源码中会过滤掉所有integration.request.querystring.*的映射)。通常仅在确实需要加工请求体时才使用请求模板。
从 compileMethodsToSqs.js 可以看到这一分支逻辑:没有自定义模板时注入默认的 Action/MessageBody 两个查询参数;存在自定义模板时设置 PassthroughBehavior: 'NEVER',强制 integration.request.header.Content-Type 为 application/x-www-form-urlencoded,并过滤掉所有 querystring 型 requestParameters。
12.3 SNS 请求模板
SNS 与 Kinesis 类似,通过 Fn::Sub 注入主题 ARN 拼接 Publish 表单:
# 使用 Fn::Sub 需要该插件
plugins:
- serverless-cloudformation-sub-variables
custom:
apiGatewayServiceProxies:
- kinesis:
path: /sns
method: post
topicName: { 'Fn::GetAtt': ['SNSTopic', 'TopicName'] }
request:
template:
application/json:
Fn::Sub:
- "Action=Publish&Message=$util.urlEncode('This is a fixed message')&TopicArn=$util.urlEncode('#{MyTopicArn}')"
- MyTopicArn: { Ref: MyTopic }
关键点:SNS 的映射模板最终必须返回一段合法的
application/x-www-form-urlencoded字符串。
十三、自定义响应体映射模板
除了各服务章节中见到的“简化 / 完整响应定制”,你还可以直接为 success(成功)、serverError(5xx)、clientError(4xx)分别提供映射模板:
模板必须是 JSON 格式。若某个模板未提供,对应的集成响应将原样返回给客户端。
以 Kinesis 为例:
custom:
apiGatewayServiceProxies:
- kinesis:
path: /kinesis
method: post
streamName: { Ref: 'MyStream' }
response:
template:
success: |
{
"success": true
}
serverError: |
{
"success": false,
"errorMessage": "Server Error"
}
clientError: |
{
"success": false,
"errorMessage": "Client Error"
}
这组模板把三类原始集成响应统一收敛为结构化 JSON,便于客户端做统一错误处理。schema 中 response.template 只接受 success/clientError/serverError 三个键,其余键会被校验拒绝。
十四、部署后的输出
部署完成后,若存在代理配置,Framework 会在 after:deploy:deploy 钩子中调用 display()(见 index.js),从已部署栈信息中取出 RestApiId 并打印出类似如下的端点清单:
Serverless API Gateway Service Proxy Outputs
endpoints:
POST - https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/kinesis
每个代理服务的 method 会转为大写并与实际部署的 stage URL 拼接展示,方便你直接复制调用。
十五、总结
API Gateway AWS 服务代理把“API 入口 → 消息/存储服务”的经典无服务器链路压缩为一段声明式 YAML:custom.apiGatewayServiceProxies 数组中的每个元素就是一个免 Lambda 的 REST 集成。你可以用一套统一语法触达六类 AWS 服务,并针对每个端点精细控制 CORS、鉴权方式、API Key 要求、执行 IAM 角色、请求/响应映射模板、S3 Path Override、DynamoDB 条件表达式等细节。
结合仓库源码(入口 index.js、校验 schema.js、工具 utils.js、以及 package/{kinesis,sqs,s3,sns,dynamodb,eventbridge} 目录下的服务级编译实现)可以看出其设计:配置即契约——所有字段在上层经过 Joi 严格校验,随后在打包阶段被编译为标准的 AWS::ApiGateway::Method 等 CloudFormation 资源,部署后通过 serverless deploy 的编排原样落到 AWS 上。若你还想了解这些代理方法在 REST API 事件体系中的位置,可进一步阅读 API Gateway 事件配置 与 serverless.yml 参考。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00