Getting json body in aws Lambda via API gateway

I'm currently using NodeJS to build a bot on AWS lambda via AWS Api Gateway and I'm running into an issue with POST requests and JSON data. My api uses 'Use Lambda Proxy integration' and even when I test the proxy sending a content-type of Application/json and some json in the body e.g {"foo":"bar"} I can't access the object without parsing it first

e.g

  var json = JSON.parse(event.body);
console.log(json.foo);

Now I know this doesn't seem a big deal just running it through JSON.parse, but I've seen a number of other examples where this isn't the case at all. see here https://github.com/pinzler/fb-messenger-bot-aws-lambda/blob/master/index.js

Do I need to add anything to my API gateway to handle this correctly? my 'request body' step in the 'post method request' section has a content-type of application/json set-up for the request body.

The readme for the example above doesn't seem to use proxy integration as far as I can tell so I'm not sure what I should be doing here

190802 次浏览

在 API 网关中可以配置两种不同的 Lambda 集成:

  1. Lambda non-proxy integration (医生), also called Lambda custom integration
  2. Lambda proxy integration (医生)

对于 Lambda 非代理集成,你可以自定义在有效负载中传递给 Lambda 的内容,而不需要解析主体,但是当你在 API 网关中使用 Lambda 代理整合时,API 网关会在有效负载中将所有内容代理给 Lambda,如下所示:

{
"message": "Hello me!",
"input": {
"path": "/test/hello",
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, lzma, sdch, br",
"Accept-Language": "en-US,en;q=0.8",
"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",
"Host": "wt6mne2s9k.execute-api.us-west-2.amazonaws.com",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48",
"Via": "1.1 fb7cca60f0ecd82ce07790c9c5eef16c.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "nBsWBOrSHMgnaROZJK1wGCZ9PcRcSpq_oSXZNQwQ10OTZL4cimZo3g==",
"X-Forwarded-For": "192.168.100.1, 192.168.1.1",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
"pathParameters": {"proxy": "hello"},
"requestContext": {
"accountId": "123456789012",
"resourceId": "us4z18",
"stage": "test",
"requestId": "41b45ea3-70b5-11e6-b7bd-69b5aaebc7d9",
"identity": {
"cognitoIdentityPoolId": "",
"accountId": "",
"cognitoIdentityId": "",
"caller": "",
"apiKey": "",
"sourceIp": "192.168.100.1",
"cognitoAuthenticationType": "",
"cognitoAuthenticationProvider": "",
"userArn": "",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48",
"user": ""
},
"resourcePath": "/{proxy+}",
"httpMethod": "GET",
"apiId": "wt6mne2s9k"
},
"resource": "/{proxy+}",
"httpMethod": "GET",
"queryStringParameters": {"name": "me"},
"stageVariables": {"stageVarName": "stageVarValue"},
"body": "{\"foo\":\"bar\"}",
"isBase64Encoded": false
}
}

对于您引用的示例,它没有从原始请求获取主体。它正在构建回到 API 网关的响应体。它应该是这样的格式:

{
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"body": "...",
"isBase64Encoded": false
}

我和 Zappa 一起使用 lambda; 我用 json 格式的 POST 发送数据:

我的 basic _ lambda _ pure.py 代码是:

import time
import requests
import json
def my_handler(event, context):
print("Received event: " + json.dumps(event, indent=2))
print("Log stream name:", context.log_stream_name)
print("Log group name:",  context.log_group_name)
print("Request ID:", context.aws_request_id)
print("Mem. limits(MB):", context.memory_limit_in_mb)
# Code will execute quickly, so we add a 1 second intentional delay so you can see that in time remaining value.
print("Time remaining (MS):", context.get_remaining_time_in_millis())


if event["httpMethod"] == "GET":
hub_mode = event["queryStringParameters"]["hub.mode"]
hub_challenge = event["queryStringParameters"]["hub.challenge"]
hub_verify_token = event["queryStringParameters"]["hub.verify_token"]
return {'statusCode': '200', 'body': hub_challenge, 'headers': 'Content-Type': 'application/json'}}


if event["httpMethod"] == "post":
token = "xxxx"
params = {
"access_token": token
}
headers = {
"Content-Type": "application/json"
}
_data = {"recipient": {"id": 1459299024159359}}
_data.update({"message": {"text": "text"}})
data = json.dumps(_data)
r = requests.post("https://graph.facebook.com/v2.9/me/messages",params=params, headers=headers, data=data, timeout=2)
return {'statusCode': '200', 'body': "ok", 'headers': {'Content-Type': 'application/json'}}

我得到了下一个 Json 的回复:

