Configuración de las integraciones de proxy de Lambda en API Gateway - Amazon API Gateway

Configuración de las integraciones de proxy de Lambda en API Gateway

Descripción de la integración de proxy de Lambda en API Gateway

La integración de proxy de Lambda de Amazon API Gateway es un mecanismo sencillo, potente y ágil para desarrollar una API con la configuración de un solo método de API. La integración de proxy de Lambda permite al cliente llamar a una única función de Lambda en el backend. La función obtiene acceso a muchos recursos o características de otros servicios de AWS, incluidas las llamadas a otras funciones Lambda.

En la integración de proxy de Lambda, cuando un cliente envía una solicitud de API, API Gateway pasa a la función de Lambda integrada un evento de objeto, salvo que no se conserve el orden de los parámetros de la solicitud. Estos datos de la solicitud incluyen los encabezados de solicitud, los parámetros de cadena de consulta, las variables de ruta de la URL, la carga y los datos de configuración de la API. Los datos de configuración pueden incluir el nombre de la etapa de implementación actual, las variables de la etapa, la identidad del usuario o el contexto de autorización (si lo hubiera). La función de Lambda del backend analiza los datos de la solicitud entrante para determinar la respuesta que devuelve. Para que API Gateway transmita la salida de Lambda como la respuesta de la API al cliente, la función de Lambda debe devolver el resultado en este formato.

Dado que API Gateway no interviene mucho entre el cliente y la función de Lambda del backend para la integración de proxy de Lambda, el cliente y la función de Lambda integrada pueden adaptarse a los cambios de cada uno sin interrumpir la configuración de integración existente de la API. Para habilitar esto, el cliente debe seguir los protocolos de aplicación promulgados por la función de Lambda del backend.

Puede configurar una integración de proxy de Lambda para cualquier método de API. Sin embargo, una integración de proxy de Lambda es más potente cuando se configura para un método de API que implica un recurso de proxy genérico. El recurso de proxy genérico se puede indicar mediante una variable especial de ruta basada en plantilla de {proxy+}, el marcador de posición del método catch-all ANY o ambos. El cliente puede transmitir la entrada a la función de Lambda del backend en la solicitud entrante según lo soliciten los parámetros o la carga aplicable. Los parámetros de solicitud incluyen encabezados, variables de ruta de la URL, parámetros de cadenas de consulta y la carga aplicable. La función de Lambda integrada verifica todas las fuentes de entrada antes de procesar la solicitud y de responder al cliente con mensajes de error importantes si falta alguna de las entradas requeridas.

Al llamar a un método de API integrado con el método HTTP genérico de ANY y el recurso genérico de {proxy+}, el cliente envía una solicitud con un método HTTP específico en lugar de ANY. El cliente también especifica una ruta de URL específica en lugar de {proxy+} e incluye cualquier encabezado, parámetro de cadena de consulta o una carga aplicable.

En la siguiente lista se resumen los comportamientos de tiempo de ejecución de los diferentes métodos de API con la integración de proxy de Lambda:

  • ANY /{proxy+}: el cliente debe elegir un método HTTP específico, debe establecer una jerarquía de ruta de recursos específica y puede establecer cualquier encabezado, parámetro de cadena de consulta y carga aplicable para transmitir los datos como entrada a la función de Lambda integrada.

  • ANY /res: el cliente debe elegir un método HTTP específico y puede establecer cualquier encabezado, parámetro de cadena de consulta y carga aplicable para transmitir los datos como entrada a la función de Lambda integrada.

  • GET|POST|PUT|... /{proxy+}: el cliente puede establecer una jerarquía de ruta de recursos específica, cualquier encabezado, parámetro de cadena de consulta y carga aplicable para transmitir los datos como entrada a la función de Lambda integrada.

  • GET|POST|PUT|... /res/{path}/...: el cliente debe elegir un segmento de ruta específico (para la variable {path}) y puede establecer cualquier encabezado de solicitud, parámetro de cadena de consulta y carga aplicable para transmitir los datos de entrada a la función de Lambda integrada.

  • GET|POST|PUT|... /res: el cliente puede elegir cualquier encabezado de solicitud, parámetro de cadena de consulta y carga aplicable para transmitir los datos de entrada a la función de Lambda integrada.

