【问题标题】:AWS SNS is bypassing API Gateway and calling Lamba functions directlyAWS SNS 绕过 API Gateway 直接调用 Lambda 函数
【发布时间】: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}。因此,您可以使用POSTGET 方法查询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对象是完全不同的。

思想的食物

  1. 有些人可能想知道:“我可以将 SNS 事件直接转发到 Lambda 函数,为什么还要麻烦制作 API 网关?”。原因很简单,我需要在 SNS 消息中附加附加信息,在这种情况下是发送消息的电话号码。使用 API Gateway,我可以创建尽可能多的电话号码订阅,而无需复制任何代码。

  2. 其他人可能想知道:“为什么不使用内置在 SNS 中的 SMS 订阅而不是自己制作?”。一方面,我在加拿大,Amazon SMS 订阅在加拿大不再有效。其次,我可能希望使用亚马逊的其他短信服务。

  3. 事实证明,SNS 主题可以直接调用 Lambda 函数。在这种情况下,SNS JSON 对象是完全相同的。因此,就好像 AWS 正在检测 HTTPS 端点域,解析底层 Lambda 函数并将调用直接路由到 Lambda 函数,而不通过 API Gateway 服务。

  4. 事实上,当我在我控制的另一个域上构建另一个 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


    【解决方案1】:

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

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

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

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

    最好的,

    尤尔根

    【讨论】:

    • 就是这样。现在效果很好。感谢您的帮助。
    猜你喜欢
    • 2020-08-21
    • 1970-01-01
    • 2017-12-07
    • 1970-01-01
    • 2016-11-08
    • 1970-01-01
    • 1970-01-01
    • 2018-08-28
    • 2019-06-03
    相关资源
    最近更新 更多