Menu
CoddyTech

Erreurs de validation JSON

Fait partie de la section API JSON du Journey Django de Coddy. Leçon 7 sur 19.

Actuellement, un POST sans titre provoque une erreur KeyError, et Django répond par 500 Server Error, ce qui n'indique pas au programme ce qu'il faut corriger.

Lorsque le problème vient des données, réponds avec 400 Bad Request et indique ce qui ne va pas en JSON, un message par champ, comme un formulaire Django conserve ses erreurs. Une librairie qui vérifie l'année d'un livre :

errors = {}
year = data.get("year")
if not isinstance(year, int) or year < 0:
    errors["year"] = "Enter the year as a whole number."
if errors:
    return JsonResponse({"errors": errors}, status=400)

Il reste deux pièges. Un titre composé uniquement d'espaces n'est pas vide, mais " ".strip() vaut "". Un corps qui n'est pas du JSON fait lever json.loads une exception json.JSONDecodeError. Intercepte-la avec try et except, et réponds avec 400 au lieu de planter.

challenge icon

Défi

Moyen

Fais en sorte que POST /api/tasks/ refuse les données incorrectes avec le statut 400.

  1. Dans task_list, intercepte json.JSONDecodeError autour de json.loads et réponds avec {"error": "The body is not valid JSON."}.
  2. Si le titre est manquant ou vide après suppression des espaces, n’enregistre rien et réponds avec {"errors": {"title": "This field is required."}}.
  3. Sinon, crée la tâche avec le titre débarrassé des espaces.

Dans l’onglet Shell, la ligne ci-dessous affiche This field is required.

from django.test import Client; print(Client().post("/api/tasks/", {"title": "  "}, content_type="application/json").json())

Essayez vous-même

import json

from django.http import JsonResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt

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)


@csrf_exempt
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))

Toutes les leçons de API JSON

Entraînez-vous par vous-même : Playground Django