ChatGPT解决这个技术问题 Extra ChatGPT

通过 API 网关在 aws Lambda 中获取 json 正文

我目前正在使用 NodeJS 通过 AWS Api Gateway 在 AWS lambda 上构建一个机器人,并且遇到了 POST 请求和 JSON 数据的问题。我的 api 使用“使用 Lambda 代理集成”,即使我测试代理发送 Application/json 的内容类型和正文中的一些 json 例如{"foo":"bar"}我无法访问该对象而不先解析它

例如

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

现在我知道通过 JSON.parse 运行它似乎没什么大不了的,但是我已经看到了许多其他示例,其中根本不是这种情况。看这里https://github.com/pinzler/fb-messenger-bot-aws-lambda/blob/master/index.js

我是否需要向我的 API 网关添加任何内容才能正确处理此问题?我在“发布方法请求”部分中的“请求正文”步骤为请求正文设置了应用程序/json 的内容类型。

据我所知,上面示例的自述文件似乎没有使用代理集成,所以我不确定我应该在这里做什么


j
jarmod

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

Lambda 非代理集成(文档),也称为 Lambda 自定义集成 Lambda 代理集成(文档)

对于 Lambda 非代理集成,您可以自定义要在不需要解析正文的负载中传递给 Lambda 的内容,但是当您在 API Gateway 中使用 Lambda 代理集成时,API Gateway 会将所有内容代理到有效载荷中的 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 Gateway。它应该是这种格式:

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

然后 JSON.parse(event.body) (Lambda 代理集成)
@timhc22 这是重点,单行应该是公认的答案
m
mon

我认为在使用 API Gateway 与 Lambda 集成时需要了解一些事项。

Lambda 集成与 Lambda 代理集成

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

如何将查询字符串或路由参数从 Amazon API Gateway 传递到 AWS Lambda

自 2017 年 9 月起,您不再需要配置映射来访问请求正文。

AWS 上的无服务器架构

Lambda 代理集成,如果您启用它,API Gateway 会将每个请求映射到 JSON 并将其作为事件对象传递给 Lambda。在 Lambda 函数中,您将能够从中检索查询字符串参数、标头、阶段变量、路径参数、请求上下文和正文。如果不启用 Lambda 代理集成,您必须在 API Gateway 的集成请求部分创建一个映射模板,并自行决定如何将 HTTP 请求映射到 JSON。如果要将信息传递回客户端,您可能必须创建一个集成响应映射。在添加 Lambda 代理集成之前,用户被迫手动映射请求和响应,这是一个令人惊愕的原因,尤其是对于更复杂的映射。

文字需要引导思维。让术语直截了当。

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

Lambda 集成 = 模板转换 使用 Apache Velocity 模板进行转换过程,您需要自己编写模板。

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

使用 Lambda 代理集成,在 lambda 事件中的正文是一个用反斜杠转义的字符串,而不是 JSON。

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

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

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

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

我收到“格式错误的 Lambda 代理响应”错误或 Amazon API Gateway 的 502 状态代码。我该如何解决这个问题?

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

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

如何在 JavaScript 中使用 JSON

字符串对于传输很有用,但您希望能够将它们转换回客户端和/或服务器端的 JSON 对象。

AWS documentation 显示要做什么。

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);

感谢您指出正文只是转义字符串而不是 JSON。
Z
Zags

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

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

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


E
Egalicia

我在 Zappa 中使用 lambda;我正在使用 POST 以 json 格式发送数据:

我的 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
}

我的数据在正文键上,但是是 code64 编码的,我怎么知道?我看到关键是Base64Encoded

我复制 body 键的值并使用 This tool 和“eureka”解码,我得到了值。

我希望这对你有帮助。 :)


D
David

您可能忘记定义 Content-Type 标头。例如:

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