Menu
CoddyTech

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.

challenge icon

Thử thách

Trung bình

Cho 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