CloudFormation API Gateway CORS 问题访问 XMLHttpRequest 被阻止

Posted

技术标签:

【中文标题】CloudFormation API Gateway CORS 问题访问 XMLHttpRequest 被阻止【英文标题】:CloudFormation API Gateway CORS issue access to XMLHttpRequest blocked 【发布时间】:2019-11-03 10:34:27 【问题描述】:

我正在尝试使用 CloudFormation 创建 API 网关,但我遇到了 CORS 问题。

前端出错:

POST https://<>.execute-api.us-east-1.amazonaws.com/prod/<> 500
new:1 Access to XMLHttpRequest at '<>' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
API 的创建没有任何问题,我什至仔细检查控制台上的每一页与工作 API 并发现它们的 Method RequestIntegration RequestIntegration ResponseMethod Response 用于所有方法(包括 OPTIONS)。 如果我删除模板创建的资源并在同一个 API 网关中手动创建它们,那么我的代码将按预期工作。我已经使用 localhost、S3 存储桶和 PostMan 中的前端代码进行了测试,因此我可以验证我的前端代码、lambda 函数和数据库是否正常工作。

我知道人们以前遇到过这个问题,但我找不到任何可以解决我的问题的答案。

这里是my template。

请注意,"method.response.header.Access-Control-Allow-Origin": false 实际上创建的 API 的设置与工作 API 相同。

我也使用correct answer for this question中的代码。

是的,我的 OPTIONS 请求具有“Access-Control-Allow-Origin”标头。

更新

按照以下 dannymac 的回答。我得到了这些:

我将console.log(event.requestContext); 添加到我的 Lambda 函数(用 Node.js 编写)。 当我测试函数时,有 Lambda 的日志。
2019-06-27T20:07:03.118Z    462b93b2-9d4b-4ed3-bc04-f966fcd034cf    Debug CORS issue. Request ID:
2019-06-27T20:07:03.118Z    462b93b2-9d4b-4ed3-bc04-f966fcd034cf    undefined
好像没有event.requestContext。 我在API Gateway 设置中选择了Enable CloudWatch Logs-INFOEnable Detailed CloudWatch MetricsCloudWatch log role ARN*:arn:aws:iam::&lt;ID&gt;:role/ApiGatewayCloudWatchLogsRole(这是AWS 创建的角色)。 但是,API Gateway 没有 CloudWatch 日志。有默认登录CloudWatch - Log Groups/aws/apigateway/welcome
Time (UTC +00:00)
2019-06-27
19:50:55
Cloudwatch logs enabled for API Gateway
看起来CloudWatch 日志没有从API Gateway 获取测试。 这是我在API Gateway 中测试GET 方法得到的结果:
Response Body

  "message": "Internal server error"

Response Headers

Logs
Execution log for request 10d90173-9919-11e9-82e1-dd33dda3b9df
Thu Jun 27 20:20:54 UTC 2019 : Starting execution for request: 10d90173-9919-11e9-82e1-dd33dda3b9df
Thu Jun 27 20:20:54 UTC 2019 : HTTP Method: GET, Resource Path: /notes
Thu Jun 27 20:20:54 UTC 2019 : Method request path: 
Thu Jun 27 20:20:54 UTC 2019 : Method request query string: userid=<ID>
Thu Jun 27 20:20:54 UTC 2019 : Method request headers: 
Thu Jun 27 20:20:54 UTC 2019 : Method request body before transformations: 
Thu Jun 27 20:20:54 UTC 2019 : Endpoint request URI: https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:770402430649:function:test-api-gateway-2-LambdaFunction-1XDONAN3QIY9I/invocations
Thu Jun 27 20:20:54 UTC 2019 : Endpoint request headers: x-amzn-lambda-integration-tag=... [TRUNCATED]
Thu Jun 27 20:20:54 UTC 2019 : Endpoint request body after transformations: "resource":"/notes","path":"/notes","httpMethod":"GET","headers":null,"multiValueHeaders":null,"queryStringParameters":"userid":"<USERID>","multiValueQueryStringParameters":"userid":["<USERID>"],"pathParameters":null,"stageVariables":null,"requestContext":"path":"/notes","accountId":"<ID>"...,"identity":"cognitoIdentityPoolId":null,"cognitoIdentityId":null,"apiKey":"test-invoke-api-key","principalOrgId":null,"cognitoAuthenticationType":null,"userArn":"<ARN>","apiKeyId":"test-invoke-api-key-id","userAgent":..."test [TRUNCATED]
Thu Jun 27 20:20:54 UTC 2019 : Sending request to https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:<ID>:function:test-api-gateway-2-LambdaFunction-<STRING>/invocations
Thu Jun 27 20:20:54 UTC 2019 : Received response. Status: 403, Integration latency: 6 ms
Thu Jun 27 20:20:54 UTC 2019 : Endpoint response headers: Date=Thu, 27 Jun 2019 20:20:54 GMT, Content-Length=130, Connection=keep-alive, x-amzn-RequestId=<ID>
Thu Jun 27 20:20:54 UTC 2019 : Endpoint response body before transformations: <AccessDeniedException>
  <Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>