{
"resource": "/",
"path": "/",
"httpMethod": "POST",
"headers": {
"Accept": "*/*",
"Accept-Encoding": "deflate, gzip",
"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": "ox53v9d8ug.execute-api.us-east-1.amazonaws.com",
"Via": "1.1 f1836a6a7245cc3f6e190d259a0d9273.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "LVcBZU-YqklHty7Ii3NRFOqVXJJEr7xXQdxAtFP46tMewFpJsQlD2Q==",
"X-Amzn-Trace-Id": "Root=1-59ec25c6-1018575e4483a16666d6f5c5",
"X-Forwarded-For": "69.171.225.87, 52.46.17.84",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https",
"X-Hub-Signature": "sha1=10504e2878e56ea6776dfbeae807de263772e9f2"
},
"queryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
"path": "/dev",
"accountId": "001513791584",
"resourceId": "i6d2tyihx7",
"stage": "dev",
"requestId": "d58c5804-b6e5-11e7-8761-a9efcf8a8121",
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
"cognitoIdentityId": null,
"caller": null,
"apiKey": "",
"sourceIp": "69.171.225.87",
"accessKey": null,
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": null,
"userAgent": null,
"user": null
},
"resourcePath": "/",
"httpMethod": "POST",
"apiId": "ox53v9d8ug"
},
"body": "eyJvYmplY3QiOiJwYWdlIiwiZW50cnkiOlt7ImlkIjoiMTA3OTk2NDk2NTUxMDM1IiwidGltZSI6MTUwODY0ODM5MDE5NCwibWVzc2FnaW5nIjpbeyJzZW5kZXIiOnsiaWQiOiIxNDAzMDY4MDI5ODExODY1In0sInJlY2lwaWVudCI6eyJpZCI6IjEwNzk5NjQ5NjU1MTAzNSJ9LCJ0aW1lc3RhbXAiOjE1MDg2NDgzODk1NTUsIm1lc3NhZ2UiOnsibWlkIjoibWlkLiRjQUFBNHo5RmFDckJsYzdqVHMxZlFuT1daNXFaQyIsInNlcSI6MTY0MDAsInRleHQiOiJob2xhIn19XX1dfQ==",
"isBase64Encoded": true
}

我的数据是在 body的关键,但代码64编码,我怎么知道这一点? 我看到的关键 是 Base64编码的

我复制 尸体键的值,然后用 这个工具和“ eureka”解码,就得到了值。

我希望这个能帮到你

我认为在使用与 Lambda 的 API 网关集成时,有一些事情需要理解。

Lambda 集成与 Lambda 代理集成

过去只有 Lambda 集成需要映射模板。我想这就是为什么仍然看到许多使用它的例子。

从2017年9月开始,您不再需要配置映射来访问请求主体。

如果启用了 Lambda 代理集成,API 网关将把每个请求映射到 JSON,并将其作为事件对象传递给 Lambda。在 Lambda 函数中,您将能够从中检索查询字符串参数、标头、阶段变量、路径参数、请求上下文和主体。

如果不启用 Lambda 代理集成,则必须在 API 网关的 Integration Request 部分中创建映射模板,并自己决定如何将 HTTP 请求映射到 JSON。如果要将信息传递回客户机,则可能必须创建 IntegrationResponse 映射。

在添加 Lambda 代理集成之前,用户被迫手动地图请求和响应,这是一个惊愕的来源,特别是使用更复杂的映射。

语言需要引导思维,才能理解术语。

  • Lambda 代理集成 = 传递
    只需将 HTTP 请求传递给 lambda。

  • Lambda 集成 = 模板转换
    使用 Apache Velocity 模板完成一个转换过程,您需要自己编写模板。

Body 是转义字符串,而不是 JSON

Using Lambda Proxy Integration, the body in the event of lambda is a string escaped with backslash, not a JSON.

"body": "{\"foo\":\"bar\"}"

如果在 JSON 格式化程序中测试。

Parse error on line 1:
{\"foo\":\"bar\"}
-^
Expecting 'STRING', '}', got 'undefined'

下面的文档是关于响应的,但它应该适用于请求。

如果要返回 JSON,那么 body 字段必须转换为字符串,否则将导致响应出现进一步问题。您可以在 Node.js 函数中使用 JSON.stringify 来处理这个问题; 其他运行时需要不同的解决方案,但是概念是相同的。

为了让 JavaScript 以 JSON 对象的形式访问它,需要使用 JavaScript 中的 JSON.parse,Python 中的 JSON.dump 将其转换回 JSON 对象。

Strings are useful for transporting but you’ll want to be able to convert them back to a JSON object on the client and/or the server side.

AWS 文档告诉我们该怎么做。

if (event.body !== null && event.body !== undefined) {
let body = JSON.parse(event.body)
if (body.time)
time = body.time;
}
...
var response = {
statusCode: responseCode,
headers: {
"x-custom-header" : "my custom header value"
},
body: JSON.stringify(responseBody)
};
console.log("response: " + JSON.stringify(response))
callback(null, response);

You may have forgotten to define the Content-Type header. For example:

  return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ items }),
}

如果您正在使用 Lambda 代理集成,则需要对 JavaScript 使用 JSON.parse(event.body),对 Python 使用 json.loads(event["body"])

您正在查看的其他示例是 AWS 服务 Lambda 集成(这与 Lambda 代理集成不同) ,它们在其中自己构建了事件模板。

有关 Lambda 代理集成的结构的更多细节,请参见 https://stackoverflow.com/a/41656022/2800876

通过 API 网关获取 AWS Lambda (C #)中的 json 主体,并使用 AWS. NET Mock Lambda 测试工具进行测试

如果您试图使用 .NET 模拟 Lambda 测试工具或其他工具(如 Postman)在本地调试您的 Lambda 函数,并希望通过启用 Lambda 代理集成的 AWS API 网关公开您的 Lambda 函数,请遵循以下步骤:

1-在 Mock Lambda 测试工具的示例请求下选择“ API 网关 AWS 代理”。您得到一个长 json,其中至少需要修改 bodypath成员

{
"body": "{\"foo\":\"bar\"}",
"path": "/YourLambdafunction",
...
}

2-使用第一个参数的 APIGatewayProxyRequest数据类型定义 FunctionHandler,然后可以对参数 Body成员进行强反序列化

public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest data, ILambdaContext context)
{
MyCustomClass? myObj = JsonConvert.DeserializeObject<MyCustomClass>(data.Body);
}