Tanto el recurso de proxy de {proxy+} como el recurso personalizado de {custom} se expresan como variables de ruta basada en plantilla. Sin embargo, {proxy+} puede hacer referencia a cualquier recurso en una jerarquía de ruta, mientras que {custom} se refiere únicamente a un segmento de ruta específico. Por ejemplo, una tienda de comestibles podría organizar un inventario de productos en línea por nombres de departamento, categorías de productos y tipos de productos. El sitio web de la tienda de comestibles puede entonces representar los productos disponibles con las siguientes variables de ruta basada en plantilla para los recursos personalizados: /{department}/{produce-category}/{product-type}. Por ejemplo, las manzanas están representadas con /produce/fruit/apple y las zanahorias con /produce/vegetables/carrot. También puede utilizar /{proxy+} para representar a cualquier departamento, cualquier categoría de producto o cualquier tipo de producto que un cliente puede buscar mientras realiza sus compras en la tienda en línea. Por ejemplo, /{proxy+} puede hacer referencia a cualquiera de los siguientes elementos:

  • /produce

  • /produce/fruit

  • /produce/vegetables/carrot

Para que los clientes puedan buscar cualquier producto disponible, su categoría de producto y el departamento asociado en la tienda, puede exponer un único método de GET /{proxy+} con permisos de solo lectura. Del mismo modo, para permitir que un supervisor actualice el inventario del departamento produce, puede configurar otro método único de PUT /produce/{proxy+} con permisos de lectura y escritura. Para permitir que un cajero actualice el total de unidades de una verdura, puede configurar un método POST /produce/vegetables/{proxy+} con permisos de lectura y escritura. Para que el gerente de una tienda pueda realizar cualquier acción posible en cualquier producto disponible, el desarrollador de la tienda en línea puede exponer el método ANY /{proxy+} con permisos de lectura y escritura. En cualquier caso, en el tiempo de ejecución, el cliente o el empleado debe seleccionar un producto específico de un tipo determinado en un departamento elegido o en un departamento específico.

Para obtener más información acerca de la configuración de las integraciones del proxy de API Gateway, consulte Configuración de una integración de proxy con un recurso de proxy.

La integración del proxy requiere que el cliente tenga un conocimiento más detallado de los requisitos del backend. Por lo tanto, para garantizar un óptimo rendimiento de la aplicación y experiencia del usuario, el desarrollador del backend debe comunicar con claridad al desarrollador del cliente los requisitos del backend y proporcionar un mecanismo de comentarios de errores sólido cuando los requisitos no se cumplan.

Compatibilidad con encabezados de varios valores y parámetros de cadenas de consulta

API Gateway ahora es compatible con varios encabezados y parámetros de cadena de consulta que tengan el mismo nombre. Los encabezados de varios valores y los encabezados y parámetros de un solo valor se pueden combinar en las mismas solicitudes y respuestas. Para obtener más información, consulte Formato de entrada de una función de Lambda para la integración de proxy y Formato de salida de una función de Lambda para la integración de proxy.

Configuración de un recurso de proxy con la integración de proxy de Lambda

Para configurar un recurso de proxy con el tipo de integración de proxy de Lambda, cree un recurso de API con un parámetro de ruta expansiva (por ejemplo, /parent/{proxy+}) e integre este recurso con un backend de funciones Lambda (por ejemplo, arn:aws:lambda:us-west-2:123456789012:function:SimpleLambda4ProxyResource) en el método ANY. El parámetro de ruta expansiva debe estar al final de la ruta del recurso de la API. Al igual que con un recurso que no es de proxy, puede configurar el recurso de proxy mediante la consola de API Gateway importando un archivo de definición de OpenAPI o llamando directamente a la API REST de API Gateway.

El siguiente archivo de definición de API de OpenAPI muestra un ejemplo de una API con un recurso de proxy que se integra con la función de Lambda denominada SimpleLambda4ProxyResource.

