CSRF y API
Parte de la sección API JSON del Journey de Django de Coddy. Lección 6 de 19.
Un programa real que envía POST /api/tasks/ obtiene 403 Forbidden. La protección CSRF de Django exige que cada POST lleve un token secreto de una de tus propias páginas, para que otro sitio web no pueda hacer que el navegador de un visitante envíe tus formularios. Un programa no tiene esa página ni ese token.
@csrf_exempt, de django.views.decorators.csrf, desactiva la comprobación para una vista:
@csrf_exempt
def webhook(request):
...Eso implica renunciar a algo: ahora cualquier sitio web puede hacer que el navegador de un visitante envíe este POST. Está bien para una vista que no depende de quién haya iniciado sesión, como la lista de tareas. Una API que actúa como el usuario que ha iniciado sesión mantiene CSRF activado, y el script de su página envía el token en un encabezado X-CSRFToken.
Desafío
IntermedioPermite que los programas creen tareas sin un token CSRF. En tasks/views.py, exime únicamente a task_list de la comprobación, para que POST /api/tasks/ funcione sin token.
Ejecuta la línea de abajo en la pestaña Shell, donde enforce_csrf_checks=True hace que el cliente de prueba compruebe los tokens: imprime 201 en lugar de 403.
from django.test import Client; print(Client(enforce_csrf_checks=True).post("/api/tasks/", {"title": "Pay rent"}, content_type="application/json").status_code)Pruébalo tú mismo
import json
from django.http import JsonResponse
from django.shortcuts import render
from .models import Task
def task_to_dict(task):
return {
"id": task.id,
"title": task.title,
"done": task.done,
"priority": task.priority,
}
def home(request):
return render(request, "tasks/home.html", {"tasks": Task.objects.all()})
def stats(request):
data = {
"total": Task.objects.count(),
"done": Task.objects.filter(done=True).count(),
}
return JsonResponse(data)
def task_list(request):
if request.method == "POST":
data = json.loads(request.body)
task = Task.objects.create(
title=data["title"],
priority=data.get("priority", "normal"),
)
return JsonResponse(task_to_dict(task), status=201)
tasks = Task.objects.all()
done = request.GET.get("done")
if done == "true":
tasks = tasks.filter(done=True)
elif done == "false":
tasks = tasks.filter(done=False)
return JsonResponse([task_to_dict(task) for task in tasks], safe=False)
def task_detail(request, pk):
task = Task.objects.filter(pk=pk).first()
if task is None:
return JsonResponse({"error": "Task not found"}, status=404)
return JsonResponse(task_to_dict(task))
Todas las lecciones de API JSON
2Lectura de solicitudes
Filtrar con una consultaCrear con POST5Proyecto: Organizar el trabajo
Proyecto: PrioridadesProyecto: Borrar las tareas completadasPractica por tu cuenta: Playground de Django