Thu Jun 27 20:20:54 UTC 2019 : Lambda invocation failed with status: 403. Lambda request id: feb22917-0dea-4f91-a274-fb6b85a69121
Thu Jun 27 20:20:54 UTC 2019 : Execution failed due to configuration error: 
Thu Jun 27 20:20:54 UTC 2019 : Method completed with status: 500
我还在 Swagger 2 中导出了工作和不工作的 API 网关。唯一的区别是:
// working one:
"x-amazon-apigateway-any-method": 
        "produces": [
          "application/json"
        ],
        "parameters": [
          
            "name": "noteid",
            "in": "path",
            "required": true,
            "type": "string"
          
        ],
        "responses": 
          "200": 
            "description": "200 response",
            "schema": 
              "$ref": "#/definitions/Empty"
            
          
        ,
        "security": [
          
            "mobile-notes-api-authorizer": []
          
        ]
      
// not working one:
"x-amazon-apigateway-any-method": 
        "produces": [
          "application/json"
        ],
        "responses": 
          "200": 
            "description": "200 response",
            "schema": 
              "$ref": "#/definitions/Empty"
            
          
        ,
        "security": [
          
            "test-api-gateway-2-authorizer": []
          
        ]
      
他们都有:
"headers": 
              "Access-Control-Allow-Origin": 
                "type": "string"
              ,
              "Access-Control-Allow-Methods": 
                "type": "string"
              ,
              "Access-Control-Allow-Headers": 
                "type": "string"
              
            
我之前尝试在我的 API Gateway 的Body 中使用 Swagger 模板,但无法解决无效授权问题。

【问题讨论】:

有时会在预检 OPTIONS 请求没有“Access-Control-Allow-Origin”标头时发生这种情况,您是否已经检查过? 也许这对你有帮助:***.com/questions/40292888/… 感谢@DeependraDangal,如果您查看我的模板,我会使用正确答案中的代码。 (不要担心这里的奇怪的downvote。评论它没有任何价值,因为匿名的downvoter 早就消失了。如果必须,请将您的回复放在 cmets 中,但请不要在帖子中添加投票评论 - 大多数读者不感兴趣)。 谢谢@halfer,我会记住的。 【参考方案1】:

我已经解决了这个问题。主要有两点:

    Lambda 的 IntegrationHttpMethod 必须是 POST。我找到了答案here。 模板没有允许 API Gateway 调用 Lambda 函数的 AWS::Lambda::Permission。 使用模板,当您使用AWS::Lambda::Permission 时,它会将 API 显示为您的 Lambda 函数的触发器。 但是,如果您手动创建 API Gateway 并将其与您的 Lambda 函数链接,它不会将 API Gateway 显示为触发器,但它仍然有效。

所以对于我上面发布的模板,我需要添加这些才能使其工作:

"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"
                            ,
                            "/*"
                        ]
                    ]
                
            
        ,

并编辑方法 ANY 看起来像这样

"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"
                ]
            
        ,

【讨论】:

我希望我早点看到你的答案......会节省我几个小时的挖掘......【参考方案2】:

我的最佳猜测POST 到您的 ANY lambda 函数在执行期间失败,并且没有将 Access-Control-Allow-Origin 标头设置为 *(或您的域)。每当我从非OPTIONS 请求中同时收到5XX 错误和CORS 错误时,对我来说几乎总是这样。

建议的后续步骤:在您的 Lambda 源代码中添加调试日志记录并在您的 API Gateway Rest API 中打开 CloudWatch Logs 后重现错误情况。为此,您可以转到 API Gateway 控制台,单击Stages > Prod > Logs/Tracing,然后检查这两个:Enable CloudWatch Logs(日志级别:INFO)和Enable Detailed CloudWatch Metrics。然后,您必须“部署”更改才能使它们生效。通过单击 Rest API 的 Resources 菜单中的 Actions 按钮并选择 Deploy API 来执行此操作。我还建议从您的 Lambda 函数记录 extendedRequestId(传递给您的处理程序的事件属性),以便将 Lambda 请求与 API Gateway 请求绑定:event.requestContext.extendedRequestId

示例 API 网关日志:

(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

相关的 Lambda 执行日志示例:

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  

其他想法:导出损坏的 API 和工作 API 的 Swagger 定义。比较一下,看看有什么不同。从控制台执行此操作,方法是转到 Stages > Prod > Export > Export as Swagger + API Gateway Extensions。它可能与 CloudFormation 模板不完全相同,但非常接近。

【讨论】:

非常感谢@dannymac!让我试试你的建议,然后回复你。 关于swagger 导出。我已经这样做了,还有openAPI,但由于授权方的问题,我无法使swaggeropenAPI 代码在AWS::ApiGateway::RestApiBody 属性内工作。我会更新我的问题。 我已按照您的建议进行操作,但无法找到问题所在。我已经更新了我上面的问题。

以上是关于CloudFormation API Gateway CORS 问题访问 XMLHttpRequest 被阻止的主要内容,如果未能解决你的问题,请参考以下文章

AWS Cloudformation 将 API 密钥链接到 API 网关

尝试通过 Cloudformation 创建 AWS API 网关时出错

使用 CloudFormation 的 API Gateway 自定义域

AWS:使用 cloudformation 模板将 WAF 附加到 api 网关

CloudFormation 中的 AWS API Gateway 方法响应

使用 Cloudformation 的 AWS Api Gateway 代理资源?