如何使用 AWS_PROXY 模式通过 API Gateway 和 CloudFront 通过 AWS Lambda 返回二进制内容?

gen*_*ood 6 amazon-web-services amazon-cloudfront aws-cloudformation aws-lambda aws-api-gateway

如何AWS_PROXY使用 CloudFormation使用集成类型预置 AWS API 网关,以 CloudFront 为前端(以便我可以进行 HTTP 到 HTTPS 重定向)并由 AWS Lambda 函数提供支持?

下面是一个 CloudFormation 模板,显示了我尝试过的内容。这包括

  • 一个简单的 Lambda 函数,它在模式下返回 Lambda 的预期输出格式AWS_PROXY
    • 这已isBase64Encoded设置为 True。
  • 一个AWS::ApiGateway::RestApiCloudFormation 资源,其中BinaryMediaTypes包含一个值为 的属性*~1*

我已经阅读了这篇 AWS 论坛帖子、AWS_PROXY 和二进制响应,但还没有弄清楚我做错了什么。论坛帖子包括发布有关AWS_PROXY模式以及其他模式的人,因此有点令人困惑。

这AWS文档页面,Support Binary Payloads in API Gateway是的,我相信,谈论比其他模式AWS_PROXY,因为它谈论设置IntegrationResponses需要使用属性的StatusCode其匹配的MethodResponse的StatusCode。

这是一个展示问题的 CloudFormation 模板。您可以通过这些步骤重现它

  1. 为您账户中现有 Route53 区域中的 DNS 域名提供 ACM 证书
  2. 将域名、区域名称(以“.”字符结尾)和 ACM ARN 作为参数提供给 CloudFormation 堆栈
  3. 使用下面的模板启动 CloudFormation 堆栈(因为它使用 CloudFront,这可能需要 30 分钟)
  4. curl API 网关的 URL

如果这正常工作,您将返回二进制 png HTTP 响应,而不是返回 base64 响应。

AWSTemplateFormatVersion: 2010-09-09
Description: Test binary responses with AWS_PROXY mode
Parameters:
  CustomDomainName:
    Type: String
    Description: The custom domain name to use for the API
    Default: ''
    # AWS::ApiGateway::DomainName can not contain any uppercase characters
    AllowedPattern: '^[^A-Z]*$'
    ConstraintDescription: must not contain any uppercase characters
  DomainNameZone:
    Type: String
    Description: The Route53 DNS zone containing the custom domain name
    Default: ''
  CertificateArn:
    Type: String
    Description: The ARN of the AWS ACM Certificate for your custom domain name
    Default: ''
