Una dirección web que ejecuta una función
Una tienda quiere una dirección web, /pedidos/lucia, que responda con los pedidos de Lucía. Nadie quiere mantener un servidor solo para eso.
Un API Gateway puede recibir la petición web y entregarla a una función Lambda. La función debe responder con la forma exacta que espera la puerta, también cuando la petición es incorrecta.
Al final de esta lección
Nota
Puedo escribir la respuesta que una puerta de API espera de una función.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Petición | lo que envía quien llama a una dirección web | request |
| Respuesta | lo que la función devuelve | response |
| Código de estado | un número que dice cómo fue la petición | 404 |
| Cabecera | un dato con nombre que viaja junto al cuerpo | content-type |
| Cuerpo | el texto principal de una petición o respuesta | body |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Entra una petición, sale una respuesta
La puerta convierte la petición web en un evento. El método, como GET, está en requestContext.http.method. La ruta está en rawPath.
La función devuelve un diccionario con tres partes: statusCode, headers y body. El cuerpo debe ser un texto, así que usa json.dumps. Son formas documentadas, no la salida de una pila tuya.
Run: la puerta más pequeña
import json
# La forma documentada de una petición de HTTP API, recortada.
event = {
"requestContext": {"http": {"method": "GET"}},
"rawPath": "/pedidos/lucia",
"headers": {"accept": "application/json"},
}
def handler(event, context):
method = event["requestContext"]["http"]["method"]
path = event["rawPath"]
data = {"you_asked": method + " " + path}
return {"statusCode": 200,
"headers": {"content-type": "application/json"},
"body": json.dumps(data)}
answer = handler(event, None)
print(answer["statusCode"])
print(answer["body"])
print(type(answer["body"]).__name__)
# Cambia "GET" por "POST" en el evento y vuelve a ejecutarlo.
Un código de estado dice quién se equivocó
Un 2 al principio, como 200 o 201, significa éxito. Un 4, como 400 o 404, significa que quien llama pidió algo incorrecto. Un 5 significa que falló la propia función.
Una petición incorrecta no es un fallo del programa. Devuelve un 4 con un error corto en el cuerpo. Una función que lanza un error da a quien llama un 5 vago y no le dice nada que pueda arreglar.
Ejemplo resuelto: los códigos de esta lección
| Código | Significado | Úsalo cuando |
|---|---|---|
| 200 | OK | Encontraste y devuelves los datos |
| 201 | Creado | Se guardó un pedido nuevo |
| 400 | Petición incorrecta | Falta un campo, está mal o el cuerpo no es JSON válido |
| 404 | No encontrado | No existe la ruta o el pedido |
| 405 | Método no permitido | La ruta existe, el método no |
| 415 | Tipo de contenido no admitido | El content-type no es application/json |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un error frecuente
Advertencia
Mucha gente deja que la función se rompa con una entrada mala. Quien llama recibe entonces un error de servidor vago. Comprueba antes la entrada. Cuando la culpa es de quien llama, devuelve un 4 y di qué debe arreglar. Reserva el 5 para los fallos tuyos.
Run: un error como respuesta
import json
def reply(status, data):
return {"statusCode": status,
"headers": {"content-type": "application/json"},
"body": json.dumps(data)}
def handler(event, context):
# Una clave que falta es culpa de quien llama: responde 400, no te rompas.
if "queryStringParameters" not in event:
return reply(400, {"error": "añade ?customer=nombre"})
name = event["queryStringParameters"]["customer"]
return reply(200, {"hello": name})
print(handler({"queryStringParameters": {"customer": "lucia"}}, None)["statusCode"])
print(handler({}, None)["statusCode"])
print(handler({}, None)["body"])
# Añade una tercera línea que llame a handler con {"queryStringParameters": {}}. ¿Qué pasa?
Lee la petición con cuidado
No siempre están todas las claves. Sin cadena de consulta, queryStringParameters no aparece en el evento. Comprueba con in antes de leerla.
El cuerpo llega como texto. Si isBase64Encoded es verdadero, el texto está codificado y primero hay que descodificarlo. Los nombres de cabecera llegan en minúsculas, así que busca content-type.
Run: lee un cuerpo en base64
import json
import base64
event = {
"body": base64.b64encode(b'{"item": "te"}').decode("utf-8"),
"isBase64Encoded": True,
}
text = event["body"]
print(text)
if event["isBase64Encoded"] is True:
text = base64.b64decode(text).decode("utf-8")
print(text)
print(json.loads(text)["item"])
# Pon isBase64Encoded en False y vuelve a ejecutarlo. ¿Qué sale mal?
Lo que este módulo no enseñó
Nota
Todo aquí se ejecutó sin conexión, en Python, sobre respuestas con la forma de las reales. No desplegaste nada, no entraste en una cuenta real ni viste una factura. No conociste lo que cuesta mantener un servicio en marcha. Eso es un paso aparte, y se aprende mejor con alguien que pueda revisar los ajustes de tu cuenta.