You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改CloudFormation模板处理S3代理API Gateway的403/404错误

问题:API Gateway代理S3时,请求不存在对象返回200而非404

我有一个CloudFormation模板,实现通过API Gateway直接获取S3对象。请求存在的对象时正常返回200和内容:

curl -i "https://xxx.execute-api.eu-west-1.amazonaws.com/prod/index.json"

返回结果:

HTTP/2 200 
content-type: application/json
...
{"hello": "world"}

但请求不存在的对象时,本该返回404 Not Found,却仍返回200 OK,同时返回S3的AccessDenied错误XML:

curl -i "https://xxx.execute-api.eu-west-1.amazonaws.com/prod/abcd.json"

返回结果:

HTTP/2 200 
content-type: application/json
content-length: 243
...
<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>AccessDenied</Code>
    <Message>Access Denied</Message>
    <RequestId>CH2PRDQ2MVN0CXXX</RequestId>
    <HostId>cCV8dG+6CXA5pVrzTwLqiRqMjpuW8F+iuISQRUrKDP5PugEA6f4wauU0Egmb3b1GyvLjQZgjXXX=</HostId>
</Error>

原CloudFormation模板如下:

Resources:
  S3Bucket:
    Type: AWS::S3::Bucket
  ApiGatewayRestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: S3ProxyAPI
  ApiGatewayResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId:
        Ref: ApiGatewayRestApi
      ParentId:
        Fn::GetAtt:
          - ApiGatewayRestApi
          - RootResourceId
      PathPart: "{proxy+}"
  ApiGatewayMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      RestApiId:
        Ref: ApiGatewayRestApi
      ResourceId:
        Ref: ApiGatewayResource
      HttpMethod: GET
      RequestParameters:
        method.request.path.proxy: true
      MethodResponses:
        - StatusCode: 200
      Integration:
        IntegrationHttpMethod: ANY
        Type: AWS
        Uri:
          Fn::Sub: arn:aws:apigateway:${AWS::Region}:s3:path/${S3Bucket}/{proxy}
        Credentials:
          Fn::GetAtt:
            - ApiGatewayRole
            - Arn
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        PassthroughBehavior: WHEN_NO_MATCH
        IntegrationResponses:
          - StatusCode: 200
  ApiGatewayDeployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn: ApiGatewayMethod
    Properties:
      RestApiId:
        Ref: ApiGatewayRestApi
      StageName: prod
  ApiGatewayRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: apigateway.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: ApiGatewayS3ProxyPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetObject
                Resource:
                  Fn::Sub: arn:aws:s3:::${S3Bucket}/*
Outputs:
  ApiEndpoint:
    Value:
      Fn::Sub: https://${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com/prod/{proxy}
  BucketName:
    Value:
      Ref: S3Bucket
解决方案

需要做两处关键修改:

1. 扩展IAM权限,允许API Gateway执行s3:ListBucket操作

当前角色仅允许s3:GetObject,当对象不存在时,S3返回AccessDenied而非NotFound(因为无权限检查对象是否存在)。添加s3:ListBucket权限,让API Gateway能确认对象是否存在,此时S3会返回正确的NotFound错误。

修改ApiGatewayRole中的策略:

Policies:
  - PolicyName: ApiGatewayS3ProxyPolicy
    PolicyDocument:
      Version: '2012-10-17'
      Statement:
        - Effect: Allow
          Action:
            - s3:GetObject
          Resource:
            Fn::Sub: arn:aws:s3:::${S3Bucket}/*
        - Effect: Allow
          Action:
            - s3:ListBucket
          Resource:
            Fn::Sub: arn:aws:s3:::${S3Bucket}

2. 在API Gateway中添加错误映射

需要在MethodResponses中添加404状态码,并在IntegrationResponses中配置将S3的NotFound错误映射到API Gateway的404响应,同时处理AccessDenied(如果仍出现)。

修改ApiGatewayMethod的MethodResponses:

MethodResponses:
  - StatusCode: 200
  - StatusCode: 404

修改IntegrationResponses,添加错误映射:

IntegrationResponses:
  - StatusCode: 200
  - StatusCode: 404
    SelectionPattern: '^404$'
    ResponseParameters:
      method.response.header.Content-Type: "'application/json'"
    ResponseTemplates:
      application/json: '{"error": "Not Found"}'
  - StatusCode: 404
    SelectionPattern: '^AccessDenied$'
    ResponseParameters:
      method.response.header.Content-Type: "'application/json'"
    ResponseTemplates:
      application/json: '{"error": "Not Found"}'

这里添加两个404映射:一个匹配S3返回的404状态码,另一个匹配AccessDenied(避免权限配置仍有问题时返回200),同时用响应模板把S3的XML错误转为JSON格式。

另外注意:将IntegrationHttpMethod从ANY改为GET,更符合当前仅处理GET请求的场景,避免不必要的权限风险。

修改后的完整CloudFormation模板

Resources:
  S3Bucket:
    Type: AWS::S3::Bucket
  ApiGatewayRestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: S3ProxyAPI
  ApiGatewayResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId:
        Ref: ApiGatewayRestApi
      ParentId:
        Fn::GetAtt:
          - ApiGatewayRestApi
          - RootResourceId
      PathPart: "{proxy+}"
  ApiGatewayMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      RestApiId:
        Ref: ApiGatewayRestApi
      ResourceId:
        Ref: ApiGatewayResource
      HttpMethod: GET
      RequestParameters:
        method.request.path.proxy: true
      MethodResponses:
        - StatusCode: 200
        - StatusCode: 404
      Integration:
        IntegrationHttpMethod: GET
        Type: AWS
        Uri:
          Fn::Sub: arn:aws:apigateway:${AWS::Region}:s3:path/${S3Bucket}/{proxy}
        Credentials:
          Fn::GetAtt:
            - ApiGatewayRole
            - Arn
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        PassthroughBehavior: WHEN_NO_MATCH
        IntegrationResponses:
          - StatusCode: 200
          - StatusCode: 404
            SelectionPattern: '^404$'
            ResponseParameters:
              method.response.header.Content-Type: "'application/json'"
            ResponseTemplates:
              application/json: '{"error": "Not Found"}'
          - StatusCode: 404
            SelectionPattern: '^AccessDenied$'
            ResponseParameters:
              method.response.header.Content-Type: "'application/json'"
            ResponseTemplates:
              application/json: '{"error": "Not Found"}'
  ApiGatewayDeployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn: ApiGatewayMethod
    Properties:
      RestApiId:
        Ref: ApiGatewayRestApi
      StageName: prod
  ApiGatewayRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: apigateway.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: ApiGatewayS3ProxyPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetObject
                Resource:
                  Fn::Sub: arn:aws:s3:::${S3Bucket}/*
              - Effect: Allow
                Action:
                  - s3:ListBucket
                Resource:
                  Fn::Sub: arn:aws:s3:::${S3Bucket}
Outputs:
  ApiEndpoint:
    Value:
      Fn::Sub: https://${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com/prod/{proxy}
  BucketName:
    Value:
      Ref: S3Bucket

内容的提问来源于stack exchange,提问作者Justin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:28:11