Resources:
  TestFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: AllowLambdaLogging
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: '*'
  TestFunction:
    Type: AWS::Lambda::Function
    Properties:
      Description: Test Function
      Code:
        ZipFile: |
          def lambda_handler(event, context):
            body = 'iVBORw0KGgoAAAANSUhEUgAAABEAAAAKCAYAAABSfLWiAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAA0gAAANIBBp0MHQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHgSURBVCiRlY47aFNxGEfP97/p05SWYhXfEHMjNZobuChYk1iwUCKKiqSjj0XpIM46uDgUQdxqk0lUHJwsiEPtoEmtgxhMIx2StFJBhA4tOCTVPO7n0C5uesbDj8NPAEJO4oXCLqDHU3PbktYJhM/lwty07SRmEHlQKWRn7Uh8VlRvqDFpoEdgo7yQO+0DqP80V1ZW3v0KOcMxI95dMFOqnD8YGfoAckCUZMCNlWhKvxoGxaNWLuZGAQUQwNhOfEJFjhqPugo7u7RzZEN+50HvgO4R5KKKPkVlb9VXfbit5X+Cp2FBn5WLc/dNyBkeAkksFXJnWurdA6xi8U0VqIBc89R6q0hVPLmgtF7+yOdrlUI2ZdXb4hhzKRQ95frENL6qZ+2zo/FHqHQAA6RSlpZWp0WYWC5mF4NO4j3C1aWF+UXbiZ0VZKxFo4pitTcbywAE3JHeQDRhAxIOh9vZxITDw34A13Xbdrtu95Yn4Mb2HzoSjwSDyQ4A0SlOyjjz/Af6mE7q3AQGgW4D1DTDc01zWTP0/lPlG02ULxgmUfoEQCfx4+MWMI5SQvi0NVpDWcejC6EfsBGOA4cR0vh4RZNz8tfNzVgSYRTlGLADGADWge/AR4QZ+ngtY9Q1w3aus/YHPCW0c1bW92YAAAAASUVORK5CYII='
            return {
              'headers': {'Content-Type': 'image/png'},
              'statusCode': 200,
              'isBase64Encoded': True,
              'body': body}
      Handler: index.lambda_handler
      Runtime: python3.7
      Role: !GetAtt TestFunctionRole.Arn
      Timeout: 900
  TestFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      # Let's hope that the Lambda function doesn't execute before this LogGroup
      # resource is created, creating the LogGroup with no expiration and
      # preventing this resource from creating
      LogGroupName: !Join [ '/', ['/aws/lambda', !Ref 'TestFunction' ] ]
      RetentionInDays: 1
  TestRoute53RecordSet:
    Type: AWS::Route53::RecordSet
    Properties:
      AliasTarget:
        DNSName: !GetAtt TestCloudFrontDistribution.DomainName
        HostedZoneId: Z2FDTNDATAQYW2  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-aliastarget-1.html
      Comment: Bind the custom domain name to the Test CloudFront fronted API Gateway
      HostedZoneName: !Ref DomainNameZone
      Name: !Ref CustomDomainName
      Type: A
  TestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: Test
      BinaryMediaTypes:
        - '*~1*'
      Description: Test API
      FailOnWarnings: true
      EndpointConfiguration:
        Types:
          - REGIONAL
  TestApiGatewayDomainName:
    # The ApiGateway requires a custom domain name, despite sitting behind
    # CloudFront. This is because we want to pass all ( * ) HTTP headers
    # through CloudFront and onto API Gateway. If we didn't set a custom domain
    # name on the API Gateway, the "Host" header passed through from CloudFront
    # to API Gateway would be for the custom domain, but API Gateway, which uses
    # SNI, wouldn't know which TLS certificate to use in the handshake because
    # API Gateway would have no record of that Host header. This would result in
    # API Gateway being unable to setup a TLS connection with the inbound
    # CloudFront connection attempt, API Gateway writing no logs about this
    # fact, and CloudFront returning to the user an error of
    # {"message":"Forbidden"}
    # If we weren't passing the "Host" header from CloudFront to API Gateway
    # this resource wouldn't be needed
    Type: AWS::ApiGateway::DomainName
    Properties:
      # Uppercase letters are not supported in DomainName
      DomainName: !Ref CustomDomainName
      EndpointConfiguration:
        Types:
          - REGIONAL
      RegionalCertificateArn: !Ref CertificateArn
      SecurityPolicy: TLS_1_2
  TestBasePathMapping:
    Type: AWS::ApiGateway::BasePathMapping
    Properties:
      # BasePath:   # Not specifying this so that we have no base path
      DomainName: !Ref TestApiGatewayDomainName
      RestApiId: !Ref TestApi
      Stage: !Ref TestApiStage
  TestLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      FunctionName: !GetAtt TestFunction.Arn
      Principal: apigateway.amazonaws.com
      SourceArn: !Join [ '', [ 'arn:aws:execute-api:', !Ref 'AWS::Region', ':', !Ref 'AWS::AccountId', ':', !Ref 'TestApi', '/*/*' ] ]
  TestApiStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      DeploymentId: !Ref TestApiDeployment
      MethodSettings:
        - DataTraceEnabled: true
          HttpMethod: '*'
          ResourcePath: /*
      RestApiId: !Ref TestApi
  TestApiDeployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn:
      - TestRequest
    Properties:
      RestApiId: !Ref TestApi
      StageName: DummyStage
      # Deployment with an Empty Embedded Stage
      # The following instructional text is no longer present in the AWS
      # documentation for AWS::ApiGateway::Deployment StageName and it's not
      # clear if it still applies.
      #
      # "Note This property is required by API Gateway. We recommend that you
      # specify a name using any value (see Examples) and that you don’t use
      # this stage. We recommend not using this stage because it is tied to
      # this deployment, which means you can’t delete one without deleting the
      # other. For example, if you delete this deployment, API Gateway also
      # deletes this stage, which you might want to keep. Instead, use the
      # AWS::ApiGateway::Stage resource to create and associate a stage with
      # this deployment."
  TestResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref TestApi
      ParentId: !GetAtt TestApi.RootResourceId
      PathPart: '{proxy+}'
  TestRequest:
    DependsOn: TestLambdaPermission
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      HttpMethod: GET
      Integration:
        Type: AWS_PROXY
        # IntegrationHttpMethod is POST regardless of the HttpMethod for this resource
        # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#set-up-lambda-proxy-integration-using-cli
        # "For Lambda integrations, you must use the HTTP method of POST for the
        # integration request, according to the specification of the Lambda service
        # action for function invocations."
        IntegrationHttpMethod: POST
        Uri: !Join [ '', [ 'arn:aws:apigateway:', !Ref 'AWS::Region', ':lambda:path/2015-03-31/functions/', !GetAtt 'TestFunction.Arn', '/invocations' ] ]
      ResourceId: !Ref TestResource
      RestApiId: !Ref TestApi
  TestPOSTRequest:
    DependsOn: TestLambdaPermission
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      HttpMethod: POST
      Integration:
        Type: AWS_PROXY
        # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#set-up-lambda-proxy-integration-using-cli
        # "For Lambda integrations, you must use the HTTP method of POST for the
        # integration request, according to the specification of the Lambda service
        # action for function invocations."
        IntegrationHttpMethod: POST
        Uri: !Join [ '', [ 'arn:aws:apigateway:', !Ref 'AWS::Region', ':lambda:path/2015-03-31/functions/', !GetAtt 'TestFunction.Arn', '/invocations' ] ]
      ResourceId: !Ref TestResource
      RestApiId: !Ref TestApi
  TestRootRequest:
    # This resource is necessary to get API Gateway to respond to requests for the '/' path
    # Without it API Gateway will respond to requests for '/' with the error
    # {"message":"Missing Authentication Token"}
    # https://stackoverflow.com/q/46578615/168874
    # https://stackoverflow.com/q/52909329/168874
    DependsOn: TestLambdaPermission
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      HttpMethod: GET
      Integration:
        Type: AWS_PROXY
        # IntegrationHttpMethod is POST regardless of the HttpMethod for this resource
        # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#set-up-lambda-proxy-integration-using-cli
        # "For Lambda integrations, you must use the HTTP method of POST for the
        # integration request, according to the specification of the Lambda service
        # action for function invocations."
        IntegrationHttpMethod: POST
        Uri: !Join [ '', [ 'arn:aws:apigateway:', !Ref 'AWS::Region', ':lambda:path/2015-03-31/functions/', !GetAtt 'TestFunction.Arn', '/invocations' ] ]
      # ResourceId must use the RootResourceId attribute of the AWS::ApiGateway::RestApi
      # https://stackoverflow.com/a/56121914/168874
      ResourceId: !GetAtt TestApi.RootResourceId
      RestApiId: !Ref TestApi
  TestCloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Comment: !Join [ ':', [!Ref 'AWS::StackName', 'Test']]
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
            - POST
            - DELETE
            - OPTIONS
            - PUT
            - PATCH
          Compress: true
          DefaultTTL: 0
          MinTTL: 0
          MaxTTL: 0
          ForwardedValues:
            Cookies:
              Forward: all
            QueryString: true
            Headers:
              - '*'
          TargetOriginId: TestCloudFrontOriginId
          ViewerProtocolPolicy: redirect-to-https
        # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-defaultrootobject
        DefaultRootObject: ''  # "If you don't want to specify a default root object when you create a distribution, include an empty DefaultRootObject element."
        Enabled: true
        Aliases:
          - !Ref CustomDomainName
        HttpVersion: http2
        IPV6Enabled: true
        #Logging:
        #  Logging
        Origins:
          - CustomOriginConfig:
              OriginProtocolPolicy: https-only
              OriginSSLProtocols:
                - TLSv1.2
            DomainName: !GetAtt TestApiGatewayDomainName.RegionalDomainName
            Id: TestCloudFrontOriginId
            # OriginPath: !Join [ '', [ '/', !Ref 'TestApiStage' ] ]
        PriceClass: PriceClass_100  # US, Canada, Europe, Israel
        ViewerCertificate:
          AcmCertificateArn: !Ref CertificateArn
          MinimumProtocolVersion: TLSv1.2_2018
          SslSupportMethod: sni-only
Run Code Online (Sandbox Code Playgroud)

gen*_*ood 3

我联系了AWS Support,经过多次来回发现问题出在AWS文档中。

\n\n

AWS::ApiGateway::RestApiCloudFormation 资源类型的文档页面的说明不正确

\n\n
\n

斜杠必须用 ~1 转义。例如,image/png 在 BinaryMediaTypes 列表中将是 image~1png。

\n
\n\n

事实证明这不是真的,您应该在该BinaryMediaTypes字段中输入的值*/*不是*~1*。这使得该字段看起来像这样

