如何从Amazon API Gateway将POST参数传递给AWS Lambda

60
在这个问题中,如何从Amazon API Gateway将查询字符串或路由参数传递给AWS Lambda展示了如何使用API网关将查询字符串参数映射到AWS Lambda。我想做同样的事情,但是将POST值映射而不是查询字符串。我尝试了:
{
    "values": "$input.params()"
}
但是没有起作用,我看不到实际的表单数据。顺便说一下,我是使用以下方式进行发布:application/x-www-form-urlencoded。我从Lambda函数中获取响应,所以我知道它正在调用Lambda,但我的问题是我无法在任何地方看到POST参数。我无法弄清楚如何映射它们。我转储了在Lambda端接收到的所有内容,这是它:
 {"values":"{path={}, querystring={}, header={Accept=*/*, Accept-Encoding=gzip, deflate, Accept-Language=en-US,en;q=0.8, Cache-Control=no-cache, 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/x-www-form-urlencoded, Origin=chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop, Postman-Token=7ba28698-8753-fcb1-1f48-66750ce12ade, Via=1.1 6ba5553fa41dafcdc0e74d152f3a7a75.cloudfront.net (CloudFront), X-Amz-Cf-Id=sc8C7dLcW0BHYopztNYrnddC0hXyFdBzHv0O9aWU1gKhd1D_J2HF3w==, X-Forwarded-For=50.196.93.57, 54.239.140.62, X-Forwarded-Port=443, X-Forwarded-Proto=https}}"}

看一下这个教程,我相信它会有所帮助 - https://snowulf.com/2015/08/05/tutorial-aws-api-gateway-to-lambda-to-dynamodb/ - Stefan
13个回答

88

r7kamura的回答很好。此外,以下是一个易于理解且稳健的映射模板示例,适用于所有情况(假设为POST):application/x-www-form-urlencoded

{
    "data": {
        #foreach( $token in $input.path('$').split('&') )
            #set( $keyVal = $token.split('=') )
            #set( $keyValSize = $keyVal.size() )
            #if( $keyValSize >= 1 )
                #set( $key = $util.urlDecode($keyVal[0]) )
                #if( $keyValSize >= 2 )
                    #set( $val = $util.urlDecode($keyVal[1]) )
                #else
                    #set( $val = '' )
                #end
                "$key": "$val"#if($foreach.hasNext),#end
            #end
        #end
    }
}

它将转换输入的内容为

name=Marcus&email=email%40example.com&message=

进入
{
    "data": {
                "name": "Marcus",
                "email": "email@example.com",
                "message": ""
    }
}

lambda 处理程序可以像这样使用它(这个示例会返回所有的输入数据):

module.exports.handler = function(event, context, cb) {
  return cb(null, {
    data: event.data
  });
};

4
兄弟,连一个赞都没有,这个对我非常有效,谢谢! - ObjectiveTruth
1
完美地工作了。在过去的几天里,我一直在尝试使用AWS加载Facebook画布应用程序,这让我很烦恼。@Marcus做得很好。 - Rukshan Dangalla
你好!我正在尝试在c# Dot.Net Core Lambda集成中使用此解决方案,到目前为止,映射使请求通过lambda。但是我的正文始终为空。我已经尝试构建与表单参数匹配的模型,但无济于事。如果有人成功地在c#中使用了这个解决方案,任何帮助都将不胜感激。 - Lorien
1
谢谢你。我被这个问题困扰了两天。不知怎么地,通过 GitHub 链接到达了这里。非常感谢。 - Ankush
1
先生,您救了我的命。 - whck6
显示剩余4条评论

23
如果您启用了Lambda代理集成enter image description here。 POST请求的主体将会可用。
event['body']['param']

GET参数和标头也将通过以下方式可用:

event['pathParameters']['param1']
event["queryStringParameters"]['queryparam1']
event['requestContext']['identity']['userAgent']
event['requestContext']['identity']['sourceIP']

2
event.body 返回 id="some",但无法通过 event.body.id 或您所写的方式获取 id - Nobody
1
event['body'](以及其他)引发了KeyError错误,@Jonathan能否详细解释一下这是如何工作的?我正在尝试从URL传递一个变量到lambda处理程序,然后使用该变量填充我想要进行requests.get的URL。 - KMFR

22
您可以通过在集成设置中配置映射模板来将任何请求正文数据转换为有效的JSON格式,以便AWS Lambda可以接收它。
目前似乎Amazon API Gateway尚未正式支持application/x-www-form-urlencoded,但avilewin在AWS论坛上发布了解决方案。在映射模板中,您可以使用Velocity Template Language(VTL),因此您需要配置映射模板,将application/x-www-form-urlencoded格式转换为有效的JSON格式。当然,这是一种不太优美的解决方案,但我认为这是目前唯一的方法。

