Obtener el cuerpo json en aws Lambda a través de API gateway

84

Actualmente estoy usando NodeJS para crear un bot en AWS lambda a través de AWS Api Gateway y tengo un problema con las solicitudes POST y los datos JSON. Mi api usa 'Usar integración Lambda Proxy' e incluso cuando pruebo el proxy enviando un tipo de contenido de Application / json y algo de json en el cuerpo, por ejemplo, {"foo":"bar"}no puedo acceder al objeto sin analizarlo primero

p.ej

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

Ahora sé que esto no parece un gran problema con solo ejecutarlo a través de JSON.parse, pero he visto varios otros ejemplos en los que este no es el caso en absoluto. ver aquí https://github.com/pinzler/fb-messenger-bot-aws-lambda/blob/master/index.js

¿Necesito agregar algo a mi puerta de enlace API para manejar esto correctamente? mi paso 'cuerpo de solicitud' en la sección 'solicitud de método de publicación' tiene un tipo de contenido de configuración de aplicación / json para el cuerpo de solicitud.

El archivo Léame del ejemplo anterior no parece utilizar la integración de proxy por lo que puedo decir, por lo que no estoy seguro de qué debería hacer aquí.

TommyBs
fuente

Respuestas:

71

Hay dos integraciones de Lambda diferentes que puede configurar en API Gateway, como la integración de Lambda y la integración de proxy de Lambda. Para la integración de Lambda , puede personalizar lo que va a pasar a Lambda en la carga útil que no necesita para analizar el cuerpo, pero cuando utiliza la integración de Lambda Proxy en API Gateway, API Gateway enviará todo a Lambda en la carga útil. Me gusta esto,

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

Para el ejemplo al que hace referencia, no obtiene el cuerpo de la solicitud original. Está construyendo el cuerpo de respuesta a API Gateway. Debería estar en este formato,

{
    "statusCode": httpStatusCode,
    "headers": { "headerName": "headerValue", ... },
    "body": "...",
    "isBase64Encoded": false
}
Ka Hou Ieong
fuente
23
y luego JSON.parse (event.body) (Integración de proxy Lambda)
timhc22
3
@ timhc22 este es el punto principal, la única línea debería ser la respuesta aceptada
5413668060
49

Creo que hay algunas cosas que hay que entender al trabajar con la integración de API Gateway con Lambda.

Integración Lambda vs Integración Lambda Proxy

Solía ​​haber solo Integración Lambda que requiere plantillas de mapeo. Supongo que es por eso que todavía veo muchos ejemplos usándolo.

  • Cómo pasar un parámetro de ruta o cadena de consulta a AWS Lambda desde Amazon API Gateway

    A partir de septiembre de 2017, ya no es necesario configurar asignaciones para acceder al cuerpo de la solicitud.

  • Arquitectura sin servidor en AWS

    Integración de Lambda Proxy: si la habilita, API Gateway asignará cada solicitud a JSON y la pasará a Lambda como el objeto de evento. En la función Lambda, podrá recuperar parámetros de cadena de consulta, encabezados, variables de etapa, parámetros de ruta, contexto de solicitud y el cuerpo de la misma.

    Sin habilitar la Integración de Lambda Proxy, tendrá que crear una plantilla de asignación en la sección Solicitud de integración de API Gateway y decidir cómo asignar la solicitud HTTP a JSON usted mismo. Y probablemente tendría que crear un mapeo de respuesta de integración si tuviera que devolver la información al cliente.

    Antes de que se agregara la Integración de proxy de Lambda, los usuarios se vieron obligados a mapear solicitudes y respuestas manualmente, lo que era una fuente de consternación , especialmente con mapeos más complejos.

el cuerpo es una cadena de escape, no JSON

Con Lambda Proxy Integration, el cuerpo en caso de lambda es una cadena de escape con barra invertida, no un JSON.

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

Si se prueba en un formateador JSON.

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

El documento a continuación trata sobre la respuesta, pero debería aplicarse a la solicitud.

Para que JavaScript acceda a él como un objeto JSON, es necesario convertirlo nuevamente en un objeto JSON con json.parse en JapaScript, json.dumps en Python.

  • Cómo trabajar con JSON en JavaScript

    Las cadenas son útiles para el transporte, pero querrá poder convertirlas nuevamente en un objeto JSON en el lado del cliente y / o del servidor.

La documentación de AWS muestra qué hacer.

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);
Lun
fuente
Gracias por señalar que el cuerpo es una cadena de escape, NO JSON.
nngeek
1

Estoy usando lambda con Zappa; Estoy enviando datos con POST en formato json:

Mi código para basic_lambda_pure.py es:

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'}}

Obtuve la siguiente respuesta 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
}

mis datos estaban en la clave del cuerpo , pero están codificados en code64, ¿cómo puedo saber esto? Vi la clave isBase64Encoded

Copio el valor de la clave del cuerpo y lo decodifico con Esta herramienta y "eureka", obtengo los valores.

Espero que esto te ayude. :)

Egalicia
fuente