\n\n
Resources:\n  TestApi:\n    Type: AWS::ApiGateway::RestApi\n    Properties:\n      Name: Test\n      BinaryMediaTypes:\n        - \'*/*\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此,通过对问题中的模板进行更改,生成的堆栈可以正确提供二进制资源。

\n\n

我已确认此 AWS 文档页面Support Binary Payloads in API Gateway确实讨论了除我的问题之外的模式AWS_PROXY,并且不适用于我的问题。

\n\n

我已确认有效的修复模板是这样的

\n\n
AWSTemplateFormatVersion: 2010-09-09\nDescription: Test binary responses with AWS_PROXY mode\nParameters:\n  CustomDomainName:\n    Type: String\n    Description: The custom domain name to use for the API\n    Default: \'\'\n    # AWS::ApiGateway::DomainName can not contain any uppercase characters\n    AllowedPattern: \'^[^A-Z]*$\'\n    ConstraintDescription: must not contain any uppercase characters\n  DomainNameZone:\n    Type: String\n    Description: The Route53 DNS zone containing the custom domain name\n    Default: \'\'\n  CertificateArn:\n    Type: String\n    Description: The ARN of the AWS ACM Certificate for your custom domain name\n    Default: \'\'\nResources:\n  TestFunctionRole:\n    Type: AWS::IAM::Role\n    Properties:\n      AssumeRolePolicyDocument:\n        Version: 2012-10-17\n        Statement:\n          - Effect: Allow\n            Principal:\n              Service:\n                - lambda.amazonaws.com\n            Action:\n              - sts:AssumeRole\n      Policies:\n        - PolicyName: AllowLambdaLogging\n          PolicyDocument:\n            Version: 2012-10-17\n            Statement:\n              - Effect: Allow\n                Action:\n                  - logs:CreateLogGroup\n                  - logs:CreateLogStream\n                  - logs:PutLogEvents\n                Resource: \'*\'\n  TestFunction:\n    Type: AWS::Lambda::Function\n    Properties:\n      Description: Test Function\n      Code:\n        ZipFile: |\n          def lambda_handler(event, context):\n            body = \'iVBORw0KGgoAAAANSUhEUgAAABEAAAAKCAYAAABSfLWiAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAA0gAAANIBBp0MHQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHgSURBVCiRlY47aFNxGEfP97/p05SWYhXfEHMjNZobuChYk1iwUCKKiqSjj0XpIM46uDgUQdxqk0lUHJwsiEPtoEmtgxhMIx2StFJBhA4tOCTVPO7n0C5uesbDj8NPAEJO4oXCLqDHU3PbktYJhM/lwty07SRmEHlQKWRn7Uh8VlRvqDFpoEdgo7yQO+0DqP80V1ZW3v0KOcMxI95dMFOqnD8YGfoAckCUZMCNlWhKvxoGxaNWLuZGAQUQwNhOfEJFjhqPugo7u7RzZEN+50HvgO4R5KKKPkVlb9VXfbit5X+Cp2FBn5WLc/dNyBkeAkksFXJnWurdA6xi8U0VqIBc89R6q0hVPLmgtF7+yOdrlUI2ZdXb4hhzKRQ95frENL6qZ+2zo/FHqHQAA6RSlpZWp0WYWC5mF4NO4j3C1aWF+UXbiZ0VZKxFo4pitTcbywAE3JHeQDRhAxIOh9vZxITDw34A13Xbdrtu95Yn4Mb2HzoSjwSDyQ4A0SlOyjjz/Af6mE7q3AQGgW4D1DTDc01zWTP0/lPlG02ULxgmUfoEQCfx4+MWMI5SQvi0NVpDWcejC6EfsBGOA4cR0vh4RZNz8tfNzVgSYRTlGLADGADWge/AR4QZ+ngtY9Q1w3aus/YHPCW0c1bW92YAAAAASUVORK5CYII=\'\n            return {\n              \'headers\': {\'Content-Type\': \'image/png\'},\n              \'statusCode\': 200,\n              \'isBase64Encoded\': True,\n              \'body\': body}\n      Handler: index.lambda_handler\n      Runtime: python3.7\n      Role: !GetAtt TestFunctionRole.Arn\n      Timeout: 900\n  TestFunctionLogGroup:\n    Type: AWS::Logs::LogGroup\n    Properties:\n      # Let\'s hope that the Lambda function doesn\'t execute before this LogGroup\n      # resource is created, creating the LogGroup with no expiration and\n      # preventing this resource from creating\n      LogGroupName: !Join [ \'/\', [\'/aws/lambda\', !Ref \'TestFunction\' ] ]\n      RetentionInDays: 1\n  TestRoute53RecordSet:\n    Type: AWS::Route53::RecordSet\n    Properties:\n      AliasTarget:\n        DNSName: !GetAtt TestCloudFrontDistribution.DomainName\n        HostedZoneId: Z2FDTNDATAQYW2  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-aliastarget-1.html\n      Comment: Bind the custom domain name to the Test CloudFront fronted API Gateway\n      HostedZoneName: !Ref DomainNameZone\n      Name: !Ref CustomDomainName\n      Type: A\n  TestApi:\n    Type: AWS::ApiGateway::RestApi\n    Properties:\n      Name: Test\n      BinaryMediaTypes:\n        - \'*/*\'\n      Description: Test API\n      FailOnWarnings: true\n      EndpointConfiguration:\n        Types:\n          - REGIONAL\n  TestApiGatewayDomainName:\n    # The ApiGateway requires a custom domain name, despite sitting behind\n    # CloudFront. This is because we want to pass all ( * ) HTTP headers\n    # through CloudFront and onto API Gateway. If we didn\'t set a custom domain\n    # name on the API Gateway, the "Host" header passed through from CloudFront\n    # to API Gateway would be for the custom domain, but API Gateway, which uses\n    # SNI, wouldn\'t know which TLS certificate to use in the handshake because\n    # API Gateway would have no record of that Host header. This would result in\n    # API Gateway being unable to setup a TLS connection with the inbound\n    # CloudFront connection attempt, API Gateway writing no logs about this\n    # fact, and CloudFront returning to the user an error of\n    # {"message":"Forbidden"}\n    # If we weren\'t passing the "Host" header from CloudFront to API Gateway\n    # this resource wouldn\'t be needed\n    Type: AWS::ApiGateway::DomainName\n    Properties:\n      # Uppercase letters are not supported in DomainName\n      DomainName: !Ref CustomDomainName\n      EndpointConfiguration:\n        Types:\n          - REGIONAL\n      RegionalCertificateArn: !Ref CertificateArn\n      SecurityPolicy: TLS_1_2\n  TestBasePathMapping:\n    Type: AWS::ApiGateway::BasePathMapping\n    Properties:\n      # BasePath:   # Not specifying this so that we have no base path\n      DomainName: !Ref TestApiGatewayDomainName\n      RestApiId: !Ref TestApi\n      Stage: !Ref TestApiStage\n  TestLambdaPermission:\n    Type: AWS::Lambda::Permission\n    Properties:\n      Action: lambda:invokeFunction\n      FunctionName: !GetAtt TestFunction.Arn\n      Principal: apigateway.amazonaws.com\n      SourceArn: !Join [ \'\', [ \'arn:aws:execute-api:\', !Ref \'AWS::Region\', \':\', !Ref \'AWS::AccountId\', \':\', !Ref \'TestApi\', \'/*/*\' ] ]\n  TestApiStage:\n    Type: AWS::ApiGateway::Stage\n    Properties:\n      DeploymentId: !Ref TestApiDeployment\n      MethodSettings:\n        - DataTraceEnabled: true\n          HttpMethod: \'*\'\n          ResourcePath: /*\n      RestApiId: !Ref TestApi\n  TestApiDeployment:\n    Type: AWS::ApiGateway::Deployment\n    DependsOn:\n      - TestRequest\n    Properties:\n      RestApiId: !Ref TestApi\n      StageName: DummyStage\n      # Deployment with an Empty Embedded Stage\n      # The following instructional text is no longer present in the AWS\n      # documentation for AWS::ApiGateway::Deployment StageName and it\'s not\n      # clear if it still applies.\n      #\n      # "Note This property is required by API Gateway. We recommend that you\n      # specify a name using any value (see Examples) and that you don\xe2\x80\x99t use\n      # this stage. We recommend not using this stage because it is tied to\n      # this deployment, which means you can\xe2\x80\x99t delete one without deleting the\n      # other. For example, if you delete this deployment, API Gateway also\n      # deletes this stage, which you might want to keep. Instead, use the\n      # AWS::ApiGateway::Stage resource to create and associate a stage with\n      # this deployment."\n  TestResource:\n    Type: AWS::ApiGateway::Resource\n    Properties:\n      RestApiId: !Ref TestApi\n      ParentId: !GetAtt TestApi.RootResourceId\n      PathPart: \'{proxy+}\'\n  TestRequest:\n    DependsOn: TestLambdaPermission\n    Type: AWS::ApiGateway::Method\n    Properties:\n      AuthorizationType: NONE\n      HttpMethod: GET\n      Integration:\n        Type: AWS_PROXY\n        # IntegrationHttpMethod is POST regardless of the HttpMethod for this resource\n        # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#set-up-lambda-proxy-integration-using-cli\n        # "For Lambda integrations, you must use the HTTP method of POST for the\n        # integration request, according to the specification of the Lambda service\n        # action for function invocations."\n        IntegrationHttpMethod: POST\n        Uri: !Join [ \'\', [ \'arn:aws:apigateway:\', !Ref \'AWS::Region\', \':lambda:path/2015-03-31/functions/\', !GetAtt \'TestFunction.Arn\', \'/invocations\' ] ]\n      ResourceId: !Ref TestResource\n      RestApiId: !Ref TestApi\n  TestPOSTRequest:\n    DependsOn: TestLambdaPermission\n    Type: AWS::ApiGateway::Method\n    Properties:\n      AuthorizationType: NONE\n      HttpMethod: POST\n      Integration:\n        Type: AWS_PROXY\n        # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#set-up-lambda-proxy-integration-using-cli\n        # "For Lambda integrations, you must use the HTTP method of POST for the\n        # integration request, according to the specification of the Lambda service\n        # action for function invocations."\n        IntegrationHttpMethod: POST\n        Uri: !Join [ \'\', [ \'arn:aws:apigateway:\', !Ref \'AWS::Region\', \':lambda:path/2015-03-31/functions/\', !GetAtt \'TestFunction.Arn\', \'/invocations\' ] ]\n      ResourceId: !Ref TestResource\n      RestApiId: !Ref TestApi\n  TestRootRequest:\n    # This resource is necessary to get API Gateway to respond to requests for the \'/\' path\n    # Without it API Gateway will respond to requests for \'/\' with the error\n    # {"message":"Missing Authentication Token"}\n    # https://stackoverflow.com/q/46578615/168874\n    # https://stackoverflow.com/q/52909329/168874\n    DependsOn: TestLambdaPermission\n    Type: AWS::ApiGateway::Method\n    Properties:\n      AuthorizationType: NONE\n      HttpMethod: GET\n      Integration:\n        Type: AWS_PROXY\n        # IntegrationHttpMethod is POST regardless of the HttpMethod for this resource\n        # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#set-up-lambda-proxy-integration-using-cli\n        # "For Lambda integrations, you must use the HTTP method of POST for the\n        # integration request, according to the specification of the Lambda service\n        # action for function invocations."\n        IntegrationHttpMethod: POST\n        Uri: !Join [ \'\', [ \'arn:aws:apigateway:\', !Ref \'AWS::Region\', \':lambda:path/2015-03-31/functions/\', !GetAtt \'TestFunction.Arn\', \'/invocations\' ] ]\n      # ResourceId must use the RootResourceId attribute of the AWS::ApiGateway::RestApi\n      # https://stackoverflow.com/a/56121914/168874\n      ResourceId: !GetAtt TestApi.RootResourceId\n      RestApiId: !Ref TestApi\n  TestCloudFrontDistribution:\n    Type: AWS::CloudFront::Distribution\n    Properties:\n      DistributionConfig:\n        Comment: !Join [ \':\', [!Ref \'AWS::StackName\', \'Test\']]\n        DefaultCacheBehavior:\n          AllowedMethods:\n            - GET\n            - HEAD\n            - POST\n            - DELETE\n            - OPTIONS\n            - PUT\n            - PATCH\n          Compress: true\n          DefaultTTL: 0\n          MinTTL: 0\n          MaxTTL: 0\n          ForwardedValues:\n            Cookies:\n              Forward: all\n            QueryString: true\n            Headers:\n              - \'*\'\n          TargetOriginId: TestCloudFrontOriginId\n          ViewerProtocolPolicy: redirect-to-https\n        # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-defaultrootobject\n        DefaultRootObject: \'\'  # "If you don\'t want to specify a default root object when you create a distribution, include an empty DefaultRootObject element."\n        Enabled: true\n        Aliases:\n          - !Ref CustomDomainName\n        HttpVersion: http2\n        IPV6Enabled: true\n        #Logging:\n        #  Logging\n        Origins:\n          - CustomOriginConfig:\n              OriginProtocolPolicy: https-only\n              OriginSSLProtocols:\n                - TLSv1.2\n            DomainName: !GetAtt TestApiGatewayDomainName.RegionalDomainName\n            Id: TestCloudFrontOriginId\n            # OriginPath: !Join [ \'\', [ \'/\', !Ref \'TestApiStage\' ] ]\n        PriceClass: PriceClass_100  # US, Canada, Europe, Israel\n        ViewerCertificate:\n          AcmCertificateArn: !Ref CertificateArn\n          MinimumProtocolVersion: TLSv1.2_2018\n          SslSupportMethod: sni-only\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果 AWS 更新此文档页面来解决此问题,您可以在此处查看原始页面

\n