Tutorial 8~15 MinutenAnfänger

Validierung

Sichere deine API-Endpunkte mit integrierter Datenvalidierung – deklarativ, typsicher und mit automatischen Fehlermeldungen.

Was du lernen wirst

  • Validierungs-Decorators für Struct-Felder
  • String-, Zahlen- und Custom-Validierung
  • Automatische Fehlermeldungen
  • Manuelle Validierung für komplexe Fälle
1

Warum Validierung?

Jede API muss Eingaben validieren, bevor sie verarbeitet werden. Ohne Validierung riskierst du ungültige Daten, Sicherheitslücken und schwer zu debuggende Fehler.

Typsicher

Validierung findet vor deiner Logik statt – garantiert saubere Daten.

Fehlermeldungen

Automatische, klare Fehlermeldungen für den Client.

Security

Schutz vor Injection-Attacken und XSS.

2

Deklarative Validierung

Velisch ermöglicht Validierungsregeln direkt in Struct-Definitionen. Der Compiler generiert daraus hocheffizienten Validierungscode.

Validierungs-Decorators

struct CreateUserRequest {
    @MinLength(3)
    @MaxLength(50)
    name: string,
    
    @Email
    email: string,
    
    @Min(18)
    @Max(120)
    age: number,
    
    @Pattern("^[a-z0-9_]+$")
    username: string,
}

@POST("/api/users")
fn createUser(request: CreateUserRequest): User {
    // Validierung erfolgt AUTOMATISCH vor dem Funktionsaufruf!
    // Wenn Validierung fehlschlägt → 400 Bad Request
    
    return db.save(User {
        id: generateId(),
        name: request.name,
        email: request.email,
        age: request.age,
        username: request.username,
    });
}
3

Verfügbare Validators

Velisch bietet eine umfangreiche Bibliothek an eingebauten Validators:

String-Validierung

struct StringValidation {
    @MinLength(3)           // Mindestens 3 Zeichen
    @MaxLength(100)         // Maximal 100 Zeichen
    @Length(10)             // Exakt 10 Zeichen
    name: string,
    
    @Email                  // Gültiges Email-Format
    email: string,
    
    @Url                    // Gültige URL
    website: string,
    
    @Pattern("^[A-Z]{2}[0-9]{4}$")  // Regex-Pattern
    code: string,
    
    @NotEmpty               // Nicht leer (kein "")
    @Trim                   // Whitespace entfernen
    description: string,
    
    @Lowercase              // Zu Kleinbuchstaben konvertieren
    @Alphanumeric           // Nur a-z, A-Z, 0-9
    username: string,
}

Zahlen-Validierung

struct NumberValidation {
    @Min(0)                 // Mindestens 0
    @Max(100)               // Maximal 100
    percentage: number,
    
    @Positive               // > 0
    price: number,
    
    @Negative               // < 0
    debt: number,
    
    @Range(1, 10)           // Zwischen 1 und 10 (inklusiv)
    rating: number,
    
    @Integer                // Nur ganze Zahlen
    quantity: number,
}

Erweiterte Validators

struct AdvancedValidation {
    @Required               // Pflichtfeld (nicht null/undefined)
    id: string,
    
    @Optional               // Kann fehlen
    nickname: string?,
    
    @ArrayMinSize(1)        // Liste mit mind. 1 Element
    @ArrayMaxSize(10)       // Liste mit max. 10 Elementen
    tags: List<string>,
    
    @Unique                 // Keine Duplikate in der Liste
    categories: List<string>,
    
    @ValidNested            // Verschachtelte Validierung
    address: Address,
    
    @Enum(["draft", "published", "archived"])
    status: string,
}

Datum & Zeit

  • @Date – Gültiges Datum
  • @FutureDate – In der Zukunft
  • @PastDate – In der Vergangenheit

Sicherheit

  • @Sanitize – HTML escapen
  • @StrongPassword – Passwort-Check
  • @NoSql – SQL-Injection-Schutz
4

Custom Validators

Für komplexe Validierungslogik kannst du eigene Validators definieren:

// Custom Validator definieren
@Validator
fn isGermanPhone(value: string): Result<void, string> {
    if (!value.startsWith("+49") && !value.startsWith("0")) {
        return Err("Muss eine deutsche Telefonnummer sein");
    }
    if (value.length() < 10) {
        return Err("Telefonnummer zu kurz");
    }
    return Ok(());
}

// Custom Validator verwenden
struct ContactForm {
    @Custom(isGermanPhone)
    phone: string,
    
    @Email
    email: string,
}

// Validator mit Parameter
@Validator
fn isDivisibleBy(value: number, divisor: number): Result<void, string> {
    if (value % divisor != 0) {
        return Err("Muss durch {divisor} teilbar sein");
    }
    return Ok(());
}

struct Order {
    @Custom(isDivisibleBy, 5)  // Muss durch 5 teilbar sein
    quantity: number,
}
5

Manuelle Validierung

Manchmal brauchst du Validierung, die über einzelne Felder hinausgeht – z.B. Abhängigkeiten zwischen Feldern oder Datenbankabfragen.

// Validierung mit Business-Logik
fn processOrder(order: OrderRequest): Result<Order, Error> {
    // Prüfung: Mindestens ein Artikel
    if (order.items.isEmpty()) {
        return Err(ValidationError("items", "Bestellung darf nicht leer sein"));
    }
    
    // Prüfung: Summe korrekt
    let calculatedTotal = order.items
        .map(item => item.price * item.quantity)
        .reduce((a, b) => a + b, 0.0);
    
    if ((order.total - calculatedTotal).abs() > 0.01) {
        return Err(ValidationError("total", "Summe stimmt nicht"));
    }
    
    // Prüfung: User existiert (DB-Check)
    let user = db.find(User, order.userId);
    if (user == null) {
        return Err(ValidationError("userId", "User nicht gefunden"));
    }
    
    // Alles OK → Order erstellen
    return Ok(createOrder(order));
}

// Kombiniert: Deklarativ + Manuell
@POST("/api/orders")
fn createOrder(request: CreateOrderRequest): Order {
    // 1. Deklarative Validierung (automatisch)
    // 2. Manuelle Validierung
    let result = processOrder(request);
    
    return match (result) {
        Ok(order) => order,
        Err(e) => throw HttpError(400, e.message),
    };
}

Zusammenfassung

Was du gelernt hast:

  • Deklarative Validierung mit Decorators
  • String-, Zahlen- und erweiterte Validators
  • Custom Validators für eigene Logik
  • Manuelle Validierung für komplexe Fälle

Nächste Schritte:

  • Security mit Rollen und Decorators
  • Authentifizierung implementieren