0

我有一个 AWS S3 支持的静态网站和一个 RestApi。我正在为静态网站和 RestApi 配置一个 Cloudfront Distribution。我为 S3 源和 RestApi 源完成了 OriginConfigs。我正在使用 AWS CDK 在代码中定义基础设施。

该方法已从本文中采用:https://dev.to/evnz/single-cloudfront-distribution-for-s3-web-app-and-api-gateway-15c3]

API 在相对路径下定义/r/<resourcename>/r/api/<methodname>. 示例将/r/Account引用 Account 资源并/r/api/Validate引用称为 Validate 的 rpc 样式方法(在本例中为 HTTP POST 方法)。实现资源方法的 Lambda 方法使用正确的 PREFLIGHT OPTIONS 配置,静态网站的 url 列在该资源的允许来源中。例如:/r/api/Validate方法 lambda 有

exports.main = async function(event, context) {
  try {
    var method = event.httpMethod;

    if(method === "OPTIONS") {
      const response = {
        statusCode: 200,
        headers: {
          "Access-Control-Allow-Headers" : "*",
          "Access-Control-Allow-Credentials": true,
          "Access-Control-Allow-Origin": website_url,
          "Vary": "Origin",
          "Access-Control-Allow-Methods": "OPTIONS,POST,GET,DELETE"
        }
      };
      return response;
    } else if(method === "POST") {
      ...
    }
   ...
}

API 和网站部署良好。这是 CDK 部署代码片段。

        const string api_domain = "myrestapi.execute-api.ap-south-1.amazonaws.com";
        const string api_stage = "prod";

        internal WebAppStaticWebsiteStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            // The S3 bucket to hold the static website contents
            var bucket = new Bucket(this, "WebAppStaticWebsiteBucket", new BucketProps {
                PublicReadAccess = false,
                BlockPublicAccess = BlockPublicAccess.BLOCK_ALL,
                RemovalPolicy = RemovalPolicy.DESTROY,
                WebsiteIndexDocument = "index.html",
                Cors = new ICorsRule[] {
                    new CorsRule() {
                        AllowedHeaders = new string[] { "*" },
                        AllowedMethods = new HttpMethods[] { HttpMethods.GET, HttpMethods.POST, HttpMethods.PUT, HttpMethods.DELETE, HttpMethods.HEAD },
                        AllowedOrigins = new string[] { "*" }
                    }
                }
            });

            var cloudfrontOAI = new OriginAccessIdentity(this, "CloudfrontOAI", new OriginAccessIdentityProps() {
                Comment = "Allows cloudfront access to S3"
            });

            bucket.AddToResourcePolicy(new PolicyStatement(new PolicyStatementProps() {
                Sid = "Grant cloudfront origin access identity access to s3 bucket",
                Actions = new [] { "s3:GetObject" },
                Resources = new [] { bucket.BucketArn + "/*" },
                Principals = new [] { cloudfrontOAI.GrantPrincipal }
            }));

            // The cloudfront distribution for the website
            var distribution = new CloudFrontWebDistribution(this, "WebAppStaticWebsiteDistribution", new CloudFrontWebDistributionProps() {
                ViewerProtocolPolicy = ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
                DefaultRootObject = "index.html",
                PriceClass = PriceClass.PRICE_CLASS_ALL,
                GeoRestriction = GeoRestriction.Whitelist(new [] {
                    "IN"
                }),
                OriginConfigs = new [] {
                    new SourceConfiguration() {
                        CustomOriginSource = new CustomOriginConfig() {
                            OriginProtocolPolicy = OriginProtocolPolicy.HTTPS_ONLY,
                            DomainName = api_domain,
                            AllowedOriginSSLVersions = new OriginSslPolicy[] { OriginSslPolicy.TLS_V1_2 },
                        },
                        Behaviors = new IBehavior[] {
                            new Behavior() {
                                IsDefaultBehavior = false,
                                PathPattern = $"/{api_stage}/r/*",
                                AllowedMethods = CloudFrontAllowedMethods.ALL,
                                CachedMethods = CloudFrontAllowedCachedMethods.GET_HEAD_OPTIONS,
                                DefaultTtl = Duration.Seconds(0),
                                ForwardedValues = new CfnDistribution.ForwardedValuesProperty() {
                                    QueryString = true,
                                    Headers = new string[] { "Authorization" }
                                }
                            }
                        }
                    },
                    new SourceConfiguration() {
                        S3OriginSource = new S3OriginConfig() {
                            S3BucketSource = bucket,
                            OriginAccessIdentity = cloudfrontOAI
                        },
                        Behaviors = new [] {
                            new Behavior() {
                                IsDefaultBehavior = true,
                                //PathPattern = "/*",
                                DefaultTtl = Duration.Seconds(0),
                                Compress = false,
                                AllowedMethods = CloudFrontAllowedMethods.ALL,
                                CachedMethods = CloudFrontAllowedCachedMethods.GET_HEAD_OPTIONS
                            }
                        },
                    }
                }
            });

            // The distribution domain name - output
            var domainNameOutput = new CfnOutput(this, "WebAppStaticWebsiteDistributionDomainName", new CfnOutputProps() {
                Value = distribution.DistributionDomainName
            });

            // The S3 bucket deployment for the website
            var deployment = new BucketDeployment(this, "WebAppStaticWebsiteDeployment", new BucketDeploymentProps(){
                Sources = new [] {Source.Asset("./website/dist")},
                DestinationBucket = bucket,
                Distribution = distribution
            });
        }

我遇到以下错误(从浏览器控制台错误日志中提取):

bundle.js:67 POST https://mywebapp.cloudfront.net/r/api/Validate 405

bundle.js:67 
<?xml version="1.0" encoding="UTF-8"?>
<Error>
  <Code>MethodNotAllowed</Code>
  <Message>The specified method is not allowed against this resource.</Message>
  <Method>POST</Method>
  <ResourceType>OBJECT</ResourceType>
  <RequestId>xxxxx</RequestId>
  <HostId>xxxxxxxxxxxxxxx</HostId>
</Error>

预期的流程是对https://mywebapp.cloudfront.net/r/api/Validate的 POST 调用(使用 fetch() api 进行)由 cloudfront 转发到 RestApi 后端。看起来 Cloudfront 正在这样做,但后端返回错误(基于错误消息)。

我错过了什么?我该如何进行这项工作?

4

1 回答 1

0

通过执行以下操作已解决此问题:

  1. 移至 Distribution 构造(根据 AWS 文档,它是接收最新更新时使用的构造)。
  2. 添加 CachePolicy 和 OriginRequestPolicy 来控制 Cookie 转发和 Header 转发
于 2021-04-13T18:47:20.670 回答