简体   繁体   中英

AWS CDK CORS error with Cloudfront + Static Website on S3 + API Gateway + Lambda + DynamoDb setup

I am using AWS CDK (v1.87.1 (build 9eeaa93)) to define my infrastructure as code. I use C# to define my CDK stack(s).

I have my data stored in DynamoDb and an API gateway backed by Lambda functions to read/write to the DynamoDb. This is my backend.

My frontend is a simple static website (HTML + JS) hosted on AWS S3 distributed through CloudFront.

My API works fine when I test it independently with curl or in the AWS console. However, when I call the API using the fetch() browser API from within my static website page, I get the following error (in the browser):

Access to fetch at 'https://xxxxxxxx.execute-api.ap-south-1.amazonaws.com/prod/Account' from origin 'https://abcdefg.cloudfront.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

My CorsOptions are defined as follows:

    var defaultCorsPreflightOptions = new CorsOptions() {
        AllowOrigins = Cors.ALL_ORIGINS,
        AllowMethods = Cors.ALL_METHODS,
        AllowHeaders = new [] {"*"},
        AllowCredentials = true,
        MaxAge = Duration.Days(0)
    };

My API is as follows:

    var api = new RestApi(this, "my-api", new RestApiProps {
        RestApiName = "My Service",
        Description = "This is the service API"
    });

My resource creation adds the CorsOption for preflight (In the above error message 'Account' would be a resource added to the root):

        var resourceType = api.Root.AddResource(ent);
        resourceType.AddCorsPreflight(defaultCorsPreflightOptions);

My lambda handler also has

if(method == "OPTIONS") {
  const response = {
    statusCode: 200,
    headers: {
      "Access-Control-Allow-Headers" : "Content-Type",
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "OPTIONS,POST,PUT,GET,DELETE"
    }
  };
  return response;      
} else if(method === "POST") {
     // ... omitted
}

The JS client code that calls the REST API is:

    const response = await fetch(`${api_url}/${entity}`,{
        method: 'POST',
        mode: 'cors',
        body: item,
        headers: {
            'Content-Type': 'application/json'
        }
    });

The Behavior that is attached to the CloudFront distribution:

        // The cloudfront distribution for the website
        var behavior = new Behavior() {
            IsDefaultBehavior = true,
            AllowedMethods = CloudFrontAllowedMethods.ALL,
            MaxTtl = Duration.Seconds(0),
            MinTtl = Duration.Seconds(0),
            DefaultTtl = Duration.Seconds(0),
            Compress = false,
            ForwardedValues = new CfnDistribution.ForwardedValuesProperty() {
                QueryString = true,
                Headers = new [] {"Authorization", "Access-Control-Allow-Origin"}
            }
        };

My CloudFront distribution is as follows:

        var distribution = new CloudFrontWebDistribution(this, "StaticWebsiteDistribution", new CloudFrontWebDistributionProps() {
            OriginConfigs = new [] {
                new SourceConfiguration() {
                    S3OriginSource = new S3OriginConfig() {
                        S3BucketSource = bucket
                    },
                    Behaviors = new [] {
                        behavior
                    }
                }
            }
        });

My S3 Bucket deployment code is:

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

I have tried looking into the AWS CDK documentation. I have tried adding the default CORS option at the API level also, but without any success. What am I missing? Please help.

I figured it out. The error was was in the Lambda handler for the POST / GET / DELETE / PUT methods. I needed to return the headers (example given below):

  return {
    statusCode: 200,
    headers: {
      "Access-Control-Allow-Origin": "*",  // I was missing this
      "Content-Type": "application/json"   // and this
    },
    body: JSON.stringify({"id":`${id}`})   // and this was a string earlier
  };

I had another error in the client side with the fetch() response handling. I was using response.json() whereas it should have been response.text() (since I was sending text in the response body earlier).

I was misled by the curl response (in my testing) which was just the plain text whereas there was a JSON parse issue with handling the fetch() response.

Key takeaway: Check your Lambda handler responses.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM