Módulo 1 · Fundamentos que sostienen todo

Lección 01 — HTTP a fondo

Métodos, códigos de estado con criterio, CORS, cookies seguras, caché y TLS — verificado con curl.

Publicada
En esta lección
  1. Ejercicio 1 — Anatomía con
  2. Ejercicio 2 — Diseña el contrato de un endpoint
  3. Ejercicio 3 — Códigos correctos en DRF
  4. Ejercicio 4 — CORS en la práctica
  5. Ejercicio 5 — Cookies de sesión seguras
  6. Ejercicio 6 — Caché HTTP y 304
  7. Ejercicio 7 — Razona como diseñador de API (discusión)
  8. Ejercicio 8 — HTTPS y TLS (teórico, sin código)
  9. Entrega

Hazlos en orden. Entrega tus respuestas en el chat (pegando comandos y outputs recortados). No mires solutions.md hasta entregar. La corrección es parte del aprendizaje.

Preparación (2 min):

bash
cd ~/dev/ticketflow && source .venv/bin/activate
pip install django-cors-headers && pip freeze > requirements.txt

Añade a config/settings.py:

python
INSTALLED_APPS = [ ..., "corsheaders", ]
MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", ... ]  # lo primero possible
CORS_ALLOWED_ORIGINS = ["http://localhost:5173"]   # supuesta app web del alumno

Ejercicio 1 — Anatomía con curl -v (calentamiento)

Con el servidor corriendo (python manage.py runserver):

  1. Ejecuta curl -v http://127.0.0.1:8000/api/health/ e identifica en la salida: línea de petición, línea de estado, tres cabeceras de respuesta y el cuerpo.
  2. Repite con curl -v -H "Accept: text/html" http://127.0.0.1:8000/api/health/. ¿Qué cambia y por qué?

Entrega: las 4 respuestas en una línea cada una.

Ejercicio 2 — Diseña el contrato de un endpoint

Diseña (solo en papel/chat, sin código) la petición y respuesta completas para reservar asientos del evento 42:

  • Petición: método, ruta, 3 cabeceras que consideres imprescindibles y cuerpo JSON.
  • Respuesta en el caso feliz: código + 2 cabeceras + cuerpo.
  • Respuesta cuando el asiento 1051 ya está ocupado: código + cuerpo.

Pista: hay un código diseñado exactamente para el caso "choque de estado".

Ejercicio 3 — Códigos correctos en DRF

Este código está mal en dos cosas (código de estado y semántica del método). Corrígelo y explica cada fallo:

python
@api_view(["POST"])
def cancel_reservation(request, pk):
    reservation = Reservation.objects.get(pk=pk)
    reservation.status = "cancelled"
    reservation.save()
    return Response({"status": "cancelled"})   # ← sin status= explícito

Pista: ¿qué código espera alguien que acaba de cancelar? ¿Y si la reserva no existe (lo verás al probarlo)?

Ejercicio 4 — CORS en la práctica

Con la configuración de la preparación y el servidor levantado:

bash
curl -i -X OPTIONS http://127.0.0.1:8000/api/health/ \
  -H "Origin: http://localhost:5173" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: authorization,content-type"
  1. ¿Qué códigos y cabeceras Access-Control-* ves en la respuesta?
  2. Repite con Origin: http://evil.example. ¿Qué cambia?
  3. Añade "Idempotency-Key" a CORS_ALLOWED_ORIGINS... perdona, al listado de cabeceras permitidas (CORS_ALLOW_HEADERS), reinicia y repite 1. ¿Qué cambió en las cabeceras de respuesta?

Entrega: salidas recortadas + tu explicación de qué acabas de probar (es exactamente lo que hace un navegador antes de un POST desde otro origen).

Ejercicio 5 — Cookies de sesión seguras

  1. Con django.contrib.sessions activo (viene por defecto), haz login por el admin o crea una sesión manual, y observa la cabecera Set-Cookie del sessionid con curl -i.
  2. En settings.py pon SESSION_COOKIE_SECURE = True y SESSION_COOKIE_SAMESITE = "Strict". Reinicia y repite con curl http://... (ojo: sin https). ¿Llega la cookie? ¿Por qué?
  3. Escribe en una frase qué protectores aporta cada atributo (HttpOnly, Secure, SameSite).

Ejercicio 6 — Caché HTTP y 304

Añade a un endpoint GET (p. ej. health o un listado futuro) soporte de validación con ETag:

python
from django.utils.cache import patch_response_headers

@api_view(["GET"])
def events_list(request):
    data = [{"id": 1, "name": "Concierto"}, {"id": 2, "name": "Obra"}]
    resp = Response(data)
    patch_response_headers(resp, cache_timeout=60)   # Cache-Control, ETag...
    return resp
  1. Pruébalo: curl -i http://127.0.0.1:8000/api/events/ y anota el ETag.
  2. Repite con curl -i -H 'If-None-Match: <tu-etag>' http://127.0.0.1:8000/api/events/. ¿Qué código recibes y qué cuerpo?
  3. ¿Qué se ha ahorrado exactamente el servidor y el cliente respecto a la primera respuesta?

Ejercicio 7 — Razona como diseñador de API (discusión)

Para cada caso elige método + código y justifica en 1-2 frases (esto es lo que se pregunta en entrevistas):

  1. Ampliar el tiempo de expiración de una reserva de 1051 (operación "renovar 10 minutos").
  2. Marcar como "cancelado" un evento completo (reemplazo de su estado).
  3. Consultar disponibilidad del evento 42.
  4. Enviar la confirmación de compra (dispara un proceso de cola, no devuelve aún las entradas).

Ejercicio 8 — HTTPS y TLS (teórico, sin código)

Responde con tus palabras:

  1. ¿Qué tres garantías da TLS y qué atacante real anula cada una?
  2. ¿Qué cambia en el handshake con TLS 1.3 respecto al 1.2 y por qué importa en latencia?
  3. Tu API está detrás de Nginx. ¿Dónde termina el TLS y por qué a esto se le llama "terminación TLS"? ¿Qué implica para el tráfico interno?

Entrega

Pega en el chat tus respuestas (con outputs recortados). Te las corrijo una a una, marcamos los fallos y actualizo el estado del curso. Después tienes autoevaluación final y pasamos a redes.