OpenAPI 3.0
{ "openapi": "3.0.0", "info": { "version": "2016-09-12T17:50:37Z", "title": "ProxyIntegrationWithLambda" }, "paths": { "/{proxy+}": { "x-amazon-apigateway-any-method": { "parameters": [ { "name": "proxy", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": {}, "x-amazon-apigateway-integration": { "responses": { "default": { "statusCode": "200" } }, "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:SimpleLambda4ProxyResource/invocations", "passthroughBehavior": "when_no_match", "httpMethod": "POST", "cacheNamespace": "roq9wj", "cacheKeyParameters": [ "method.request.path.proxy" ], "type": "aws_proxy" } } } }, "servers": [ { "url": "https://gy415nuibc.execute-api.us-east-1.amazonaws.com/{basePath}", "variables": { "basePath": { "default": "/testStage" } } } ] }
OpenAPI 2.0
{ "swagger": "2.0", "info": { "version": "2016-09-12T17:50:37Z", "title": "ProxyIntegrationWithLambda" }, "host": "gy415nuibc.execute-api.us-east-1.amazonaws.com", "basePath": "/testStage", "schemes": [ "https" ], "paths": { "/{proxy+}": { "x-amazon-apigateway-any-method": { "produces": [ "application/json" ], "parameters": [ { "name": "proxy", "in": "path", "required": true, "type": "string" } ], "responses": {}, "x-amazon-apigateway-integration": { "responses": { "default": { "statusCode": "200" } }, "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:SimpleLambda4ProxyResource/invocations", "passthroughBehavior": "when_no_match", "httpMethod": "POST", "cacheNamespace": "roq9wj", "cacheKeyParameters": [ "method.request.path.proxy" ], "type": "aws_proxy" } } } } }

Con la integración de proxy de Lambda, API Gateway asigna en tiempo de ejecución una solicitud entrante en el parámetro de entrada event de la función de Lambda. La entrada incluye el método de solicitud, la ruta, los encabezados, todos los parámetros de cadena de consulta, todas las cargas, el contexto asociado y todas las variables de etapa definidas. El formato de entrada se explica en Formato de entrada de una función de Lambda para la integración de proxy. Para que API Gateway asigne la salida de Lambda a respuestas HTTP correctamente, la función de Lambda debe producir el resultado en el formato que se describe en Formato de salida de una función de Lambda para la integración de proxy.

Con la integración de proxy de Lambda de un recurso de proxy a través del método ANY, la función de Lambda del backend actúa como el controlador de eventos de todas las solicitudes a través del recurso de proxy. Por ejemplo, para registrar patrones de tráfico, puede hacer que el dispositivo móvil envíe la información de ubicación del país, la ciudad, la calle y el edificio enviando una solicitud con /state/city/street/house en la ruta URL del recurso de proxy. La función de Lambda del backend puede analizar la ruta URL e insertar tuplas de ubicación en una tabla de DynamoDB.

Configuración de una integración de proxy de Lambda mediante la AWS CLI

En esta sección, mostraremos cómo utilizar la AWS CLI para configurar una API con la integración de proxy de Lambda.

nota

Para obtener instrucciones detalladas acerca de cómo utilizar la consola de API Gateway para configurar un recurso de proxy con la integración de proxy de Lambda, consulte Tutorial: Desarrollo de una API de REST Hello World con integración de proxy de Lambda.

Como ejemplo, utilizaremos la siguiente función de Lambda de muestra como el backend de la API:

export const handler = function(event, context, callback) { console.log('Received event:', JSON.stringify(event, null, 2)); var res ={ "statusCode": 200, "headers": { "Content-Type": "*/*" } }; var greeter = 'World'; if (event.greeter && event.greeter!=="") { greeter = event.greeter; } else if (event.body && event.body !== "") { var body = JSON.parse(event.body); if (body.greeter && body.greeter !== "") { greeter = body.greeter; } } else if (event.queryStringParameters && event.queryStringParameters.greeter && event.queryStringParameters.greeter !== "") { greeter = event.queryStringParameters.greeter; } else if (event.multiValueHeaders && event.multiValueHeaders.greeter && event.multiValueHeaders.greeter != "") { greeter = event.multiValueHeaders.greeter.join(" and "); } else if (event.headers && event.headers.greeter && event.headers.greeter != "") { greeter = event.headers.greeter; } res.body = "Hello, " + greeter + "!"; callback(null, res); };

Si lo comparamos con la configuración de la integración de Lambda personalizada, la entrada de esta función de Lambda se puede expresar en los parámetros de la solicitud y en el cuerpo. Tiene más libertad para permitir al cliente transferir los mismos datos de entrada. En este caso el cliente puede transferir el nombre de greeter como un parámetro de cadena de consulta, un encabezado o una propiedad del cuerpo. La función también puede admitir la integración de Lambda personalizada. La configuración de la API es más sencilla. No se configura ninguna respuesta de método ni de integración.

Para configurar una integración de proxy de Lambda mediante la AWS CLI
  1. Llame al comando create-rest-api para crear una API:

    aws apigateway create-rest-api --name 'HelloWorld (AWS CLI)' --region us-west-2

    Anote el valor id (te6si5ach7) de la API resultante en la respuesta:

    { "name": "HelloWorldProxy (AWS CLI)", "id": "te6si5ach7", "createdDate": 1508461860 }

    Necesitará el id de la API en esta sección.

  2. Llame al comando get-resources para obtener el id del recurso raíz:

    aws apigateway get-resources --rest-api-id te6si5ach7 --region us-west-2

    La respuesta correcta se muestra del modo siguiente:

    { "items": [ { "path": "/", "id": "krznpq9xpg" } ] }

    Anote el valor id (krznpq9xpg) del recurso raíz. Lo necesitará en el siguiente paso y más adelante.

  3. Llame a create-resource para crear un recurso de API Gateway para /greeting:

    aws apigateway create-resource --rest-api-id te6si5ach7 \ --region us-west-2 \ --parent-id krznpq9xpg \ --path-part {proxy+}

    La respuesta correcta será similar a la que se muestra a continuación:

    { "path": "/{proxy+}", "pathPart": "{proxy+}", "id": "2jf6xt", "parentId": "krznpq9xpg" }

    Anote el valor {proxy+} del recurso id (2jf6xt) resultante. Lo necesitará para crear un método en el recurso /{proxy+} en el siguiente paso.

  4. Llame a put-method para crear una solicitud de método ANY para ANY /{proxy+}:

    aws apigateway put-method --rest-api-id te6si5ach7 \ --region us-west-2 \ --resource-id 2jf6xt \ --http-method ANY \ --authorization-type "NONE"

    La respuesta correcta será similar a la que se muestra a continuación:

    { "apiKeyRequired": false, "httpMethod": "ANY", "authorizationType": "NONE" }

    Este método de API permite al cliente recibir o enviar saludos de la función de Lambda al backend.

  5. Llame a put-integration para configurar la integración del método ANY /{proxy+} con una función de Lambda denominada HelloWorld. Esta función responde a la solicitud con un mensaje de "Hello, {name}!", si se proporciona el parámetro greeter, o bien "Hello, World!", si no se ha establecido el parámetro de cadena de consulta.

    aws apigateway put-integration \ --region us-west-2 \ --rest-api-id te6si5ach7 \ --resource-id 2jf6xt \ --http-method ANY \ --type AWS_PROXY \ --integration-http-method POST \ --uri arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:123456789012:function:HelloWorld/invocations \ --credentials arn:aws:iam::123456789012:role/apigAwsProxyRole
    importante

    Para las integraciones Lambda, debe utilizar el método HTTP de POST para la solicitud de integración, según la especificación de la acción del servicio de Lambda para las invocaciones de funciones. El rol de IAM de apigAwsProxyRole debe tener políticas que permitan al servicio apigateway invocar funciones Lambda. Para obtener más información sobre los permisos de IAM, consulte Modelo de permisos de API Gateway para invocar una API.

    La salida correcta será similar a la que se muestra a continuación:

    { "passthroughBehavior": "WHEN_NO_MATCH", "cacheKeyParameters": [], "uri": "arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:1234567890:function:HelloWorld/invocations", "httpMethod": "POST", "cacheNamespace": "vvom7n", "credentials": "arn:aws:iam::1234567890:role/apigAwsProxyRole", "type": "AWS_PROXY" }

    En lugar de proporcionar un rol de IAM para credentials, puede llamar al comando add-permission para agregar permisos basados en recursos. Esto es lo que hace la consola de API Gateway.

  6. Llame a create-deployment para implementar la API en una etapa test:

    aws apigateway create-deployment --rest-api-id te6si5ach7 --stage-name test --region us-west-2
  7. Pruebe la API mediante los siguientes comandos cURL en un terminal.

    Llame a la API con el parámetro de cadena de consulta de ?greeter=jane:

    curl -X GET 'https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/greeting?greeter=jane'

    Llame a la API con un parámetro de encabezado de greeter:jane:

    curl -X GET https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/hi \ -H 'content-type: application/json' \ -H 'greeter: jane'

    Llame a la API con un cuerpo de {"greeter":"jane"}:

    curl -X POST https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/hi \ -H 'content-type: application/json' \ -d '{ "greeter": "jane" }'

    En todos estos casos, la salida es una respuesta 200 con el siguiente cuerpo de respuesta:

    Hello, jane!

Formato de entrada de una función de Lambda para la integración de proxy

Con la integración de proxy de Lambda, API Gateway asigna toda la solicitud de cliente al parámetro de entrada event de la función de Lambda del backend. En el siguiente ejemplo, se muestra la estructura de un evento que API Gateway envía a una integración de proxy de Lambda.

{ "resource": "/my/path", "path": "/my/path", "httpMethod": "GET", "headers": { "header1": "value1", "header2": "value1,value2" }, "multiValueHeaders": { "header1": [ "value1" ], "header2": [ "value1", "value2" ] }, "queryStringParameters": { "parameter1": "value1,value2", "parameter2": "value" }, "multiValueQueryStringParameters": { "parameter1": [ "value1", "value2" ], "parameter2": [ "value" ] }, "requestContext": { "accountId": "123456789012", "apiId": "id", "authorizer": { "claims": null, "scopes": null }, "domainName": "id.execute-api.us-east-1.amazonaws.com", "domainPrefix": "id", "extendedRequestId": "request-id", "httpMethod": "GET", "identity": { "accessKey": null, "accountId": null, "caller": null, "cognitoAuthenticationProvider": null, "cognitoAuthenticationType": null, "cognitoIdentityId": null, "cognitoIdentityPoolId": null, "principalOrgId": null, "sourceIp": "IP", "user": null, "userAgent": "user-agent", "userArn": null, "clientCert": { "clientCertPem": "CERT_CONTENT", "subjectDN": "www.example.com", "issuerDN": "Example issuer", "serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1", "validity": { "notBefore": "May 28 12:30:02 2019 GMT", "notAfter": "Aug 5 09:36:04 2021 GMT" } } }, "path": "/my/path", "protocol": "HTTP/1.1", "requestId": "id=", "requestTime": "04/Mar/2020:19:15:17 +0000", "requestTimeEpoch": 1583349317135, "resourceId": null, "resourcePath": "/my/path", "stage": "$default" }, "pathParameters": null, "stageVariables": null, "body": "Hello from Lambda!", "isBase64Encoded": false }
nota

En la entrada:

  • La clave headers solo puede contener encabezados de un solo valor.

  • La clave multiValueHeaders puede contener encabezados de varios valores y encabezados de un solo valor.

  • Si especifica valores para headers y multiValueHeaders, API Gateway los combina en una sola lista. Si se especifica el mismo par de clave-valor en ambos, solo los valores de multiValueHeaders aparecerán en la lista combinada.

En la entrada a la función de Lambda de backend, el objeto requestContext es un mapa de pares de clave-valor. En cada par, la clave es el nombre de una propiedad de la variable $context y el valor es el valor de esa propiedad. API Gateway puede agregar nuevas claves al mapa.

En función de las características habilitadas, el mapa requestContext puede variar de API en API. Por ejemplo, en el ejemplo anterior, no se especifica ningún tipo de autorización, por lo que no hay propiedades $context.authorizer.* o $context.identity.* presentes. Cuando se especifica un tipo de autorización, esto hace que API Gateway transfiera información del usuario autorizado al punto de enlace de integración en un objeto requestContext.identity tal y como se indica a continuación:

  • Cuando el tipo de autorización es AWS_IAM, la información del usuario autorizado incluye propiedades $context.identity.*.

  • Cuando el tipo de autorización es COGNITO_USER_POOLS (autorizador de Amazon Cognito), la información del usuario autorizado incluye las propiedades $context.identity.cognito* y $context.authorizer.claims.*.

  • Cuando el tipo de autorización es CUSTOM (autorizador de Lambda), la información del usuario autorizado incluye las propiedades $context.authorizer.principalId y otras propiedades $context.authorizer.* aplicables.

Formato de salida de una función de Lambda para la integración de proxy

Con la integración de proxy de Lambda, API Gateway requiere que la función de Lambda del backend devuelva la salida de acuerdo con el siguiente formato JSON:

{ "isBase64Encoded": true|false, "statusCode": httpStatusCode, "headers": { "headerName": "headerValue", ... }, "multiValueHeaders": { "headerName": ["headerValue", "headerValue2", ...], ... }, "body": "..." }

En la salida:

  • Las claves headers y multiValueHeaders pueden no estar especificadas si no se van a devolver más encabezados de respuesta.

  • La clave headers solo puede contener encabezados de un solo valor.

  • La clave multiValueHeaders puede contener encabezados de varios valores y encabezados de un solo valor. Puede utilizar la clave multiValueHeaders para especificar todos los encabezados adicionales, incluidos los que solo contienen un valor.

  • Si especifica valores para headers y multiValueHeaders, API Gateway los combina en una sola lista. Si se especifica el mismo par de clave-valor en ambos, solo los valores de multiValueHeaders aparecerán en la lista combinada.

Para habilitar CORS para la integración de proxy de Lambda, debe agregar Access-Control-Allow-Origin:domain-name a la salida headers. domain-name puede ser * para cualquier nombre de dominio. La salida body se serializa en el frontend como la carga de respuesta del método. Si body es un blob binario, puede codificarlo como una cadena codificada en Base64 estableciendo isBase64Encoded en true y configurando */* como Binary Media Type (Tipo de medio binario). De lo contrario, puede establecerlo en false o dejarlo sin especificar.

nota

Para obtener más información acerca de cómo habilitar la compatibilidad con datos binarios, consulte Habilitar la compatibilidad con datos binarios mediante la consola de API Gateway. Para ver una función de Lambda de ejemplo, consulte Devolver medios binarios desde una integración de proxy de Lambda.

Si la salida de la función tiene un formato diferente, API Gateway devuelve una respuesta de error 502 Bad Gateway.

Para devolver una respuesta en una función de Lambda de Node.js, puede utilizar comandos como los siguientes:

  • Para devolver un resultado correcto, llame a callback(null, {"statusCode": 200, "body": "results"}).

  • Para producir una excepción, llame a callback(new Error('internal server error')).

  • En caso de que se produzca un error del lado del cliente (por ejemplo, si falta un parámetro necesario), puede llamar a callback(null, {"statusCode": 400, "body": "Missing parameters of ..."}) para devolver el error sin iniciar una excepción.

En una función de Lambda de async de Node.js, la sintaxis correspondiente sería:

  • Para devolver un resultado correcto, llame a return {"statusCode": 200, "body": "results"}.

  • Para producir una excepción, llame a throw new Error("internal server error").

  • En caso de que se produzca un error del lado del cliente (por ejemplo, si falta un parámetro necesario), puede llamar a return {"statusCode": 400, "body": "Missing parameters of ..."} para devolver el error sin iniciar una excepción.