AWS API Gateway - CORS + POST不起作用

52
CORS真的让我疯狂,我已经没有任何想法可以尝试让它工作了。
我创建了一个简单的APIG API,其中包含一个名为'abc'的资源,并添加了两个方法GET和POST,两者的授权设置都是NONE,API Key Required设置为false,所有内容都部署到了一个名为'dev'的阶段。
当然,我在这两个方法上都启用了CORS,并且我看到了三个头部Access-Control-Allow-Origin、Access-Control-Allow-Headers和Access-Control-Allow-Methods被添加到OPTIONS方法中,Access-Control-Allow-Origin被添加到POST和GET方法中。
这两个调用都映射到同一个lambda函数,该函数只是简单地将'Hello from Lambda'文本输出到控制台。
然后我创建了一个简单的HTML页面,将其作为一个静态网站托管在S3上,使用Route53指向一个域名,并开始使用jQuery $.ajax测试API进行调用。
一切都看起来很简单,直接按照文档中的说明进行操作,只是GET方法可以正常工作,并按预期将文本输出到控制台。而POST方法则会出现以下错误:
请求的资源上没有'Access-Control-Allow-Origin'头部。因此,不允许从'http://example.com'访问。响应的HTTP状态码为400。
预检请求可以正常工作并返回200 OK,所有头部都存在,但是POST请求返回了该错误和400 Bad Request。
非常感谢任何帮助,希望AWS团队也能看到这个问题...

编辑 - 从Google Chrome复制:

POST原始请求头:

POST /dev/urls HTTP/1.1
Host: kykul1mshe.execute-api.us-east-1.amazonaws.com
Connection: keep-alive
Content-Length: 73
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
Content-Type: application/json
Referer: http://example.com/dev.html
Accept-Encoding: gzip, deflate, br
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4

POST原始响应头:
HTTP/1.1 400 Bad Request
Date: Fri, 19 Aug 2016 02:14:16 GMT
Content-Type: application/json
Content-Length: 177
Connection: keep-alive
x-amzn-RequestId: a1160e45-65b2-11e6-9766-cd61e49fbcdb
X-Cache: Error from cloudfront
Via: 1.1 d64756b4df47ce24d6c62b5a8de97e87.cloudfront.net (CloudFront)
X-Amz-Cf-Id: N9mf7apicKbSM_MiZjePbEgZGIFKckWJ3lZljH8iHVKFVTcIIOQuHg==

这返回400错误请求
OPTIONS原始请求头:
Accept:*/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:kykul1mshe.execute-api.us-east-1.amazonaws.com
Origin:http://example.com
Referer:http://example.com/dev.html
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36

选项原始响应头:

Access-Control-Allow-Headers:Content-Type,X-Amz-Date,Authorization,X-Api-Key,Cache-Control,X-Requested-With
Access-Control-Allow-Methods:POST,OPTIONS
Access-Control-Allow-Origin:*
Connection:keep-alive
Content-Length:79
Content-Type:application/json
Date:Fri, 19 Aug 2016 02:14:16 GMT
Via:1.1 d64756b4df47ce24d6c62b5a8de97e87.cloudfront.net (CloudFront)
X-Amz-Cf-Id:KpGEDmIuf5RHcUnBWuA3oEMZgWHwrjy3SpLuOflRhAD8IIx5vyKGSw==
x-amzn-RequestId:a10bae11-65b2-11e6-bcf7-63b49c24629e
X-Cache:Miss from cloudfront

这返回 200 OK

嗨,我来自 API 网关。我认为您设置 API 的方式没有问题。您能否更新原始请求?这将有助于调试。 - Abhigna Nagaraja
谢谢您回复@AbhignaNagaraja - 我已经更新了帖子,附上了我在Google Chrome中得到的标题(我只是隐藏了真实域名)。 - HBR
12个回答

