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

get the raw and parsed payloads from aws api gateway inside the event?

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

在我的 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: {} }

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

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

当您使用 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 代理时,您 return 来自您的处理程序的对象 必须 遵循特定格式,API 网关根据该格式将其映射回响应,即:

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

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

澄清一下,您使用的是 lambda,因此您需要在 API 网关(您提供的)中手动定义模板。

当您尝试让您的 serverless.yml 使用 lambda-proxy(或者被接受为 aws-proxyaws_proxy)时,您是在说您已将所有内容发送给您采用不包括原始主体的不同格式。

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

好的,我的理解是您无法使用 lambda-proxy 集成,必须求助于 API 网关中的自定义映射。

老实说,我需要查看 CloudWatch 的一些错误。我还想查看您期望收到的一些示例请求正文示例。你说有错误,但 post 什么都没有。我的假设是 API Gateway 中的模板问题。自从我详细处理映射以来已经有一段时间了(同样,LAMBDA_PROXY 集成是可行的方法),但让我抛出一些想法。

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

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

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

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

最重要的是某处存在解析错误。

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

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

进行 base64 解码