【问题标题】:get the raw and parsed payloads from aws api gateway inside the event?从事件内部的 aws api 网关获取原始和解析的有效负载?
【发布时间】:2019-04-02 14:55:50
【问题描述】:

我正在尝试验证对我在 lambda 上收到的事件的请求是否松弛,我目前正在使用带有 lambda 后端的 api-gateway。

在我的 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 代理控制台生成的文件的内容,我正在使用生成的方法请求直通和 一次修改,这只是将原始正文添加到要交付的有效负载中到 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"
    }
}

回答一些反馈。 如果我在Integration Request 中使用Use Lambda Proxy integration 我得到了这样的有效载荷,这很棒,但我还需要不存在的 rawbody。

{ 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: {} }

【问题讨论】:

  • 为了澄清我遇到的一些事情,以防他们帮助其他人,原始有效负载是纯 json,我只需要将字符串解析为 json,然后将其字符串化,这有助于我解决了我遇到的最大问题。最终我不需要自定义模板,提供的 lambda 代理就足够了。

标签: aws-lambda aws-api-gateway slack-api serverless


【解决方案1】:

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

我相信您正在寻找一个名为 Lambda Proxy Integration 的小功能,您可以在 API Gateway 的集成请求选项卡下找到它。它的作用是为 Request 和 Response 提供两个标准的映射模板。

当您使用 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
}

“body”键始终是字符串,您必须根据内容类型(即 json 或 www-form-encoded 或其他任何内容)对其进行解析。

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

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

【讨论】:

  • 是的,我确实在使用代理集成,无服务器只是自动化这部分。我添加的模板是我用来解析事件的模板,如果我只使用亚马逊生成的模板,它会起作用,但根据文档,当我将 $input.body 添加到模板,它不起作用,您发布的内容是解析请求的正文,我的问题是解析器模板。
  • 据我了解,解析发生在 API Gateway 和 Lambda 之间。因此,您发布的 Apache Velocity 代码 sn-p 在原始 HTTP 请求上运行,将其转换为 Object 或 HashMap 或 Dictionary 然后将其传递给函数的事件变量。如果您使用代理集成,则不需要自己添加映射模板,aws 会为您完成。现在,如果您没有启用它,那么您可以手动添加一个映射模板,仅将所需的详细信息解析为您从 HTTP 请求正文中选择的格式化 JSON,该正文是用 Apache Velocity 编写的.
  • 如果我不使用自定义映射模板,它只会提供一个 json 解析的正文,但我无法访问原始有效负载,这会阻止我验证请求。
  • Lambda 代理始终将正文作为未解析的原始字符串发送。也许关闭额外的映射模板,然后记录并检查事件对象并查看它包含的内容。
  • 我只添加了映射模板,因为我没有得到原始的未解析字符串。
【解决方案2】:

因此,在无服务器中,这里有一些集成选项。 https://serverless.com/framework/docs/providers/aws/events/apigateway/#request-templates

为了澄清,您使用的是lambda,因此您需要在 API Gateway(您提供)中手动定义您的模板。

当您尝试让您的 serverless.yml 使用 lambda-proxy(或者接受为 aws-proxy 或 aws_proxy)时,您是说您收到的所有内容都以不同的格式发送给您,其中不包括原始格式身体。

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

好的,所以我的理解是您不能使用 lambda-proxy 集成,必须求助于 API Gateway 中的自定义映射。

我确实需要查看 CloudWatch 中的一些错误。我还想查看您希望收到的一些示例请求正文示例。你说有错误,但没有发布任何关于它们的信息。我的假设是这是 API Gateway 中的模板问题。自从我详细处理映射以来已经有一段时间了(同样,LAMBDA_PROXY 集成是可行的方法),但让我抛出一些想法。

请记住,$input.body可能包含可能会弄乱模板的 JSON。这会产生错误,您的 Lambda 将永远不会被触发。在 CloudWatch 中,您会看到无法解析事物的情况。

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

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

要启用二进制支持,请转到 API Gateway API 的设置,您将看到Binary Media Types 部分。您可以在那里提供您想要的任何内容类型字符串,如果您真的想要,甚至可以提供application/json。我认为如果您接受 JSON,您可能可以很好地解析它(可能连同转义)......但是如果您遇到一些奇怪的事情,您可能需要这样做。请记住,这是一个 API 范围的设置。我认为从您分享的内容来看,您只是在这里使用application/x-www-form-urlencoded,因此例如正常的 JSON 请求不会受到影响。

底线是某处存在解析错误。

【讨论】:

  • 抱歉,回复延迟,我一直在忙另一个项目,不能早点回去。至于错误,我实际上在 lambda 上的 Cloudwatch 上没有看到任何错误(请求永远不会到达那里),我永远无法弄清楚我可以在 api 网关上的哪个位置启用日志记录。该请求是一个使用 application/x-www-form-urlencoded 的帖子,另一个是带有签名的标头(这是原始有效负载、标头和所有内容的签名版本)和表单编码的正文,我将尝试拉一个例子。
【解决方案3】:

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

默认 API Gateway 设置是将输入视为文本,并将其序列化为 JSON。这会导致非文本输入出现问题,例如带有图像/jpeg 的多部分/表单数据。解决此问题的最佳方法是为您希望为二进制数据的 Content-Type 启用 API 网关二进制媒体类型 (https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html)。这将导致 API Gateway 将 base64 编码值移交给 Lambda,而不是尝试序列化为 JSON(它还设置了“isBase64Encoded”标头)。这样,您可以使用 Lambda 代理集成,而不必乱用映射模板或任何 $util.escapeJavaScript() 疯狂。 Lambda 代码中唯一需要处理的更改是对正文进行 base64decode。

【讨论】:

    猜你喜欢
    • 2021-07-24
    • 1970-01-01
    • 1970-01-01
    • 2016-11-08
    • 2017-01-08
    • 2021-12-14
    • 2021-03-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多