Einen PUT-Update-Endpunkt in .NET Aspire auf Linux hinzufügen
[[academy-video-youtube({"vid": "hSRI_JKiH5M", "start_time": "0", "title": "Adding a PUT Update Endpoint in .NET Aspire on Linux", "creator": "Tim Corey", "length": "8m 43s"})]]
Sobald eine API Datensätze lesen und erstellen kann, ist die nächste Operation, vorhandene zu aktualisieren. Ein PUT-Endpunkt ersetzt die gesamte Ressource mit den vom Anrufer bereitgestellten Daten, was bedeutet, dass der Anfragetext jedes Feld enthalten muss, nicht nur die, die sich geändert haben. Dieser Unterschied zwischen PUT (vollständige Ersetzung) und PATCH (teilweise Änderung) ist wichtig dafür, wie Sie den Eingabetyp gestalten und wie Anrufer mit dem Endpunkt interagieren.
In seinem Video "Hinzufügen eines PUT-Update-Endpunkts in .NET Aspire unter Linux" fügt Tim Corey den Update-Endpunkt zur Tiny Ticket API hinzu, erstellt einen dedizierten Update-Datensatztyp, der Felder enthält, die der Insert-Datensatz nicht hatte (wie ID und Abschlussdatum), wendet Validierungsattribute an und testet die Rundreise durch Swagger. Die Episode folgt demselben Muster wie in den vorherigen Teilen, führt jedoch ein nullable DateTime Feld sowie den Unterschied zwischen PUT- und PATCH-Semantik ein. Wenn Sie CRUD-Endpunkte in einer minimalen API aufbauen, deckt dieser Artikel die Update-Seite ab.
Erstellen des Update-Datensatztyps
[1:54 - 4:04] Der Insert-Datensatz aus der vorherigen Episode akzeptierte Titel, Beschreibung und Priorität. Der Update-Datensatz benötigt zwei zusätzliche Felder: die ID des zu ändernden Tickets und den DateCompleted-Zeitstempel. Tim kopiert den Insert-Datensatz und passt ihn an.
public record TicketUpdateRecord(
[Required, Range(1, int.MaxValue)] int Id,
[Required, MinLength(1)] string Title,
[Required] string Description,
DateTime? DateCompleted,
[Range(1, 5)] int Priority
);
public record TicketUpdateRecord(
[Required, Range(1, int.MaxValue)] int Id,
[Required, MinLength(1)] string Title,
[Required] string Description,
DateTime? DateCompleted,
[Range(1, 5)] int Priority
);
Das Markieren von Id als [Required] mit einer [Range(1, int.MaxValue)]-Einschränkung verhindert, dass negative Werte oder Null in die Datenbank gelangen. DateCompleted ist ein nullable DateTime?, da ein Ticket, das noch nicht gelöst wurde, kein Abschlussdatum erfordern sollte. Kein Validierungsattribut ist erforderlich, da null ein gültiger Zustand ist.
Um sicherzustellen, dass die Datensatz-Eigenschaften genau übereinstimmen, zieht Tim die Feldliste aus dem spTickets_Update-gespeicherten Verfahren. Diese Ausrichtung lässt Dapper das Record direkt ohne manuelles Property-to-Parameter-Wiring zuordnen.
Mapping des PUT-Endpunkts
[4:04 - 5:44] Die Endpunktregistrierung folgt dem etablierten Muster. MapPut bindet an die /api/tickets Route, und der Handler ruft das gespeicherte Verfahren mit dem Aktualisierungsdatensatz auf:
app.MapPut("/api/tickets", async Task<Results<NoContent, ValidationProblem>>
(TicketUpdateRecord ticket, ISqlDataAccess sql) =>
{
await sql.SaveDataAsync("dbo.spTickets_Update", ticket, "TicketDB");
return TypedResults.NoContent();
});
app.MapPut("/api/tickets", async Task<Results<NoContent, ValidationProblem>>
(TicketUpdateRecord ticket, ISqlDataAccess sql) =>
{
await sql.SaveDataAsync("dbo.spTickets_Update", ticket, "TicketDB");
return TypedResults.NoContent();
});
Die Deklaration von Results<NoContent, ValidationProblem> als Rückgabetyp sagt dem Framework, dass der Endpunkt entweder einen 204 bei Erfolg oder einen 400 zurückgibt, wenn die Validierung fehlschlägt. Die ValidationProblem-Variante wird automatisch von der Pipeline bearbeitet, die in der vorherigen Episode registriert wurde; der Handler selbst muss nur den Erfolgfall zurückgeben.
Beachtenswert ist, wie der Dapper-Wrapper den Datenzugriff prägnant hält: gespeicherter Verfahrensname, Modell, Verbindungszeichenfolgenname. Drei Parameter decken den gesamten Datenbankaufruf ab. Der Wrapper wurde früher in der Serie geschrieben und zahlt sich weiterhin aus, da jeder neue Endpunkt ihn ohne Modifikationen wiederverwendet.
PUT gegen PATCH: Wann vollständiger Ersatz wichtig ist
[6:06 - 6:46] Bevor Tim Tests durchführt, hält er inne, um den Unterschied zwischen PUT und PATCH klarzumachen. Eine PUT-Anfrage ersetzt die gesamte Ressource: Jedes Feld im Anfragetext überschreibt die entsprechende Datenbanksäule, auch wenn der Anrufer nicht beabsichtigt hatte, es zu ändern. Eine PATCH-Anfrage aktualisiert nur die Felder, die im Körper enthalten sind.
Für das Tiny Ticket-Projekt ist PUT die richtige Wahl, weil das Frontend das vollständige Ticket lädt, dem Benutzer erlaubt, Felder zu bearbeiten und das komplette Objekt zurückzusenden. In einer Produktionsanwendung erwähnt Tim, dass er wahrscheinlich einen PATCH-Endpunkt speziell für häufige Einzel-Feld-Operationen wie das Markieren eines Tickets als abgeschlossen hinzufügen würde, wo das Senden des gesamten Objekts nur um ein Datum umzudrehen, als verschwenderisch empfunden wird.
Testen des Updates über Swagger
[6:46 - 8:26] Tim launches the API and opens Swagger. Bevor er das PUT testet, führt er den GET-all-Endpunkt aus, um den aktuellen Stand der Daten zu überprüfen. Einer der Testdatensätze (ID 109) hat leere Werte für Titel, Beschreibung und Priorität von früheren Tests. Das wird das Ziel für das Update.
Er füllt den PUT-Anfragetrans mit ID 109, einem Titel von "Beispieldatensatz", einer Beschreibung und einer Priorität von 5 aus. Nach der Ausführung kommt die Antwort als 204 zurück. Das erneute Ausführen von GET-all bestätigt, dass der Datensatz jetzt die aktualisierten Werte hat.
Um die Validierung zu prüfen, löscht er das Titelfeld und führt es erneut aus. Die Antwort kommt als 400 mit einer strukturierten Fehlermeldung zurück: "Das Ticket-Titelfeld ist erforderlich." Die gleichen Validierungsattribute vom Insert-Endpunkt werden auf den Update-Datensatz übertragen, da sie das gleiche Annotationsmuster verwenden.
Abschluss: Fortschritt bei CRUD
[8:26 - 8:43] Mit dem vollständigen PUT-Endpunkt deckt die Tiny Ticket-API jetzt drei der vier CRUD-Operationen ab: Lesen (GET all und GET by ID), Erstellen (POST) und Aktualisieren (PUT). Jeder Endpunkt folgt dem gleichen strukturellen Muster, was den Codebestand vorhersehbar macht. Die verbleibende Operation ist DELETE, die Tim als nächste Episode vorschaut.
Abschluss
[8:38 - 8:43] Das Hinzufügen eines PUT-Endpunkts zu einer Minimal-API erfordert einen speziellen Aktualisierungsdatensatz mit Validierungsattributen, eine MapPut-Registrierung mit der Sammlungs-URL und einen Aufruf des gespeicherten Verfahrens über den Datenzugriffs-Wrapper. Der Results<NoContent, ValidationProblem>-Rückgabetyp lässt das Framework sowohl Erfolgs- als auch Validierungsfehlerantworten handhaben. Nullable-Felder wie DateTime? werden durchgelassen, ohne dass ein Validierungsattribut erforderlich ist, da null ein gültiger Wert für unvollständige Daten ist.
Seriennavigation: Dieser Artikel ist Teil der C# auf Linux-Serie, die die Tiny Ticket-App aufbaut. Vorher: Hinzufügen eines POST-Insert-Endpunkts. Nächste: Hinzufügen eines DELETE-Endpunkts.
Beispiel-Tipp: Wenn Ihr Update gespeichertes Verfahren die modifizierte Zeilenanzahl zurückgibt, überprüfen Sie sie, bevor Sie 204 zurückgeben. Eine Anzahl von null bedeutet, dass die ID mit keinem Datensatz übereinstimmte, und Sie sollten ein 404 zurückgeben, anstatt stillschweigend erfolgreich zu sein.
Sehen Sie sich das vollständige Video auf seinem YouTube Kanal an und gewinnen Sie weitere Einblicke in den Aufbau von CRUD-Endpunkten in der C# auf Linux-Serie.
