Datenvalidierung in .NET 10 Minimale APIs
[[academy-video-youtube({"vid": "sW_AcN-nD0Y", "start_time": "0", "title": "Datenvalidierung in .NET 10 Minimal APIs", "creator": "Tim Corey", "length": "~10m"})]]
Minimale APIs waren immer die schlanke Alternative zu Controller-basierten ASP.NET Core, aber lange Zeit hatten sie eine bemerkenswerte Lücke: Keine eingebaute Unterstützung für die Validierung eingehender Daten. Sie mussten entweder manuelle Prüfungen in jedem Handler einbauen oder auf Drittanbieterbibliotheken zurückgreifen. .NET 10 schließt diese Lücke mit First-Class-Datenanmerkungsvalidierung für Abfragezeichenfolgen, Header und Anforderungskörper, alles ohne zusätzliche Pakete.
Dieses vertiefte Eintauchen in .NET 10 Minimal APIs, basierend auf Tim Coreys Rundgang, erklärt, wie man den Validierungsdienst registriert, Modellklassen annotiert und häufige Zugriffsmodifikator-Fallen vermeidet, die Ihr Setup stören können.
Die Einrichtung: Eine einfache minimale API
[0:44 - 1:35] Das Demo beginnt mit einem minimalen ASP.NET Core Minimal-API-Projekt, das auf .NET 10 läuft. Der Umrissbereich ist absichtlich klein: zwei POST-Endpunkte, die jeweils ein anderes Modell akzeptieren.
app.MapPost("/person", (Person person) => Results.Ok(person));
app.MapPost("/login", (LoginModel login) => Results.Ok(login));
app.MapPost("/person", (Person person) => Results.Ok(person));
app.MapPost("/login", (LoginModel login) => Results.Ok(login));
Das Person Modell enthält grundlegende Identitätsfelder. LoginModel verarbeitet Anmeldeinformationen: eine E-Mail-Adresse, ein Passwort und ein Passwort-bestätigen-Feld. Beide werden als JSON-Körper gesendet. Zu diesem Zeitpunkt gibt es noch keine Eingabeüberprüfung; die API akzeptiert alles, was sie erhält, einschließlich leerer Zeichenfolgen und fehlerhaft formatierter E-Mail-Adressen.
Scalar (die moderne OpenAPI-UI, die mit .NET 10 geliefert wird) wird verwendet, um Testanforderungen direkt aus dem Browser zu stellen, sodass Sie leicht sehen können, was die API vor und nach der Vernetzung der Validierung zurückgibt.
Registrierung des Validierungsdienstes
[2:36 - 3:06] Bevor irgendwelche Validierungsattribute wirksam werden, müssen Sie auf Dienstebene zustimmen. Ein einziger Aufruf in Ihrem Dienstregistrierungsblock ermöglicht die Durchsetzung über alle Minimal-API-Endpunkte:
builder.Services.AddValidation();
builder.Services.AddValidation();
Diese eine Zeile ist der gesamte Konfigurationsschritt. Es gibt keine Middleware zum Hinzufügen, keine Pipeline-Stufe zum Einhaken. Das Framework übernimmt automatisch, sobald der Dienst registriert ist. Wenn Sie diesen Aufruf überspringen, sind Ihre Datenanmerkungsattribute zwar im Modell vorhanden, werden aber nie ausgewertet, und Anforderungen weitergeleitet, unabhängig davon, was sie enthalten.
Dies sollte als erster Debugging-Schritt berücksichtigt werden: wenn die Validierung stillschweigend nichts tut, fehlt normalerweise AddValidation().
Hinzufügen der Validierung zu einem Klassenmodell
[3:00 - 3:55] Mit dem registrierten Dienst ist das Hinzufügen von Validierung zu einem klassenbasierten Modell eine Frage des Dekorierens von Eigenschaften mit Datenanmerkungsattributen:
public class Person
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
}
public class Person
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
}
Nach dem Hinzufügen der System.ComponentModel.DataAnnotations using-Direktive am Anfang der Datei, stellt das Markieren von FirstName und LastName als [Required] sicher, dass sie validiert werden. Das Senden einer POST-Anforderung mit leerem Körper über Scalar liefert jetzt ein 400 Bad Request mit einer strukturierten Fehlermeldung zurück:
{
"errors": {
"FirstName": ["The FirstName field is required."],
"LastName": ["The LastName field is required."]
}
}
Keine benutzerdefinierte Fehlerbehandlung, keine Filterattribute. Das Framework erstellt diese Antwort automatisch, und die Prüfung läuft vor der Ausführung Ihres Handlers, sodass Sie nie gegen null innerhalb der Endpunktlogik absichern müssen.
Validierung eines Records
[4:29 - 5:30] Die gleichen Attribute funktionieren auf C#-Records, aber die Syntax unterscheidet sich leicht, weil die Record-Eigenschaften normalerweise im primären Konstruktor und nicht als separate Mitgliedsdeklarationen definiert sind.
public record LoginModel(
[Required] [EmailAddress] string Email,
[Required] string Password,
[Required] string ConfirmPassword
);
public record LoginModel(
[Required] [EmailAddress] string Email,
[Required] string Password,
[Required] string ConfirmPassword
);
Attribute für Konstruktorparameter werden auf die generierten Eigenschaften angewendet, sodass [Required] und [EmailAddress] genau so funktionieren, wie sie es in einer Klasse tun würden. Das Senden einer Anforderung mit einer fehlerhaften E-Mail, z. B. "notanemail", liefert jetzt ein 400 zurück, das das Email-Feld als ungültig identifiziert.
Das [Compare] Attribut kann auch verwendet werden, um durchzusetzen, dass ConfirmPassword mit Password übereinstimmt. Auf einem Record benötigen Attributziele einen ausdrücklichen Stupser, weil der Compiler wissen muss, dass Sie das generierte Mitglied meinen, nicht den Konstruktorparameter selbst:
[property: Compare(nameof(Password))]
string ConfirmPassword
[property: Compare(nameof(Password))]
string ConfirmPassword
Das [property:] Ziel weist den Compiler an, das Attribut an das generierte Mitglied statt an den Parameter anzuhängen. Ohne es wird [Compare] kompiliert, aber nie während der Überprüfung ausgeführt. Dies ist der kniffligste Teil des Arbeitens mit Records vs Klassen in diesem Kontext: Klasse-Eigenschaften akzeptieren Attribute natürlich, während Record-Parameter das explizite Ziel für alles benötigen, das auf Mitgliedsebene arbeitet.
Die Anforderung des öffentlichen Zugriffsschutzes
[7:51 - 9:00] Eine häufige Stolperfalle ist leicht zu übersehen und erzeugt keine Fehlermeldung, wenn Sie darauf stoßen. Das Validierungssystem verwendet Reflection, um Ihre Modelltypen zur Laufzeit zu inspizieren; für Reflection, um die Mitglieder eines Typs zu finden, muss der Typ selbst mit public markiert sein.
// Validation will NOT run; the class is internal by default
class Person { ... }
// Validation runs correctly
public class Person { ... }
// Validation will NOT run; the class is internal by default
class Person { ... }
// Validation runs correctly
public class Person { ... }
Die gleiche Regel gilt für Aufzeichnungen. Wenn Ihr Modell ohne Zugriffsmodifizierer deklariert wird, setzt C# standardmäßig internal ein und der Dienst überspringt es vollständig. Ihr Endpunkt erhält dennoch die Anfrage, der Handler wird ausgeführt und es wird keine Fehlermeldung zurückgegeben, die Prüfungen tun einfach nichts.
Dies ist ein Verhalten von ASP.NET Core, nicht spezifisch für Minimal APIs, aber es tritt hier häufiger auf, weil diese Projekte tendenziell kompakt sind und Entwickler manchmal Modelle inline oder in derselben Datei wie Program.cs definieren, ohne über die Sichtbarkeit nachzudenken.
Ein schneller Weg, um Ihr Projekt zu überprüfen: Jeder in einen Endpunkt-Handler übergebene Modelltyp sollte ausdrücklich mit public markiert werden. Wenn nicht, wird keine Anzahl von Attributen das Framework zwingen, zu reagieren.
Was Sie validieren können
Die eingebauten Datenanmerkungsattribute decken die häufigsten Szenarien ohne zusätzliche Arbeit ab:
[Required]lehnt null oder leere Werte ab[EmailAddress]validiert das Format eines E-Mail-Strings[Compare]prüft, ob zwei Eigenschaften übereinstimmen, was nützlich für die Passwortbestätigung ist[Range]erzwingt numerische oder datumsbezogene Grenzen[StringLength]begrenzt die Zeichenfolgenlänge mit einem optionalen Minimum[RegularExpression]validiert gegen ein benutzerdefiniertes Muster
All diese funktionieren über Abfragezeichenfolgenparameter, Anfrage-Header und JSON-Körper hinweg. Die gleiche Modellklasse oder das gleiche Record kann aus verschiedenen Quellen gebunden werden, ohne dass eines seiner Attribute geändert wird.
Abschluss
[7:46 - bis zum Ende] Mit AddValidation() registriert und Ihre Modelle dekoriert, erzwingen Minimal APIs Eingabebeschränkungen automatisch über Klassen und Datensätze gleichermaßen. Um dies in .NET 10 zum Laufen zu bringen, rufen Sie einfach builder.Services.AddValidation() einmal auf, dekorieren Sie Ihre Modelleigenschaften mit Standarddaten-Anmerkungsattributen und stellen Sie sicher, dass jeder Modelltyp mit public markiert ist.
Das [property:] Ziel auf Aufzeichnungen und die public-Modifiziereranforderung sind die einzigen wirklichen 'Gotchas'. Sie sind leicht zu übersehen und erzeugen stille Ausfälle anstelle kompiliert keine Fehler, daher halten Sie sie auf Ihrer Checkliste, wann immer Eingangsprüfungen scheinbar nichts tun.
Sehen Sie sich das vollständige Video auf Tim Coreys YouTube Kanal an, um die vollständige Quellcode-Durchsicht zu sehen.
