【发布时间】:2016-05-18 21:06:42
【问题描述】:
我偶然发现了 Amazon SNS 的一种相当奇怪且据我所知未记录的行为。我正在寻找解决方案或设置来修复它。
摘要
我有一个 SNS 主题,带有一个指向 Amazon API Gateway REST 端点的 HTTPS 订阅,并由用于执行请求的 Node.js Lambda 函数提供支持。
现在,如果我在主题上使用 SNS 和 发布,整个 API 网关 映射模板 会被忽略/短路。 Lambda 函数最终仅接收原始 SNS JSON 对象。
但是,如果我使用 Web 浏览器(或 curl)访问端点,则会调用 API Gateway Mapping Translation 并将正确的 JSON 数据传递到 Lambda 函数。
API 网关端点
API 网关(以下称为 TheApi)是使用 sms 资源创建的,在该资源下有一个“路径参数”{phone}。因此,您可以使用POST 或GET 方法查询https://TheApi/sms/111-222-3333。
这两种方法都有一个通用的映射模板,它抓取所有路径参数、所有标头参数、所有查询参数和整个请求正文,并将其转换为一个大型请求正文 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('$')
}
然后,此生成的对象将作为 lambda 函数在其上运行的 event 提供给 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 网关 映射模板 em> 被忽略/短路。
Lambda 函数最终仅接收到原始 SNS JSON 对象,并且没有收到我编写的自定义映射。 Lambda 函数没有收到有关调用代理、请求的 url、标头的任何信息……nada!下面是 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 订阅而不是自己制作?”。一方面,我在加拿大,Amazon SMS 订阅在加拿大不再有效。其次,我可能希望使用亚马逊的其他短信服务。
事实证明,SNS 主题可以直接调用 Lambda 函数。在这种情况下,SNS JSON 对象是完全相同的。因此,就好像 AWS 正在检测 HTTPS 端点域,解析底层 Lambda 函数并将调用直接路由到 Lambda 函数,而不通过 API Gateway 服务。
事实上,当我在我控制的另一个域上构建另一个 REST 端点时,我确实收到了带有 SNS JSON 正文的 POST 请求,我可以将其转发到 API Gateway 端点并且它被翻译得很好。 /p>
就像这样:
{
"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 与正确的映射翻译一起工作时,是否有任何隐藏设置?
【问题讨论】:
标签: amazon-web-services amazon-sns aws-lambda aws-api-gateway