Menu
CoddyTech

JSON-Validierungsfehler

Teil des Abschnitts JSON-APIs der Django-Journey von Coddy. Lektion 7 von 19.

Derzeit führt ein POST ohne Titel zu einem Absturz mit KeyError, und Django antwortet mit 500 Server Error, was dem Programm nichts darüber sagt, was behoben werden muss.

Wenn die Daten das Problem sind, antworte mit 400 Bad Request und gib in JSON an, was falsch ist – eine Meldung pro Feld, so wie ein Django-Formular seine Fehler speichert. Eine Buchhandlung überprüft das Erscheinungsjahr eines Buches:

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)

Zwei Fallstricke bleiben. Ein Titel, der nur aus Leerzeichen besteht, ist nicht leer, aber " ".strip() ist "". Ein Textkörper, der kein JSON ist, bringt json.loads dazu, json.JSONDecodeError auszulösen. Fange den Fehler mit try und except ab und antworte mit 400, statt abzustürzen.

challenge icon

Aufgabe

Mittel

Lass POST /api/tasks/ fehlerhafte Daten mit dem Status 400 ablehnen.

  1. Fange in task_list rund um json.loads den Fehler json.JSONDecodeError ab und antworte mit {"error": "The body is not valid JSON."}.
  2. Wenn der Titel fehlt oder nach dem Entfernen von Leerzeichen leer ist, speichere nichts und antworte mit {"errors": {"title": "This field is required."}}.
  3. Erstelle ihn andernfalls mit dem Titel ohne umgebende Leerzeichen.

Im Tab „Shell“ gibt die folgende Zeile This field is required. aus.

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

Probier es selbst

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

Alle Lektionen in JSON-APIs

Übe selbstständig: Django-Playground