2
AWS API Gateway仍然不支持application/x-www-form-urlencoded吗? - Vitaly Zdanevich
我也非常想知道! - tabulaR
据我所知,没有。唉,今天已经花了几个小时在这上面了。说真的,AWS 的那些小丑竟然省略了将 Webhook 发送参数传递给处理程序的最基本形式。 - jpw
2022/2023年是否支持? - Douglas Gaskell

11
您可以使用API网关模板将参数转换为JSON: https://forums.aws.amazon.com/thread.jspa?messageID=673012&tstart=0#673012 或者您可以在Lambda函数中使用QueryString解析器包来完成此操作: https://www.npmjs.com/package/qs
var qs = require('qs');
var obj = qs.parse('a=c');  // { a: 'c' } 

如果亚马逊增加内置支持此功能,我将使用它,但在此之前,我个人更喜欢第二种方式,因为它更清晰,如果出现问题更容易调试。
更新于2017年7月:
您可以使用默认支持此功能的代理集成: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html

8

扩展@markus-whybrow的答案:

{
        #foreach( $token in $input.path('$').split('&') )
            #set( $keyVal = $token.split('=') )
            #set( $keyValSize = $keyVal.size() )
            #if( $keyValSize >= 1 )
                #set( $key = $util.urlDecode($keyVal[0]) )
                #if( $keyValSize >= 2 )
                    #set( $val = $util.urlDecode($keyVal[1]) )
                #else
                    #set( $val = '' )
                #end
                "$key": "$util.escapeJavaScript($val)"#if($foreach.hasNext),#end
            #end
        #end
    }

这将清除“数据”,还可以解决一个问题,即如果您的值中有双引号。

1
如果您有数组参数怎么办?这将导致lambda函数成为一个字符串键,例如:{ key1: 'value1',key2: 'value2','array_key [][something]': 'some array value'}。在这种情况下,我们需要进行一些额外的解析,对吗? - Dianna
@Dianna,你找到这个问题的解决方案了吗? - secretshardul
1
@real_shardul 我想我确实解决了,但是我无法告诉你解决方案是什么,因为:1. 我不记得了;2. 我不再有那个 Lambda 函数和 AWS 账户的访问权限。在一年中发生了很多事情 :) - Dianna

6

2021年6月

用于POST的application/x-www-form-urlencoded映射模板: (Python)

from base64 import b64decode
from urllib.parse import parse_qs

def lambda_handler(event, context):
    params = parse_qs(b64decode(event.get('body')).decode('utf-8'))
    print(params.get('name')[0])
    print(params.get('email')[0])

同样地,使用params字典和参数名称执行任何您想要执行的操作。

这是正确的答案。现在API网关将application/x-www-form-urlencoded的POST请求体作为base64编码的字符串传递。不需要进行映射。 - gtourkas

5

我发现了一个非常简单的解决方案,我认为值得分享,因为我花了一些时间才找到这个最小化可运行代码。

如果你有一个发送内容类型为application/x-www-form-urlencoded的常规表单,只需标记“Lambda代理集成”,然后你将在event.body中找到编码的表单数据,可以使用Node.js的querystring本地模块进行解析。

const querystring = require('querystring')

function handler (event, context, callback) {
  // Read form data.
  const { user, pass } = querystring.parse(event.body)

  // Follows your code to handle request.
}

4

我使用POST请求实现了一个API,其中Content-Typeapplication/x-www-form-urlencoded。如果您只想获取类似于GET请求的长查询字符串值,则可以使用此映射语法。

{
    "body": "$input.body"
}

请注意,您还可以添加其他映射...我忽略了这一点,以解决原始问题。 这是一个博客教程,我在最初实现我的API时使用了它。然后,我的Lambda函数解析查询字符串并将数据传递给其他进程。

3
这适用于 Lambda 集成。假设您的 POST 请求正文例如为:

{
   "name" : "Hello",
   "address" : "Cool place" 
}

您可以这样访问它:
if (event.body !== null && event.body !== undefined) {
   let body = JSON.parse(event.body)
   let name = body.name;
   let address = body.address;
}

更多信息请查看:http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html

这是一个关于如何将API网关创建为Lambda的简单代理的文档链接。

3
如果您想将POST请求的全部内容发送到Lambda函数,请在“集成请求”中编写以下内容:最初的回答
{
    "values": $input.json('$')
}

如果您想从主体中构建自己的结构,请按照以下步骤进行操作:

最初的回答。

{
    "values": {
        "body-param1": $input.json('body-param1'),
        "others": {
            "body-param2": "$input.json('body-param2')",
        }
    }
}

当body-param1为数字,body-param2为字符串时。

如果您想发送头部信息,请按照以下方式操作:

Original Answer翻译成"最初的回答"

{
    "headers": {
        #foreach($param in $input.params().header.keySet())
            "$param": "$util.escapeJavaScript($input.params().header.get($param))" #if($foreach.hasNext), #end
        #end
    }
}

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