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):
cd ~/dev/ticketflow && source .venv/bin/activate
pip install django-cors-headers && pip freeze > requirements.txtAñade a config/settings.py:
INSTALLED_APPS = [ ..., "corsheaders", ]
MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", ... ] # lo primero possible
CORS_ALLOWED_ORIGINS = ["http://localhost:5173"] # supuesta app web del alumnoEjercicio 1 — Anatomía con curl -v (calentamiento)
Con el servidor corriendo (python manage.py runserver):
- 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. - 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:
@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ícitoPista: ¿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:
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"- ¿Qué códigos y cabeceras
Access-Control-*ves en la respuesta? - Repite con
Origin: http://evil.example. ¿Qué cambia? - Añade
"Idempotency-Key"aCORS_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
- Con
django.contrib.sessionsactivo (viene por defecto), haz login por el admin o crea una sesión manual, y observa la cabeceraSet-Cookiedelsessionidconcurl -i. - En
settings.pyponSESSION_COOKIE_SECURE = TrueySESSION_COOKIE_SAMESITE = "Strict". Reinicia y repite concurl http://...(ojo: sin https). ¿Llega la cookie? ¿Por qué? - 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:
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- Pruébalo:
curl -i http://127.0.0.1:8000/api/events/y anota elETag. - 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? - ¿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):
- Ampliar el tiempo de expiración de una reserva de 1051 (operación "renovar 10 minutos").
- Marcar como "cancelado" un evento completo (reemplazo de su estado).
- Consultar disponibilidad del evento 42.
- 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:
- ¿Qué tres garantías da TLS y qué atacante real anula cada una?
- ¿Qué cambia en el handshake con TLS 1.3 respecto al 1.2 y por qué importa en latencia?
- 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.