Aws lambda 从事件内部的aws api网关获取原始和解析的有效负载?

Aws lambda 从事件内部的aws api网关获取原始和解析的有效负载?,aws-lambda,aws-api-gateway,slack-api,serverless,Aws Lambda,Aws Api Gateway,Slack Api,Serverless,我正在尝试验证lambda上接收到的事件请求,我目前正在使用带有lambda后端的api网关 在我的serverless.yml上,我将此作为事件处理程序 integration: lambda passthroughBehavior: "WHEN_NO_TEMPLATE" request: template: application/x-www-form-urlencoded

我正在尝试验证lambda上接收到的事件请求,我目前正在使用带有lambda后端的api网关

在我的serverless.yml上,我将此作为事件处理程序

          integration: lambda
          passthroughBehavior: "WHEN_NO_TEMPLATE"
          request:
              template:
                application/x-www-form-urlencoded: ${file(aws-api-gateway-form-to-json.ftl)}
我获得了通过AWS API代理控制台生成的文件的内容,我正在使用生成的方法request passthrough进行一次修改,这只是将原始主体添加到要传递到lambda rawBody的有效负载中:$input.body,,但是,当我添加此修改时,请求停止到达lambda,并且在发送请求时出错

##  See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
##  This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
#set($allParams = $input.params())
{
"body-json" : $input.json('$'),
"rawBody": "$input.body",
"params" : {
#foreach($type in $allParams.keySet())
    #set($params = $allParams.get($type))
"$type" : {
    #foreach($paramName in $params.keySet())
    "$paramName" : "$util.escapeJavaScript($params.get($paramName))"
        #if($foreach.hasNext),#end
    #end
}
    #if($foreach.hasNext),#end
#end
},
"stage-variables" : {
#foreach($key in $stageVariables.keySet())
"$key" : "$util.escapeJavaScript($stageVariables.get($key))"
    #if($foreach.hasNext),#end
#end
},
"context" : {
    "account-id" : "$context.identity.accountId",
    "api-id" : "$context.apiId",
    "api-key" : "$context.identity.apiKey",
    "authorizer-principal-id" : "$context.authorizer.principalId",
    "caller" : "$context.identity.caller",
    "cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
    "cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
    "cognito-identity-id" : "$context.identity.cognitoIdentityId",
    "cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
    "http-method" : "$context.httpMethod",
    "stage" : "$context.stage",
    "source-ip" : "$context.identity.sourceIp",
    "user" : "$context.identity.user",
    "user-agent" : "$context.identity.userAgent",
    "user-arn" : "$context.identity.userArn",
    "request-id" : "$context.requestId",
    "resource-id" : "$context.resourceId",
    "resource-path" : "$context.resourcePath"
    }
}
回答一些反馈。 如果我在集成请求中使用use Lambda代理集成 我得到了这样一个有效载荷,这很好,但我还需要一个不存在的原始物体

{ body: 
{ token: 'xxxxxxxxxxxx',
team_id: 'xxxxxxxxxxxx',
api_app_id: 'xxxxxxxxxxxx',
event: 
{ client_msg_id: 'xxxxxxxxxxxx',
type: 'message',
text: 'xxxxxxxxxxxx',
user: 'xxxxxxxxxxxx',
ts: '123456789.000200',
channel: 'xxxxxxxxxxxx',
event_ts: '123456789.000200',
channel_type: 'im' },
type: 'event_callback',
event_id: 'xxxxxxxxxxxx',
event_time: 123456798,
authed_users: [ 'xxxxxxxxxxxx' ] },
method: 'POST',
principalId: '',
stage: 'dev',
cognitoPoolClaims: { sub: '' },
enhancedAuthContext: {},
headers: 
{ Accept: '*/*',
'Accept-Encoding': 'gzip,deflate',
'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',
Host: 'xxxxxxxxxxxx.execute-api.region.amazonaws.com',
'User-Agent': 'Slackbot 1.0 (+https://api.slack.com/robots)',
Via: '1.1 xxxxxxxxxxxx.cloudfront.net (CloudFront)',
'X-Amz-Cf-Id': 'xxxxxxxxxxxx==',
'X-Amzn-Trace-Id': 'Root=xxxxxxxxxxxx',
'X-Forwarded-For': 'xx.xx.xx.xx, xx.xx.xx.xx',
'X-Forwarded-Port': '443',
'X-Forwarded-Proto': 'https',
'X-Slack-Request-Timestamp': '12345678',
'X-Slack-Signature': 'v0=xxxxxxxxxxxx' },
query: {},
path: {},
identity: 
{ cognitoIdentityPoolId: '',
accountId: '',
cognitoIdentityId: '',
caller: '',
sourceIp: 'xx.xx.xx.xx',
accessKey: '',
cognitoAuthenticationType: '',
cognitoAuthenticationProvider: '',
userArn: '',
userAgent: 'Slackbot 1.0 (+https://api.slack.com/robots)',
user: '' },
stageVariables: {} }