32
如果您在API Gateway中使用代理集成,则从API Gateway启用CORS无效。 您必须从Lambda代码本身设置标题“Access-Control-Allow-Origin”。
文档中提到。
Python代码示例:
    response = {
        'statusCode': 200,
        'headers': {
            'Access-Control-Allow-Origin': '*'
        },
        'body': json.dumps({'message': 'CORS enabled')
    }
    return response

这是正确的答案。对于Node和Python都适用。谢谢! - Mirek
谢谢。我一定花了20个小时来解决这个问题。这是一个更直接的链接,指向“它不支持代理集成”文档。 - gman
1
从一个程序员到另一个程序员。谢谢。 - joel
这很奇怪。为什么它不起作用? - Prashanth Wagle

28

好的,我找到了问题的根源,它与APIG完全无关,并且确认了@AbhignaNagaraja提到的内容,即我的APIG已经正确配置。

实际问题出现在我调用jQuery.ajax的方式上,我认为它足够智能,在contentType为'application/json'时将我的参数转换为JSON字符串。 看来我必须手动将JSON参数转换为字符串,而不是传递一个JSON并让jQuery进行字符串化。

所以这是错误的调用:

$.ajax({
        url: myEndpoint,
        type: 'POST',
        crossDomain: true,
        data: {
            url: $('#url').val()
        },
        headers: {
            "X-Api-Key": 'blablabla'
        },
        dataType: 'json',
        contentType: "application/json",
        success: function (data) {
            console.info(data);
        }
    });

这是正确的决定:

 $.ajax({
        url: myEndpoint,
        type: 'POST',
        crossDomain: true,
        data: JSON.stringify({
            url: $('#url').val()
        }),
        headers: {
            "X-Api-Key": 'blablabla'
        },
        dataType: 'json',
        contentType: "application/json",
        success: function (data) {
            console.info(data);
        }
    });
如果您在调试CORS问题时需要提示,请尝试下载AWS APIG SDK并使用AWS提供的apigClient执行调用,然后将标头与自定义客户端获得的标头进行比较。当检查使用jQuery和apigClient获取的2组标头时,我注意到请求载荷看起来不同,这就是我意识到格式错误的方式,然后400代码和“没有'Access-Control-Allow-Origin'”标头的出现都变得有意义了。
希望这能帮到您。

如果它是JSON格式的,jQuery就不需要将其转换为字符串。你传递的是一个对象,而不是JSON格式的数据,因此jQuery会将任何非字符串类型的数据转换为字符串(参数字符串)。通过将其转换为字符串,你将其变成了一个字符串,因此jQuery没有对其进行处理。所以看起来你需要手动将JSON参数转换为字符串,而不是传递一个JSON并让jQuery将其转换为字符串。 - Kevin B
谢谢,将解析为字符串对我解决了相同的问题。 - deanmau5
2
一个无效的 JSON 负载导致 APIG 响应中缺少 CORS 标头,这样做是否合理? - gigi2
我知道这已经是两年前的事了,但你还记得当时是否使用了lambda代理集成吗? - openwonk
将 JSON 对象转换为字符串,当调用 Lambda 时,这个方法解决了我的问题。谢谢。 不过我不确定为什么它一开始会出现 CORS 错误。 - aspnetdeveloper

14

我曾遇到过类似的问题,但是出现在lambda代理集成中:

  • 使用浏览器激活AWS API Gateway上的CORS

  • 激活lambda代理集成

使用lambda代理集成时,你可以在lambda代码中返回自定义标头:

        var result = {
        statusCode: data.statusCode | 200,
        headers: {
          "Access-Control-Allow-Origin": "*"
        },
        body: JSON.stringify(responseBody)
    };
    callback(null, result);

这样你就可以发送CORS头了。 我认为可能有更好的方法来使其与lambda代理集成一起工作而不必将CORS耦合在lambda代码中,请告诉我您是否知道。


这实际上是在使用Lambda代理集成时能够正常工作的。谢谢! - openwonk
顺带一提,我还在我的API Gateway实例中为每个资源启用了CORS……不确定它是否有所帮助,但它确实添加/允许了一个CORS标头,这是Lambda函数产生的(现在我看到了)。 - openwonk

9
在2021年,仍然存在CORS问题。我的设置是React应用程序-> API网关-> Lambda应用程序-> Dynamo DB。我的API需要一个apiKey。
如果你已经到达这里,上述方法都不像我的那样有效。以下是我调试问题的方法:
- 我已经在GET请求中启用了CORS,这将生成OPTIONS方法。请注意,OPTIONS方法不需要apiKey。 - 启用了API Gateway的Cloudwatch日志,在那里我看到“需要API密钥,而API密钥未与API阶段的使用计划相关联”。 - 在使用计划中添加了apiKey。 - 调用Postman上的GET和OPTION方法来测试它是否工作。(两者都得到200 OK状态) - 注意到仍然会出现此错误。

enter image description here

  • 但经过进一步检查,我只允许特定的标头在我启用API Gateway上的CORS时使用。

enter image description here

换句话说,Access-Control-Allow-Headers需要与您实际请求中添加的标头匹配。
           {
                'Content-Type': 'application/json',
                'X-API-Key': 'secret',
                //'Access-Control-Allow-Origin': '*',
            }

对我来说,从请求的角度添加'Access-Control-Allow-Origin': '*'实际上导致了CORS问题,因为它不是默认允许头的一部分。


2
这应该放得更高一些。在AWS文档中从未清楚地说明答案是从我的代码中删除这些标头。它总是被陈述为确保一切都是相同的。这个方法完美地解决了问题。 - justKeepCodin

5
我也曾遇到这个错误,经过研究后发现,在非2XX响应中,API没有在响应中给出Access-Control-Allow-Origin头。因此,虽然OPTION方法和成功的(2XX)响应具有此标头,但4XX5XX没有。您还可以使用PostMan确认,并检查坏响应的标头。
在调整配置后,我确保在所有响应中返回该标头。

在调整配置之后,我很想知道你做了哪些调整。 - Aaronius

5

在2020年,仍然发现API网关CORS问题的其他原因。对于我来说(不是使用lambda代理),即使在使用API下拉菜单启用CORS之后,甚至在将“Access-Control-Allow-Origin”标头添加到Lambda函数的响应中之后,我仍然会遇到CORS错误。

对我有帮助的是将“Access-Control-Allow-Origin”添加到默认的网关响应中。在API网关UI中,在左侧菜单中找到“网关响应”。选择“默认4XX”并将其编辑以包括“Access-Control-Allow-Origin”。我在测试期间将值设置为“*”以完全开放(包括引号)。同样适用于“默认5XX”。我的Ajax调用能够通过CORS错误,并且现在我看到了真正的原因——我设置的方法所需的参数未被正确传递。

然而,我不知道这为什么会呈现为CORS错误。


3
为了解决这个问题,我向您提供了API网关中Post配置的配置文件。
选项方法 - 集成响应 - 标头映射。
X-Requested-With '*'
Access-Control-Allow-Headers 'Content-Type,x-requested-with,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Access-Control-Allow-Methods'
Access-Control-Allow-Origin 'http://localhost:4200'
Access-Control-Allow-Methods 'POST,OPTIONS'

POST方法 - 集成响应 - 头部映射

Access-Control-Allow-Origin "*" ---> can be changed by your ip obviously

我希望这可以帮助您。


遇到了同样的问题,问题在于我一开始使用了“使用Lambda代理集成”,这种方式根本不允许进行这种映射。 - MichaelChan
你好,你有屏幕截图吗?头部好像是一些隐藏的东西。 - Andres Navarro
1
只有在“Lambda代理集成”禁用的情况下才能起作用。否则,标头应该作为来自lambda的响应的一部分发送。 - MichaelChan
就是这样,我的朋友。 - Andres Navarro

3
我曾遇到类似的问题——这与API的配置方式或我在前端进行的POST请求无关。解决问题的方法是将API部署在AWS API网关上。一旦您创建了一个API方法/资源,并将它们绑定到Lambda函数,它们就不会自动部署。
您需要点击“操作”,然后点击“部署API”才能从前端访问这些微服务。

2
有时候,如果Lambda超时时间较短,就会出现CORS错误。

1
谢谢!我简直不敢相信这就是问题所在……我的应用程序所有端点都能正常工作,除了一个,原来是超时问题。 - David Azar
1
欢迎 :). 此外,低内存可能会出现相同的问题。 - Rafsan Uddin Beg Rizan

1

有许多帖子指导您确保lambda函数返回适当的CORS标头,它们是正确的。然而,同样关键的是使用JSON.stringify()将json对象转换为字符串。似乎Postman已经为我们做了这个,因此当Postman请求和$.ajax请求发送相同的json对象时,一个成功而另一个失败是误导性的。


非常感谢您,先生。您的回答帮助了我很多。 - joel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接