Skip to content

How-to: Validierungsfehler behandeln ​

So geben Sie Domain-Validierungsfehler an den Client zurück mit ValidationException und ValidationError aus nene2.validation.


1. ValidationCode als StrEnum definieren ​

Das Framework definiert keine Standard-Fehlercodes. Definieren Sie diese pro Projekt mit einem StrEnum.

python
from enum import StrEnum

class ValidationCode(StrEnum):
    REQUIRED = "required"
    INVALID_FORMAT = "invalid_format"
    TOO_SHORT = "too_short"
    TOO_LONG = "too_long"
    ALREADY_EXISTS = "already_exists"
    OUT_OF_RANGE = "out_of_range"

Ein StrEnum zu verwenden:

  • kann direkt an den ValidationError-Konstruktor übergeben werden (typsicher)
  • serialisiert zu seinem String-Wert in JSON
  • ermöglicht IDE-Vervollständigung und statische Analyse

2. Mehrere Felder validieren ​

Fehler in einer Liste akkumulieren und eine einzelne ValidationException auslösen.

python
from nene2.validation import ValidationError, ValidationException

def validate_registration(username: str, email: str, age: int) -> None:
    errors: list[ValidationError] = []

    if len(username) < 3:
        errors.append(ValidationError("username", "At least 3 characters required", ValidationCode.TOO_SHORT))
    if "@" not in email:
        errors.append(ValidationError("email", "Enter a valid email address", ValidationCode.INVALID_FORMAT))
    if age < 0 or age > 150:
        errors.append(ValidationError("age", "Age must be between 0 and 150", ValidationCode.OUT_OF_RANGE))

    if errors:
        raise ValidationException(errors)

Beispielantwort (422):

json
{
  "type": "https://example.com/problems/validation-failed",
  "title": "Validation Failed",
  "status": 422,
  "errors": [
    {"field": "username", "message": "At least 3 characters required", "code": "too_short"},
    {"field": "email",    "message": "Enter a valid email address", "code": "invalid_format"}
  ]
}

3. Ein einzelnes Feld validieren ​

ValidationException.single() löst in einer Zeile aus.

python
from nene2.validation import ValidationException

raise ValidationException.single("email", "Email is required", ValidationCode.REQUIRED)

4. Pfade für verschachtelte Felder ​

Übergeben Sie den Pfad eines verschachtelten Felds als durch Punkte getrennten String (es gibt keinen Normalisierungshelfer).

python
ValidationError("address.city", "Required", ValidationCode.REQUIRED)
ValidationError("items.0.quantity", "Enter 1 or more", ValidationCode.OUT_OF_RANGE)

5. Ein @model_validator-Fehler hat das Feld "request" ​

Wenn Sie raise ValueError(...) in einem Pydantic-@model_validator(mode="after") auslösen, ist loc ein leeres Tupel. nene2s ValidationException-Konvertierung ordnet dies field: "request" zu.

python
class RegisterBody(BaseModel):
    password: str
    password_confirm: str

    @model_validator(mode="after")
    def passwords_match(self) -> Self:
        if self.password != self.password_confirm:
            raise ValueError("Passwords do not match")
        return self

Beispielantwort (422):

json
{
  "errors": [
    {
      "field": "request",
      "message": "Value error, Passwords do not match",
      "code": "value_error"
    }
  ]
}

Im Frontend behandeln Sie field: "request" als Fehler für das gesamte Formular und fügen den relevanten Feldnamen in den Fehlermeldungstext ein.


6. Integration mit ErrorHandlerMiddleware ​

Das Hinzufügen von nene2.middleware.ErrorHandlerMiddleware zur App wandelt eine ValidationException automatisch in eine 422 Problem Details-Antwort um.

python
from nene2.middleware import ErrorHandlerMiddleware

app = FastAPI()
app.add_middleware(ErrorHandlerMiddleware)

7. 400 bei Fehlern beim Dekodieren externer Eingaben zurückgeben ​

Mit Pydantics Query() / Body() wird ein Typkonvertierungsfehler zu 422. Aber für Dinge, die Sie "als String empfangen und selbst dekodieren" — Cursor (Base64), Tokens, usw. — gibt ErrorHandlerMiddleware sie als 500 zurück, wenn Sie die Dekodierungsausnahme nicht abfangen.

python
from fastapi.responses import JSONResponse

@app.get("/posts")
def list_posts(after: str | None = Query(None)) -> JSONResponse:
    if after is not None:
        try:
            cursor_id = _decode_cursor(after)  # z. B. base64.urlsafe_b64decode
        except Exception:
            return JSONResponse({"detail": "Invalid cursor"}, status_code=400)
        # mit cursor_id fortfahren

Fangen Sie Dekodierungsfehler wie binascii.Error zusammen über ValueError / Exception ab und geben Sie 400 Bad Request zurück — dies ist ein Benutzereingabefehler, kein Serverfehler.

Veröffentlicht unter der MIT-Lizenz.