Do them in order. Submit your answers in the chat (pasting commands and trimmed output). Don't look at solutions.md until you've submitted. Feedback is part of the learning.
Setup (2 min):
cd ~/dev/ticketflow && source .venv/bin/activate
pip install django-cors-headers && pip freeze > requirements.txtAdd to config/settings.py:
INSTALLED_APPS = [ ..., "corsheaders", ]
MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", ... ] # as early as possible
CORS_ALLOWED_ORIGINS = ["http://localhost:5173"] # the student's hypothetical web appExercise 1 — Anatomy with curl -v (warm-up)
With the server running (python manage.py runserver):
- Run
curl -v http://127.0.0.1:8000/api/health/and identify in the output: request line, status line, three response headers and the body. - Repeat with
curl -v -H "Accept: text/html" http://127.0.0.1:8000/api/health/. What changes and why?
Deliverable: the 4 answers, one line each.
Exercise 2 — Design an endpoint's contract
Design (on paper/chat only, no code) the complete request and response for reserving seats of event 42:
- Request: method, path, the 3 headers you consider essential, and the JSON body.
- Happy-path response: code + 2 headers + body.
- Response when seat 1051 is already taken: code + body.
Hint: there's a status code designed exactly for the "state clash" case.
Exercise 3 — Correct codes in DRF
This code is wrong in two ways (status code and method semantics). Fix it and explain each mistake:
@api_view(["POST"])
def cancel_reservation(request, pk):
reservation = Reservation.objects.get(pk=pk)
reservation.status = "cancelled"
reservation.save()
return Response({"status": "cancelled"}) # ← no explicit status=Hint: what code does someone who just cancelled expect? And what if the reservation doesn't exist (you'll see it when you test it)?
Exercise 4 — CORS in practice
With the setup from above and the server running:
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"- Which status code and
Access-Control-*headers do you see in the response? - Repeat with
Origin: http://evil.example. What changes? - Add
Idempotency-Keyto the allowed headers list (CORS_ALLOW_HEADERS), restart, and repeat 1. What changed in the response headers?
Deliverable: trimmed outputs + your explanation of what you just tested (this is exactly what a browser does before a POST from another origin).
Exercise 5 — Secure session cookies
- With
django.contrib.sessionsactive (it's there by default), log in via the admin or create a session manually, and observe theSet-Cookieheader of thesessionidwithcurl -i. - In
settings.pysetSESSION_COOKIE_SECURE = TrueandSESSION_COOKIE_SAMESITE = "Strict". Restart and repeat withcurl http://...(careful: no https). Does the cookie arrive? Why? - Write one sentence on what protection each attribute adds (
HttpOnly,Secure,SameSite).
Exercise 6 — HTTP caching and 304
Add ETag-based validation support to a GET endpoint (e.g. health or a future listing):
from django.utils.cache import patch_response_headers
@api_view(["GET"])
def events_list(request):
data = [{"id": 1, "name": "Concert"}, {"id": 2, "name": "Play"}]
resp = Response(data)
patch_response_headers(resp, cache_timeout=60) # Cache-Control, ETag...
return resp- Test it:
curl -i http://127.0.0.1:8000/api/events/and note theETag. - Repeat with
curl -i -H 'If-None-Match: <your-etag>' http://127.0.0.1:8000/api/events/. Which code do you get, and what body? - What exactly did the server and the client save compared to the first response?
Exercise 7 — Reason like an API designer (discussion)
For each case pick method + status code and justify in 1-2 sentences (this is what interviews ask):
- Extending the expiry of reservation 1051 (a "renew for 10 minutes" operation).
- Marking a whole event as "cancelled" (replacing its state).
- Checking availability of event 42.
- Submitting the purchase confirmation (it triggers a queue process; it doesn't return the tickets yet).
Exercise 8 — HTTPS and TLS (theory, no code)
Answer in your own words:
- Which three guarantees does TLS give, and which real attacker defeats each one?
- What changes in the handshake with TLS 1.3 vs 1.2, and why does it matter for latency?
- Your API sits behind Nginx. Where does TLS end, and why is this called "TLS termination"? What does it imply for internal traffic?
Submission
Paste your answers in the chat (with trimmed outputs). I'll grade them one by one, we'll mark the mistakes and I'll update the course state. Then you have the final self-assessment and we move on to networking.