CloudFormation API Gateway CORS issue access to XMLHttpRequest blocked

自作多情 提交于 2019-12-04 19:25:32

My Best Guess: The POST to your ANY lambda function is failing during execution, and not setting the Access-Control-Allow-Origin header to * (or your domain). Anytime I get a 5XX error and a CORS error at the same time from a non-OPTIONS request, this is almost always the case for me.

Recommended Next Steps: Reproduce the error situation after adding debug logging to your Lambda source code, and turning on CloudWatch Logs in your API Gateway Rest API. You can do this by going to the API Gateway console, clicking on Stages > Prod > Logs/Tracing, then checking these two: Enable CloudWatch Logs (Log level: INFO), and Enable Detailed CloudWatch Metrics. Then you must "deploy" the changes in order for them to take effect. Do this by clicking the Actions button from your Rest API's Resources menu, and choosing Deploy API. I also recommend logging the extendedRequestId (an event property passed to your handler) from your Lambda function in order to tie the Lambda request to the API Gateway request: event.requestContext.extendedRequestId.

Example API Gateway logs:

(b66b3876-984b-11e9-95eb-dd93c7e40ca0) Extended Request Id: b5zpBGS3IAMFvqw=
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) Verifying Usage Plan for request: b66b3876-984b-11e9-95eb-dd93c7e40ca0. API Key: API Stage: 1234567890/Prod
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) API Key authorized because method 'ANY /forms' does not require API Key. Request will not contribute to throttle or quota limits
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) Usage Plan check succeeded for API Key and API Stage 1234567890/Prod
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) Starting execution for request: b66b3876-984b-11e9-95eb-dd93c7e40ca0
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) HTTP Method: GET, Resource Path: /forms
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) Lambda execution failed with status 200 due to customer function error: select count(*) AS `count(*)` from (select `user`.* from `user` where (id IN ('some_id_123'))) as `temp` - Cannot enqueue Query after fatal error.. Lambda request id: 1ae2bb06-5347-4775-9277-caccc42f18f2
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) Method completed with status: 502
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) AWS Integration Endpoint RequestId : 1ae2bb06-5347-4775-9277-caccc42f18f2
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) X-ray Tracing ID : 1-5d13cca0-3be96a1ab93a877edc70577c

Example correlated Lambda execution logs:

START RequestId: 1ae2bb06-5347-4775-9277-caccc42f18f2 Version: $LATEST
2019-06-26T19:50:56.391Z  1ae2bb06-5347-4775-9277-caccc42f18f2 { "extendedRequestId": "b5zpBGS3IAMFvqw=", ... }
2019-06-26T19:50:57.853Z  1ae2bb06-5347-4775-9277-caccc42f18f2 { "errorMessage": "select count(*) AS `count(*)` from (select `user`.* from `user` where (id IN ('some_id_123'))) as `temp` - Cannot enqueue Query after fatal error.", ... }
END RequestId: 1ae2bb06-5347-4775-9277-caccc42f18f2
REPORT RequestId: 1ae2bb06-5347-4775-9277-caccc42f18f2  Duration: 1660.45 ms  Billed Duration: 1700 ms Memory Size: 256 MB  Max Memory Used: 57 MB  

Other Thoughts: Export the Swagger definitions of both the broken API and the working API. Compare and see what is different. Do this from the console by going to Stages > Prod > Export > Export as Swagger + API Gateway Extensions. It may not be exactly the same as the CloudFormation template, but it's pretty close.

I've figured out the issue. There are 2 main things:

  1. The IntegrationHttpMethod for Lambda must be POST. I found the answer here.
  2. The template didn't have AWS::Lambda::Permission that allows API Gateway to invoke Lambda function. With the template, when you use AWS::Lambda::Permission, it will show the API as a trigger of your Lambda function. However, if you manually create the API Gateway and link it with your Lambda function, it won't show API Gateway as a trigger but it still works.

So for the template I posted above, I needed to add these for it to work:

"LambdaPermission": {
            "Type": "AWS::Lambda::Permission",
            "Description": "Permission for API GateWay to invoke Lambda.",
            "Properties": {
                "Action": "lambda:invokeFunction",
                "FunctionName": {
                    "Fn::GetAtt": [
                        "LambdaFunction",
                        "Arn"
                    ]
                },
                "Principal": "apigateway.amazonaws.com",
                "SourceArn": {
                    "Fn::Join": [
                        "",
                        [
                            "arn:aws:execute-api:",
                            {
                                "Ref": "AWS::Region"
                            },
                            ":",
                            {
                                "Ref": "AWS::AccountId"
                            },
                            ":",
                            {
                                "Ref": "ApiGateway"
                            },
                            "/*"
                        ]
                    ]
                }
            }
        },

And edit method ANY to look like this

"methodNotesANY": {
            "Type": "AWS::ApiGateway::Method",
            "DependsOn": "LambdaPermission",
            "Properties": {
                "AuthorizationType": "COGNITO_USER_POOLS",
                "AuthorizerId": {
                    "Ref": "GatewayAuthorizer"
                },
                "RestApiId": {
                    "Ref": "ApiGateway"
                },
                "ResourceId": {
                    "Ref": "resourceNotes"
                },
                "HttpMethod": "ANY",
                "Integration": {
                    "Type": "AWS_PROXY",
                    "IntegrationHttpMethod": "POST",
                    "Uri": {
                        "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations"
                    },
                    "IntegrationResponses": [{
                        "StatusCode": "200"
                    }]
                },
                "MethodResponses": [{
                    "ResponseModels": {
                        "application/json": "Empty"
                    },
                    "StatusCode": "200"
                }]
            }
        },
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!