Dodanie punktu końcowego POST Insert z walidacją w .NET Aspire na Linuxie
[[academy-video-youtube({"vid": "oAMMHR8kKnw", "start_time": "0", "title": "Dodanie punktu końcowego POST z walidacją w .NET Aspire na Linux", "creator": "Tim Corey", "length": "20m 31s"})]]
Odczyt danych z API to tylko połowa historii. W końcu każda aplikacja musi przyjąć nowe rekordy, a to oznacza tworzenie punktu końcowego POST, który odbiera treść żądania, poddaje ją walidacji, zapisuje w bazie danych i zwraca znaczący kod statusu. Pomijanie kroku walidacji jest kuszące podczas prototypowania, ale produkcyjne API, które akceptują niezweryfikowane dane wejściowe, stają się źródłem uszkodzonych danych, które są trudniejsze do oczyszczenia niż do zapobiegania.
W swoim wideo "Dodawanie punktu końcowego POST Insert z walidacją w .NET Aspire na Linux", Tim Corey dodaje punkt wstawiania do Tiny Ticket API, tworzy dedykowany typ rekordu wejściowego, łączy się z wbudowaną w .NET walidacją dla minimalnych API i konfiguruje Swagger, aby automatycznie się uruchomił wraz z API. Odcinek obejmuje cały cykl od procedury składowanej po przetestowany punkt końcowy, w tym format odpowiedzi błędu walidacji, który .NET zwraca z pudełka. Jeśli śledzisz serię C# na Linux lub po raz pierwszy dodajesz operacje zapisu do minimalnego API, ten artykuł przeprowadza przez każdy krok.
Tworzenie typu insert rekordu
[1:46 - 4:43] Przed zbudowaniem punktu końcowego, Tim tworzy obiekt transferu danych reprezentujący kształt zapytania wstawiania. Istniejący TicketModel zawiera pola takie jak Id i CreatedDate, które baza danych generuje automatycznie. Akceptowanie ich w treści POST byłoby albo ignorowane, albo powodowało konflikty, więc oddzielny typ ogranicza wprowadzanie tylko do pól, które osoba wywołująca powinna dostarczyć.
public record TicketInsertRecord(string Title, string Description, int Priority);public record TicketInsertRecord(string Title, string Description, int Priority);Użycie record zamiast class jest świadomym wyborem. Rekordy zapewniają równość opartą na wartościach i niezmienność domyślnie, co pasuje do semantyki ładunku zapytania: dane przychodzą, są weryfikowane, przekazywane do bazy danych i nigdy nie są modyfikowane w międzyczasie. Te trzy właściwości (Title, Description, Priority) bezpośrednio mapują się na parametry procedury składowanej spTickets_Insert.
Mapowanie punktu końcowego POST
[4:43 - 9:51] Z zdefiniowanym typem rekordu, rejestracja punktu końcowego podąża za tym samym wzorcem co trasy GET, ale używa MapPost i wiąże ciało zapytania z rekordem wstawiania:
app.MapPost("/api/tickets", async (TicketInsertRecord ticket, IDbConnection db) =>
{
await db.SaveDataAsync("spTickets_Insert", ticket);
return Results.NoContent();
});app.MapPost("/api/tickets", async (TicketInsertRecord ticket, IDbConnection db) =>
{
await db.SaveDataAsync("spTickets_Insert", ticket);
return Results.NoContent();
});Zwróć uwagę, że trasa to /api/tickets bez segmentu ID, co odpowiada konwencjom REST, gdzie POST do URL kolekcji tworzy nowy zasób. Obsługujący wywołuje procedurę składowaną z całym obiektem ticket jako torbą parametrów. Dapper mapuje właściwości rekordu na parametry SQL według nazw.
Zwrócenie Results.NoContent() wysyła kod statusu 204. Tim wyjaśnia powód: insert zakończył się sukcesem, ale nie ma nic znaczącego do zwrócenia w treści odpowiedzi. Niektóre API zwracają nowo utworzony obiekt z kodem statusu 201 Created i nagłówkiem Location wskazującym nowy zasób, co jest ważną alternatywą. Dla projektu Tiny Ticket, 204 utrzymuje wszystko w lekki sposób.
Testowanie wstawiania przez Swagger
[9:51 - 14:43] Tim launches the project and navigates to Swagger. Punkt końcowy POST pojawia się ze schematem ciała zapytania pasującym do właściwości TicketInsertRecord. Uzupełnia bilet testowy tytułem, opisem i priorytetem, a następnie wykonuje żądanie.
Wraca kod 204, potwierdzając, że insert zakończył się sukcesem. Aby zweryfikować, czy dane rzeczywiście zostały zapisane, przechodzi do punktu końcowego GET all i wykonuje go. Nowy bilet pojawia się na liście obok oryginalnych rekordów testowych.
To, co test również ujawnia, to luka bez walidacji: wysłanie pustego tytułu, brakującego opisu lub priorytetu 99 wszystko kończy się sukcesem z kodem 204. Baza danych akceptuje to, co API wysyła. Ta luka motywuje do następnej sekcji.
Dodawanie wbudowanej walidacji
[14:43 - 18:28] Od wersji .NET 10 minimalne API obsługują wbudowany pipeline walidacji, który odczytuje atrybuty adnotacji danych z typu wejściowego i odrzuca nieprawidłowe żądania przed uruchomieniem handlera. Tim połączył to w dwóch krokach.
Na początku zarejestruj usługi walidacji w Program.cs. Ta jedna linia aktywuje cały pipeline:
builder.Services.AddValidation();builder.Services.AddValidation();Gdy usługa zostanie zarejestrowana, framework sprawdza każde ciało żądania pod kątem atrybutów walidacji przed uruchomieniem handlera. Drugi krok to oznaczenie rekordu insert regułami, którym musi odpowiadać każde pole:
public record TicketInsertRecord(
[Required, MinLength(1)] string Title,
[Required] string Description,
[Range(1, 5)] int Priority
);public record TicketInsertRecord(
[Required, MinLength(1)] string Title,
[Required] string Description,
[Range(1, 5)] int Priority
);[Required] zapewnia, że pole jest obecne i nie jest nullem. [MinLength(1)] zapobiega przechodzeniu pustych ciągów przez kontrolę wymaganą (ponieważ pusty ciąg technicznie nie jest nullem). [Range(1, 5)] ogranicza priorytet do ważnego poziomu. Te atrybuty to te same typy System.ComponentModel.DataAnnotations, które kontrolery ASP.NET MVC używały od lat, ale teraz działają w minimalnych API bez dodatkowego oprogramowania pośredniczącego.
Po zapisaniu i ponownym uruchomieniu, Tim wysyła żądanie z pustym tytułem i priorytetem 10. Odpowiedź wraca jako 400 Bad Request z uporządkowanym ciałem błędu:
{
"errors": {
"Title": ["The Title field is required."],
"Priority": ["The field Priority must be between 1 and 5."]
}
}Pipeline walidacji zatrzymuje żądanie przed uruchomieniem handlera, więc żadne nieprawidłowe dane nie trafiają do bazy danych. Odpowiedź błędu jest zgodna z formatem standardu RFC 7807 Problem Details, który konsumenci API mogą parsować programowo.
Automatyczne uruchamianie Swagger przy starcie
[19:44 - 20:31] Małe ulepszenie jakości życia zamyka odcinek. Za każdym razem, gdy Tim uruchomił API, musiał ręcznie wpisywać /swagger w URL przeglądarki. Aby to zautomatyzować, otwiera Properties/launchSettings.json projektu API i dodaje właściwość launchUrl do profilu HTTPS:
{
"profiles": {
"https": {
"launchUrl": "swagger"
}
}
}Przy następnym uruchomieniu, przeglądarka otwiera bezpośrednio Swagger UI zamiast strony domyślnej. To oszczędza kilka sekund na cykl debugowania, co się kumuluje podczas pełnej sesji rozwoju.
Wnioski
[20:09 - 20:31] Dodanie punktu końcowego POST do minimalnej API wiąże się z utworzeniem dedykowanego rekordu wejściowego, mapowaniem go na MapPost z URL kolekcji i wywołaniem procedury składowanej z rekordem jako obiektem parametrów. Walidacja w .NET 10 wymaga jednej rejestracji usługi i standardowych atrybutów adnotacji danych na właściwościach rekordu. Framework automatycznie obsługuje formatowanie odpowiedzi 400.
Nawigacja po serii: Ten artykuł jest częścią serii C# na Linux budującej aplikację Tiny Ticket. Poprzednio: Dodawanie punktu końcowego Get By ID. Następnie: Dodawanie punktu końcowego PUT Update.
Przykładowa wskazówka: Gdy twoja procedura składowana do wstawiania zwraca ID nowego rekordu, zmień zwrot z Results.NoContent() na Results.Created($"/api/tickets/{newId}", result), aby dać dzwoniącym status 201 z nagłówkiem lokalizacji, który może być śledzony w celu pobrania utworzonego zasobu.
Obejrzyj pełne wideo na jego kanale YouTube i zdobywaj więcej informacji na temat budowania punktów końcowych zapisu w serii C# on Linux.

