gpt4 book ai didi

amazon-web-services - AWS SNS 绕过 API Gateway 直接调用 Lambda 函数

转载 作者:行者123 更新时间:2023-12-05 00:57:00 26 4
gpt4 key购买 nike

我偶然发现了 Amazon SNS 的一种相当奇怪且据我所知未记录的行为。我正在寻找解决方案或设置来修复它。

总结

我有一个 SNS 主题,带有一个指向 Amazon API Gateway REST 端点的 HTTPS 订阅,支持一个用于执行请求的 Node.js Lambda 函数。

现在,如果我在主题上使用 SNS 和发布,整个 API 网关映射模板将被忽略/短路。 Lambda 函数最终接收到 原始的 SNS JSON 对象。

但是,如果我使用 Web 浏览器(或 curl)访问端点,则会调用 API 网关映射转换并将正确的 JSON 数据传递到 Lambda 函数。

API 网关端点

API 网关(以下称为 TheApi)是使用 sms 创建的资源下有一个“路径参数”{phone} .因此,您可以查询https://TheApi/sms/111-222-3333带有 POSTGET方法。

这两种方法都有一个通用的映射模板,它获取所有路径参数、所有 header 参数、所有查询参数和整个请求正文,并将其转换为一个 LARGE 请求正文 JSON 对象。这是模板的样子:

{
"resource-path" : "$context.resourcePath",
"http-method" : "$context.httpMethod",
"headers": {
#foreach($param in $input.params().header.keySet())
"$param": "$util.escapeJavaScript($input.params().header.get($param))" #if($foreach.hasNext),#end
#end
},
"query": {
#foreach($param in $input.params().querystring.keySet())
"$param": "$util.escapeJavaScript($input.params().querystring.get($param))" #if($foreach.hasNext),#end
#end
},
"paths": {
#foreach($param in $input.params().path.keySet())
"$param": "$util.escapeJavaScript($input.params().path.get($param))" #if($foreach.hasNext),#end

#end
},
"body" : $input.json('$')
}

然后将此结果对象作为 event 馈送到 Lambda 函数。 lambda 函数在其上运行。这是一个简单的 API 网关“测试”的结果:
Tue Feb 09 00:54:13 UTC 2016 : Endpoint request body after transformations:
{
"resource-path" : "/sms/{phone}",
"http-method" : "POST",
"headers": {
},
"query": {
},
"paths": {
"phone": "111-222-3333"
},
"body" : {"foo":"bar","Alice":"Bob"}
}

当从 Web 浏览器(或 curl 调用)调用时,此端点和被调用的 Lambda 函数可以完美运行。 AWS Cloud Watch 日志显示万事大吉,收到的 Lambda 事件与上面相同,因此调用了 Mapping 转换。

问题

现在,如果我在该主题上使用 SNS 和发布(顶部列出的 API 网关端点上的 HTTPS 订阅),整个 API 网关映射模板将被忽略/短路。

Lambda 函数最终接收到 原始的 SNS JSON 对象和我编写的任何自定义映射都没有。 Lambda 函数不会收到有关调用代理、请求的 url、 header 的任何信息......下面是 Lambda 事件的样子,如 CloudWatch 中所示:
{
"Type": "Notification",
"MessageId": "d38077e1-406a-5122-8a57-38cecfc635fd",
"TopicArn": "arn:aws:sns:us-east-1:...:...",
"Subject": "Ceci est un test",
"Message": "Ceci est un message de test.",
"Timestamp": "2016-02-06T06:06:36.649Z",
"SignatureVersion": "1",
"Signature": "...",
"SigningCertURL": "...",
"MessageAttributes": {
"AWS.SNS.MOBILE.MPNS.Type": {
"Type": "String",
"Value": "token"
},
"AWS.SNS.MOBILE.MPNS.NotificationClass": {
"Type": "String",
"Value": "realtime"
},
"AWS.SNS.MOBILE.WNS.Type": {
"Type": "String",
"Value": "wns/badge"
}
}
}

可以看出,这个JSON对象完全不同。

