CSRF và API
Thuộc phần API JSON trong hành trình Django của Coddy. Bài học 6 trên 19.
Một chương trình thật gửi POST /api/tasks/ sẽ nhận lỗi 403 Forbidden. Cơ chế bảo vệ CSRF của Django yêu cầu mỗi yêu cầu POST phải mang theo một token bí mật từ một trong các trang của bạn, để trang web khác không thể khiến trình duyệt của khách truy cập gửi biểu mẫu của bạn. Chương trình không có trang như vậy nên cũng không có token.
@csrf_exempt, thuộc django.views.decorators.csrf, tắt bước kiểm tra này cho một view:
@csrf_exempt
def webhook(request):
...Đổi lại, bất kỳ trang web nào cũng có thể khiến trình duyệt của khách truy cập gửi yêu cầu POST này. Cách này phù hợp với một view không phụ thuộc vào việc người dùng nào đang đăng nhập, chẳng hạn như danh sách tác vụ. API hoạt động với tư cách người dùng đang đăng nhập vẫn bật CSRF, còn tập lệnh trên trang của API gửi token trong tiêu đề X-CSRFToken.
Thử thách
Trung bìnhCho phép các chương trình không có token CSRF tạo tác vụ. Trong tasks/views.py, chỉ miễn kiểm tra cho task_list, để POST /api/tasks/ hoạt động mà không cần token.
Chạy dòng bên dưới trong thẻ Shell. enforce_csrf_checks=True khiến Client kiểm tra token khi chạy test: dòng này in ra 201 thay vì 403.
from django.test import Client; print(Client(enforce_csrf_checks=True).post("/api/tasks/", {"title": "Pay rent"}, content_type="application/json").status_code)Tự làm thử
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))
Tất cả bài học trong API JSON
Tự luyện tập: Playground Django