我不知道serverless是如何工作的,我的工作流程仅限于本地编写代码和上传ZIP,任何其他配置都是在AWS控制台上完成的

我相信您正在寻找一个名为的小功能,您将在API网关的Integration Request选项卡下找到它。它所做的是为请求和响应提供两个标准映射模板

使用Lambda代理集成时,事件对象将如下所示:

{
  "resource": "/users/single",
  "path": "/users/single",
  "httpMethod": "GET",
  "headers": {
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
    "accept-encoding": "gzip, deflate, br",
    "accept-language": "en-GB,en-US;q=0.9,en;q=0.8",
    "Host": "xxxxxxx.execute-api.xxxx.amazonaws.com",
    "upgrade-insecure-requests": "1",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
    "X-Amzn-Trace-Id": "Root=xxxxxxxxxxxxxxxxx",
    "X-Forwarded-For": "xx.xx.xx.xx",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "multiValueHeaders": {
    "accept": [
      "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
    ],
    "accept-encoding": ["gzip, deflate, br"],
    "accept-language": ["en-GB,en-US;q=0.9,en;q=0.8"],
    "Host": ["xxxxx.execute-api.xxxx.amazonaws.com"],
    "upgrade-insecure-requests": ["1"],
    "User-Agent": [
      "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
    ],
    "X-Amzn-Trace-Id": ["Root=xxxxx"],
    "X-Forwarded-For": ["xx.xx.xx.xx"],
    "X-Forwarded-Port": ["443"],
    "X-Forwarded-Proto": ["https"]
  },
  // this contains the get body
  "queryStringParameters": { "id": "2" },
  "multiValueQueryStringParameters": { "id": ["2"] },
  // This contains pathParams, if you url looks like users/{id}, this object will contain a key called id containing the value from the URL
  "pathParameters": null,
  "stageVariables": null,
  "requestContext": {
    "resourceId": "xxxxx",
    "resourcePath": "/users/single",
    "httpMethod": "GET",
    "extendedRequestId": "xxxxxxx=",
    "requestTime": "xx/xx/xxxx:xx:xx:xx +0000",
    "path": "/dev/users/single",
    "accountId": "642495909037",
    "protocol": "HTTP/1.1",
    "stage": "dev",
    "domainPrefix": "xxxxx",
    "requestTimeEpoch": 1547113372715,
    "requestId": "xx-xx-xx-xxxxx-xxxxxxxxx",
    "identity": {
      "cognitoIdentityPoolId": null,
      "accountId": null,
      "cognitoIdentityId": null,
      "caller": null,
      "sourceIp": "xx.xx.xx.xx",
      "accessKey": null,
      "cognitoAuthenticationType": null,
      "cognitoAuthenticationProvider": null,
      "userArn": null,
      "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
      "user": null
    },
    "domainName": "xxxxxxx.execute-api.xxxxxxx.amazonaws.com",
    "apiId": "xxxx"
  },
  "body": null, //this contains the post body
  "isBase64Encoded": false
}
正文键始终是字符串,您必须根据内容类型(即json或www表单编码或其他任何内容)对其进行解析

使用Lambda代理时,从处理程序返回的对象必须遵循特定的格式,根据API网关将其映射回响应,即:

{
    statusCode: Integer,
    headers: HashTable<String, String>,
    body: String
}

我不知道serverless是如何工作的,我的工作流程仅限于本地编写代码和上传ZIP,任何其他配置都是在AWS控制台上完成的

我相信您正在寻找一个名为的小功能,您将在API网关的Integration Request选项卡下找到它。它所做的是为请求和响应提供两个标准映射模板

使用Lambda代理集成时,事件对象将如下所示:

{
  "resource": "/users/single",
  "path": "/users/single",
  "httpMethod": "GET",
  "headers": {
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
    "accept-encoding": "gzip, deflate, br",
    "accept-language": "en-GB,en-US;q=0.9,en;q=0.8",
    "Host": "xxxxxxx.execute-api.xxxx.amazonaws.com",
    "upgrade-insecure-requests": "1",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
    "X-Amzn-Trace-Id": "Root=xxxxxxxxxxxxxxxxx",
    "X-Forwarded-For": "xx.xx.xx.xx",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "multiValueHeaders": {
    "accept": [
      "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
    ],
    "accept-encoding": ["gzip, deflate, br"],
    "accept-language": ["en-GB,en-US;q=0.9,en;q=0.8"],
    "Host": ["xxxxx.execute-api.xxxx.amazonaws.com"],
    "upgrade-insecure-requests": ["1"],
    "User-Agent": [
      "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
    ],
    "X-Amzn-Trace-Id": ["Root=xxxxx"],
    "X-Forwarded-For": ["xx.xx.xx.xx"],
    "X-Forwarded-Port": ["443"],
    "X-Forwarded-Proto": ["https"]
  },
  // this contains the get body
  "queryStringParameters": { "id": "2" },
  "multiValueQueryStringParameters": { "id": ["2"] },
  // This contains pathParams, if you url looks like users/{id}, this object will contain a key called id containing the value from the URL
  "pathParameters": null,
  "stageVariables": null,
  "requestContext": {
    "resourceId": "xxxxx",
    "resourcePath": "/users/single",
    "httpMethod": "GET",
    "extendedRequestId": "xxxxxxx=",
    "requestTime": "xx/xx/xxxx:xx:xx:xx +0000",
    "path": "/dev/users/single",
    "accountId": "642495909037",
    "protocol": "HTTP/1.1",
    "stage": "dev",
    "domainPrefix": "xxxxx",
    "requestTimeEpoch": 1547113372715,
    "requestId": "xx-xx-xx-xxxxx-xxxxxxxxx",
    "identity": {
      "cognitoIdentityPoolId": null,
      "accountId": null,
      "cognitoIdentityId": null,
      "caller": null,
      "sourceIp": "xx.xx.xx.xx",
      "accessKey": null,
      "cognitoAuthenticationType": null,
      "cognitoAuthenticationProvider": null,
      "userArn": null,
      "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
      "user": null
    },
    "domainName": "xxxxxxx.execute-api.xxxxxxx.amazonaws.com",
    "apiId": "xxxx"
  },
  "body": null, //this contains the post body
  "isBase64Encoded": false
}
正文键始终是字符串,您必须根据内容类型(即json或www表单编码或其他任何内容)对其进行解析

使用Lambda代理时,从处理程序返回的对象必须遵循特定的格式,根据API网关将其映射回响应,即:

{
    statusCode: Integer,
    headers: HashTable<String, String>,
    body: String
}

因此,在Serverless中,这里有一些集成选项

为了澄清这一点,您使用的是lambda,因此您可以在您提供的API网关中手动定义模板

当您尝试让serverless.yml使用lambda代理,或者将其作为aws代理或aws_代理接受时,您的意思是您以不同的格式将所有内容发送给您,但不包括原始正文

旁注:就API网关的LAMBDA_代理集成而言,您应该获得完整的请求正文。这是我一直使用的集成,以及{proxy+}请求路径上的任何方法,以专门避免映射模板。我不确定无服务器框架是否最终会对事件进行额外的解析,但您确实应该将事件的整个主体交给Lambda函数处理程序。我已经为AWS serverless编写了一个框架,这就是我在那里使用的,我处理的是JSON以外的请求格式。所以我知道你可以得到原始的身体。在这种情况下,您需要使用框架吗

好的,我的理解是,您不能使用lambda代理集成,必须求助于API网关中的自定义映射

我真的需要看看CloudWatch的一些错误。我还希望看到一些您希望收到的示例请求体示例。你说有错误,但没有发布任何关于它们的信息。我的假设是,这是API网关中的模板问题。自从我再次详细研究映射以来,已经有一段时间了,LAMBDA_代理集成是一条路要走,但是让我抛出一些想法

记住,$input.body可能包含JSON,这可能会弄乱模板。这将创建一个错误,并且您的Lambda将永远不会被触发。在CloudWatch中,您会看到无法解析东西的情况

您可以尝试$util.escapeJavaScript函数。您也可以尝试使用$util.base64Decode函数,这需要在API上启用二进制支持

API网关可以处理二进制数据,以base64字符串表示,这是避免模板映射问题的一种方法。然后,例如rawBody:$util.base64Decode$input.body将在映射模板中工作

要启用二进制支持,请转到API网关API的设置,您将看到二进制媒体类型部分。您可以提供任何您想要的内容类型字符串,如果您真的想要,甚至可以提供application/json。我认为如果你接受JSON,你可能会 ably可以很好地解析它,可能还可以逃跑……但是如果你遇到了一些奇怪的事情,你可能需要这样做。请记住,这是一个API范围的设置。我认为,从您共享的内容来看,您只需在此处使用application/x-www-form-urlencoded,因此正常的JSON请求不会受到影响


底线是某个地方有一个解析错误。

所以在无服务器系统中,这里有一些集成选项

为了澄清这一点,您使用的是lambda,因此您可以在您提供的API网关中手动定义模板

当您尝试让serverless.yml使用lambda代理,或者将其作为aws代理或aws_代理接受时,您的意思是您以不同的格式将所有内容发送给您,但不包括原始正文

旁注:就API网关的LAMBDA_代理集成而言,您应该获得完整的请求正文。这是我一直使用的集成,以及{proxy+}请求路径上的任何方法,以专门避免映射模板。我不确定无服务器框架是否最终会对事件进行额外的解析,但您确实应该将事件的整个主体交给Lambda函数处理程序。我已经为AWS serverless编写了一个框架,这就是我在那里使用的,我处理的是JSON以外的请求格式。所以我知道你可以得到原始的身体。在这种情况下,您需要使用框架吗

好的,我的理解是,您不能使用lambda代理集成,必须求助于API网关中的自定义映射

我真的需要看看CloudWatch的一些错误。我还希望看到一些您希望收到的示例请求体示例。你说有错误,但没有发布任何关于它们的信息。我的假设是,这是API网关中的模板问题。自从我再次详细研究映射以来,已经有一段时间了,LAMBDA_代理集成是一条路要走,但是让我抛出一些想法

记住,$input.body可能包含JSON,这可能会弄乱模板。这将创建一个错误,并且您的Lambda将永远不会被触发。在CloudWatch中,您会看到无法解析东西的情况

您可以尝试$util.escapeJavaScript函数。您也可以尝试使用$util.base64Decode函数,这需要在API上启用二进制支持

API网关可以处理二进制数据,以base64字符串表示,这是避免模板映射问题的一种方法。然后,例如rawBody:$util.base64Decode$input.body将在映射模板中工作

要启用二进制支持,请转到API网关API的设置,您将看到二进制媒体类型部分。您可以提供任何您想要的内容类型字符串,如果您真的想要,甚至可以提供application/json。我认为,如果您接受JSON,您可能可以很好地解析它,并进行转义……但是如果您遇到了一些奇怪的问题,您可能需要这样做。请记住,这是一个API范围的设置。我认为,从您共享的内容来看,您只需在此处使用application/x-www-form-urlencoded,因此正常的JSON请求不会受到影响


底线是某个地方有一个解析错误。

我意识到这是一个旧的线程,但一年半后我仍然有这个问题,我发现了许多其他类似的SO帖子,所以下面是2020年12月有效的方法:


默认的API网关设置是将输入视为文本,并将其序列化为JSON。这会导致非文本输入出现问题,例如带有图像/jpeg的多部分/表单数据。解决这一问题的最佳方法是为您预期为二进制数据的内容类型启用API网关二进制媒体类型。这将导致API网关将正文的base64编码值移交给Lambda,而不是尝试序列化为JSON。它还设置“isBase64Encoded”头。这样,您就可以使用Lambda代理集成,而不必乱搞映射模板或任何$util.escapeJavaScript。Lambda代码中唯一需要处理的变化是Base64解码身体。

我意识到这是一个旧的线索,但一年半后我仍然有这个问题,我发现了许多其他类似的SO帖子,因此,以下是2020年12月的工作内容:

默认的API网关设置是将输入视为文本,并将其序列化为JSON。这会导致非文本输入出现问题,例如带有图像/jpeg的多部分/表单数据。解决这一问题的最佳方法是为您预期为二进制数据的内容类型启用API网关二进制媒体类型。这将导致API网关将正文的base64编码值移交给Lambda,而不是尝试序列化为JSON。它还设置“isBase64Encoded”头。通过这种方式,您可以使用Lambda代理集成,而不必乱搞映射模板或任何
$util.escapeJavaScript的疯狂。Lambda代码中要处理的唯一更改是base64解码主体。

是的,我确实在使用代理集成,无服务器只是自动化了该部分。我添加的模板是我用来解析事件的模板,如果我只使用amazon生成的模板,它会工作,但是根据文档,$input.body应该包含原始请求,当我将其添加到模板时,它不工作,您发布的是解析请求的主体,我的问题是解析器模板。据我所知,解析发生在API网关和Lambda之间。因此,您发布的Apache Velocity代码段在原始HTTP请求上运行,将其转换为对象、HashMap或字典,然后将其传递给函数的事件变量。如果您使用代理集成,您不需要自己添加映射模板,aws会为您这样做。现在,如果您没有启用它,那么您可以手动添加一个映射模板,仅将所需的详细信息从HTTP请求正文中解析为您选择的格式化JSON,该正文是用Apache Velocity编写的。如果我不使用自定义映射模板,它只提供一个JSON解析正文,但我无法访问原始有效负载,这使我无法验证请求。Lambda代理始终将正文作为原始未解析字符串发送。也许关闭额外的映射模板,然后记录并检查事件对象,看看它包含什么。我只是添加了映射模板,因为我没有得到原始的未解析字符串。是的,我确实在使用代理集成,serverless只是自动化了该部分。我添加的模板是我用来解析事件的模板,如果我只使用amazon生成的模板,它会工作,但是根据文档,$input.body应该包含原始请求,当我将其添加到模板时,它不工作,您发布的是解析请求的主体,我的问题是解析器模板。据我所知,解析发生在API网关和Lambda之间。因此,您发布的Apache Velocity代码段在原始HTTP请求上运行,将其转换为对象、HashMap或字典,然后将其传递给函数的事件变量。如果您使用代理集成,您不需要自己添加映射模板,aws会为您这样做。现在,如果您没有启用它,那么您可以手动添加一个映射模板,仅将所需的详细信息从HTTP请求正文中解析为您选择的格式化JSON,该正文是用Apache Velocity编写的。如果我不使用自定义映射模板,它只提供一个JSON解析正文,但我无法访问原始有效负载,这使我无法验证请求。Lambda代理始终将正文作为原始未解析字符串发送。可能关闭额外的映射模板,然后记录并检查事件对象,查看其包含的内容。我只是添加了映射模板,因为我没有获得原始的未解析字符串。很抱歉,响应延迟,我一直忙于另一个项目,无法尽快返回。至于错误,我在lambda上的Cloudwatch上实际上没有看到任何错误——请求从未到达那里,我始终无法找到api网关上可以启用日志记录的位置。该请求是一个使用application/x-www-form-urlencoded的帖子,另一个是带有签名的标题,该签名是原始有效负载、标题和所有内容的签名版本,以及表单编码的正文,我将尝试提取一个示例。很抱歉响应延迟,我一直忙于另一个项目,无法尽快返回。至于错误,我在lambda上的Cloudwatch上实际上没有看到任何错误——请求从未到达那里,我始终无法找到api网关上可以启用日志记录的位置。该请求是一个使用application/x-www-form-urlencoded的帖子,另一个是带有签名的标题,该签名是原始有效负载、标题和所有内容的签名版本,以及表单编码的主体,我将尝试拉一个示例。为了澄清我遇到的一些事情,以防它们对其他人有帮助,原始有效负载是纯json,我只需要将字符串解析为json,然后将其重新字符串化,这帮助我解决了遇到的最大问题。最后,我不需要定制模板,提供的lambda代理就足够了。为了澄清我遇到的一些问题,以防它们对其他人有所帮助,原始负载是纯json,我只需要将字符串解析为json,然后再将其字符串化,这帮助我解决了遇到的最大问题。最后,我不需要定制模板,提供的lambda代理就足够了。