思想的食物
  • 有些人可能会问:“当我可以将 SNS 事件直接转发到 Lambda 函数时,为什么还要麻烦地制作 API 网关?”。原因很简单,我需要在 SNS 消息中附加附加信息,在这种情况下是将消息发送到的电话号码。使用 API Gateway,我可以为尽可能多的电话号码创建尽可能多的订阅,而无需复制任何代码。
  • 其他人可能想知道:“为什么不使用 SNS 中内置的 SMS 订阅而不是自己制作?”。一方面,我在加拿大,亚马逊短信订阅在加拿大不再有效。其次,我不妨使用亚马逊的另一种短信服务。
  • 事实证明,SNS 主题可以直接调用 Lambda 函数。在这种情况下,SNS JSON 对象完全相同。因此,就好像 AWS 正在检测 HTTPS 端点域,解析底层 Lambda 函数并将调用直接路由到 Lambda 函数,而无需通过 API 网关服务。
  • 事实上,当我在我控制的另一个域上构建另一个 REST 端点时,我确实收到了带有 SNS JSON 正文的 POST 请求,我可以将其转发到 API 网关端点,并且它得到了很好的翻译。

  • 像这样:
    {
    "resource-path": "/sms/{phone}",
    "http-method": "POST",
    "headers": {
    "Accept": "*/*",
    "CloudFront-Forwarded-Proto": "https",
    "CloudFront-Is-Desktop-Viewer": "true",
    "CloudFront-Is-Mobile-Viewer": "false",
    "CloudFront-Is-SmartTV-Viewer": "false",
    "CloudFront-Is-Tablet-Viewer": "false",
    "CloudFront-Viewer-Country": "US",
    "Content-Type": "application/json",
    "Via": "1.1 c903e93e57c533ecd52152e4407a295e.cloudfront.net (CloudFront)",
    "X-Amz-Cf-Id": "Fy_dCf5yJbW1GOZWJMVJqhbz1qt6sLfNO0N33FqAtf56X1tB4py8Ig==",
    "X-Forwarded-For": "69.65.27.156, 54.182.212.5",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
    },
    "query": {},
    "paths": {
    "phone": "14184901585"
    },
    "body": {
    "Type": "Notification",
    "MessageId": "d38077e1-406a-5122-8a57-38cecfc635fd",
    "TopicArn": "arn:aws:sns:us-east-1:...:...",
    "Subject": "Ceci est un test",
    "Message": "Ceci est un message de test.",
    "Timestamp": "2016-02-06T06:06:36.649Z",
    "SignatureVersion": "1",
    "Signature": "...",
    "SigningCertURL": "...",
    "UnsubscribeURL": "...",
    "MessageAttributes": {
    "AWS.SNS.MOBILE.MPNS.Type": {
    "Type": "String",
    "Value": "token"
    },
    "AWS.SNS.MOBILE.MPNS.NotificationClass": {
    "Type": "String",
    "Value": "realtime"
    },
    "AWS.SNS.MOBILE.WNS.Type": {
    "Type": "String",
    "Value": "wns/badge"
    }
    }
    }
    }

    调用寻求帮助

    当我可以做这个时,是否有任何隐藏的设置 SNS -> API Gateway -> Lambda使用正确的映射翻译?

    最佳答案

    根据请求的内容类型应用映射模板。如果请求中没有指定内容类型,则默认为“application/json”。

    根据您的描述,我假设您的映射模板设置为内容类型“application/json”。只要客户端没有在其请求中指定不同的内容类型(例如,浏览器就是这种情况),这就可以正常工作。

    由于 SNS 发送带有 header “Content-type: text/plain”( SNS Send Message Over HTTP )的请求,它与您的映射模板的内容类型不匹配,因此将忽略它​​。要开始工作,您可以更改当前映射中的内容类型,也可以添加另一个匹配“文本/纯文本”的内容类型。

    有关更多详细信息,您还可以在 AWS 论坛中查看此处:Default Content-Type for Mapping Template

    最好的事物,

    于尔根

    关于amazon-web-services - AWS SNS 绕过 API Gateway 直接调用 Lambda 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35282587/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com