首页
/ Serverless Framework API Gateway AWS 服务代理集成(Service Proxy)配置实战指南

Serverless Framework API Gateway AWS 服务代理集成(Service Proxy)配置实战指南

2026-09-08 09:37:55作者:曹令琨Iris

本文基于开源仓库 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 动作(如 PutRecordSendMessagePutObjectPublishPutItemPutEvents);
  • 使用 IAM 执行角色(integration credentials)完成服务调用并回传响应。

这一模式的收益非常直观:没有函数冷启动、没有按毫秒计的 Lambda 计费、基础设施更少。Serverless Framework 将其封装为声明式配置 custom.apiGatewayServiceProxies,在运行 serverless deploy 时自动生成对应的 AWS::ApiGateway::MethodAWS::ApiGateway::Resource、IAM 角色与 Deployment 等 CloudFormation 资源。

从源码入口 index.js 可以确认该插件的工作时序:它注册了 package:compileEventsafter:deploy:deploy 两个钩子,前者在打包阶段校验并编译代理资源,后者在部署结束后打印生成的端点信息。同时 shouldLoad() 通过读取 serverless.service.custom.apiGatewayServiceProxies 决定是否加载本能力。

二、来源与从社区插件迁移

该能力最初来自社区插件 serverless-apigateway-service-proxy(由 serverless-operations 团队及社区贡献者维护),如今已作为 Serverless Framework 的内置能力开箱即用,无需额外安装任何东西

如果你正在从社区插件迁移:

  1. serverless.ymlplugins 段移除该插件;
  2. 从项目依赖中卸载对应 npm 包;
  3. 保留现有的 custom.apiGatewayServiceProxies 配置——内置集成会继续读取并遵循这一配置结构。

在源码文件头部的注释 index.js 中可以看到其衍生于 serverless-apigateway-service-proxy(Copyright 2019 Takahiro Horike and contributors,MIT License),完整许可文本位于仓库根目录的 THIRD_PARTY_LICENSES

三、当前支持的服务清单

根据仓库校验 schema schema.jsallowedProxies 数组,内置集成当前支持六类代理:

服务 配置键 支持的 API 动作
Kinesis Streams kinesis PutRecord(默认)、PutRecords
SQS sqs SendMessage
S3 s3 GetObjectPutObjectDeleteObject
SNS sns Publish
DynamoDB dynamodb PutItemGetItemDeleteItem
EventBridge eventbridge 自定义事件发布

这些动作枚举同样在 schema.js 中逐项校验。例如 Kinesis 的 action 仅允许 PutRecord / PutRecords,DynamoDB 仅允许三种 Item 级操作,S3 仅允许三个 Object 级操作。

四、使用方式与整体配置结构

所有服务代理统一声明在 serverless.ymlcustom.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:包含 templatecontentHandlingpassThrough 的请求定制对象;
  • 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: AWSIntegrationHttpMethod: 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::MethodIntegrationResponse 属性一一对应(statusCodeselectionPatternresponseParametersresponseTemplates):

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 为数组时走完整分支,逐个生成 IntegrationResponsesselectionPattern 缺省时回落到 statusCoderesponseParameters/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 支持静态字符串、pathParamqueryStringParam 三种形态。schema 中的约束表明:使用请求映射模板时 key 变为可选;当 requestParameters 中包含 integration.request.path.objectkey 反而禁止设置;否则 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 对象内配置 contentHandlingpassThrough

custom:
  apiGatewayServiceProxies:
    - sns:
        path: /sns
        method: post
        topicName: { 'Fn::GetAtt': ['SNSTopic', 'TopicName'] }
        request:
          contentHandling: CONVERT_TO_TEXT
          passThrough: WHEN_NO_TEMPLATES
  • contentHandling 允许值为 CONVERT_TO_BINARYCONVERT_TO_TEXT
  • passThrough 允许值为 WHEN_NO_MATCHNEVERWHEN_NO_TEMPLATES

这两者分别对应 API Gateway Method Integration 的 ContentHandling 与 PassthroughBehavior 属性,schema 中同样做了枚举校验。

九、DynamoDB 代理

DynamoDB 代理当前支持 PutItemGetItemDeleteItem 三种操作。与前面的服务不同,它要求用 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/jsonapplication/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 端点直接向事件总线投递事件。sourcedetailType 都支持字符串或从请求中提取,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-OriginAccess-Control-Allow-HeadersAccess-Control-Allow-MethodsAccess-Control-Allow-Credentials 头。源码 utils.jsaddCors() 会把 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 对象的字段(headersoriginoriginsmethodsmaxAge(最小 1)、cacheControlallowCredentials)做了校验,并且 originorigins 二选一,同时出现会直接报错。

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 只能是 CUSTOMCOGNITO_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.jsshouldCreateDefaultRole() 会判断:只要同类代理中存在任何一个未指定 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 请求模板需要注意以下几点:

  1. 每行末尾的 ## 是空注释。在 VTL 中注释会把行尾换行吞掉,使 API Gateway 把模板所有行读成一行,保证表单串拼接正确;
  2. 自定义模板必须自行设置 ActionMessageBody,内置集成不会替你补这两个参数;
  3. 使用自定义请求体后,PassthroughBehavior 会被自动置为 NEVER,客户端发来的请求头不再自动透传给 SQS,需要你在模板中显式搬运;
  4. 小心不要把额外的 requestParameters 混进 SQS 端点:你可能覆盖掉 integration.request.header.Content-Type,导致模板无法被正确解析;requestParameters 中自定义的查询参数在模板模式下会被忽略(源码中会过滤掉所有 integration.request.querystring.* 的映射)。通常仅在确实需要加工请求体时才使用请求模板。

compileMethodsToSqs.js 可以看到这一分支逻辑:没有自定义模板时注入默认的 Action/MessageBody 两个查询参数;存在自定义模板时设置 PassthroughBehavior: 'NEVER',强制 integration.request.header.Content-Typeapplication/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 参考

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391