Przejdź do treści stopki
Iron Academy Logo
Naucz się C#
Naucz się C#

Inne Kategorie

Dodanie punktu końcowego DELETE w .NET Aspire na Linuxie

[[academy-video-youtube({"vid": "x10CYBXrLxg", "start_time": "0", "title": "Adding a DELETE Endpoint in .NET Aspire on Linux", "creator": "Tim Corey", "length": "8m 40s"})]]

Każda CRUD API ostatecznie potrzebuje sposobu usuwania rekordów, a czasownik DELETE zamyka cztery podstawowe operacje HTTP obok GET, POST i PUT. W porównaniu do innych czasowników, DELETE jest strukturalnie najprostszy: nie wymaga ciała żądania, brak potoku walidacji, brak złożonego typu zwrotu. Czego to wprowadza, to pytanie projektowe, na które nie ma uniwersalnie poprawnej odpowiedzi, mianowicie co zwrócić, gdy wywołujący prosi o usunięcie rekordu, który nie istnieje.

W swoim wideo "Dodanie punktu końcowego DELETE w .NET Aspire na Linux", Tim Corey kończy projekt Tiny Ticket API, dodając ostatni punkt końcowy, naprawia niespójność wielkości liter parametru, która cicho siedziała w procedurze przechowywanej GET-by-ID, oraz omawia, kiedy zwrócić 404 w porównaniu do 204 dla brakującego rekordu. Odcinek również zapowiada przejście na frontend, który stanie się głównym tematem kolejnej fazy serii C# na Linux. Jeśli śledzisz serię lub po raz pierwszy tworzysz DELETE w minimalnym API, ten artykuł omawia pełny punkt końcowy i małą refaktoryzację, która zapewniła spójność wiązania parametrów w całym projekcie.

Mapowanie punktu końcowego DELETE

[1:02 - 2:14] Rejestracja punktu końcowego ma taką samą strukturę jak inne trasy, z dwoma zmianami. Trasa obejmuje segment {id:int}, dzięki czemu ID jest przekazywane w URL zamiast ciała, a sygnatura obsługi używa MapDelete zamiast MapPost lub MapPut. Nie ma rekordu wejściowego, ponieważ nic poza identyfikatorem nie jest potrzebne.

app.MapDelete("/api/tickets/{id:int}",
    async Task<Results<NoContent, ValidationProblem>>
    (ISqlDataAccess sql, int id) =>
{
    await sql.SaveDataAsync("dbo.spTickets_Delete",
        new { Id = id }, "TicketDB");
    return TypedResults.NoContent();
});
app.MapDelete("/api/tickets/{id:int}",
    async Task<Results<NoContent, ValidationProblem>>
    (ISqlDataAccess sql, int id) =>
{
    await sql.SaveDataAsync("dbo.spTickets_Delete",
        new { Id = id }, "TicketDB");
    return TypedResults.NoContent();
});

Obsługiwacz wywołuje procedurę składowaną spTickets_Delete poprzez wrapper Dapper, przekazując anonimowy obiekt z ID. Zwracanie TypedResults.NoContent() daje status 204, sygnalizując, że operacja się powiodła i nie ma ciała odpowiedzi do zwrócenia. Deklaracja typu zwrotu odpowiada punktowi końcowemu PUT z poprzedniego odcinka, ponieważ obie operacje mają ten sam zestaw możliwych wyników z perspektywy frameworka.

Naprawianie niedopasowania wielkości parametrów

[2:14 - 4:32] Podczas podłączania wywołania DELETE, Tim zauważa niespójność, którą wprowadził wcześniej w serii. Procedura składowana spTickets_Delete używa wielkich liter w parametrze Id, co oznacza, że anonimowy obiekt wymaga jawnego przypisania Id = id. Procedura spTickets_Update również używa wielkich liter w Id. Ale spTickets_Get, procedura stojąca za punktem GET-by-ID, używa małych liter w id. Ta mała wariant pozwoliła oryginalnemu obsługiwaczowi przekazać new { id } bez jawnego przypisania, co było wygodne na tamtej chwilę, ale pozostawiło bazę kodu niespójną.

Zamiast przenosić tę asymetrię dalej, otwiera SQL Server Management Studio i zmienia procedurę GET, aby używała wielkich liter w Id:

ALTER PROCEDURE spTickets_Get
    @Id int
AS
BEGIN
    SELECT Id, Title, Description, DateCompleted, Priority, CreatedDate
    FROM dbo.Tickets
    WHERE Id = @Id;
END

Po zaktualizowaniu procedury obsługiwacz GET w Program.cs teraz wymaga tego samego jawnego mapowania, jakiego używają obsługiwacze DELETE i PUT, zmieniając z new { id } na new { Id = id }. Zmiana jest mechaniczna, ale powód jest ważny: spójna wielkość parametrów w procedurach składowanych oznacza, że każdy punkt końcowy wiąże parametry w ten sam sposób, co usuwa małe, ale realne źródło zamieszania przy czytaniu warstwy dostępu do danych później. Konwencja, która jest stosowana tylko w jednym z czterech miejsc, nie jest konwencją.

Kiedy zwrócić 204 vs. 404 dla brakującego rekordu

[4:46 - 5:46] Po skompilowaniu punktu końcowego, Tim zatrzymuje się na kwestii projektowej, która pojawia się przy każdej implementacji DELETE. Jeśli osoba wywołująca przesyła ID, który nie istnieje, co powinno zwrócić API? Istnieją dwie rozsądne odpowiedzi.

Zwrócenie 204 NoContent niezależnie od tego, czy wiersz został usunięty, traktuje żądanie jako idempotentne. Z perspektywy osoby wywołującej zasób zniknął, co było celem. To jest to, co robi obecny handler, i to właśnie zostanie wdrożone w projekcie Tiny Ticket. Zwracanie 404 NotFound dla brakującego rekordu daje osobie wywołującej więcej informacji, ale wymaga od procedury składowanej raportowania, czy wiersz rzeczywiście został usunięty, zazwyczaj poprzez zwrócenie liczby wierszy, które handler może sprawdzić przed podjęciem decyzji, którą odpowiedź wysłać.

Dla wewnętrznego CRUD API, gdzie front end już wie, które identyfikatory istnieją (ponieważ właśnie załadował listę), 204 jest w porządku. Dla publicznego API, gdzie osoby wywołujące mogą zgadywać identyfikatory, 404 zapobiega cichym złudzeniom, że dane zostały usunięte, gdy nigdy nie istniały. Tim zauważa, że zwrócenie 404 może ujawnić informacje o tym, które identyfikatory istnieją w bazie danych, choć dla operacji usunięcia praktyczne ryzyko jest niskie, ponieważ korzystanie z punktu końcowego już sugeruje dostęp do zapisu.

Testowanie przez Swagger

[5:46 - 7:08] Wraz z działającą bazą danych, Tim uruchamia API i otwiera Swagger. Zaczyna od GET all, aby zorientować się w obecnych danych: rekordy 1, 2, 3 z oryginalnego seeda, oraz 107, 109 i 110 pozostawione po wcześniejszych testach wstawiania.

Wykonuje DELETE dla 107 i dostaje zwrot 204. To samo dla 110. Aby zweryfikować zachowanie przy brakującym rekordzie, wykonuje DELETE dla 1011, identyfikatora, który nigdy nie był w bazie danych. Odpowiedź to nadal 204, bez wskazania, że nic nie zostało usunięte. To jest kompromis omówiony w poprzedniej sekcji, teraz widoczny w rzeczywistej odpowiedzi API.

Ścieżka powtórnego GET all potwierdza końcowy stan: pozostają rekordy 1, 2, 3 i 109. Punkt końcowy DELETE działa dla prawidłowych ID i milczy się dla niewłaściwych, dokładnie tak, jak określono w implementacji.

Podsumowanie: CRUD Zakończone

[7:08 - 8:38] Dodanie DELETE kończy cztery czasowniki CRUD dla Tiny Ticket API. Ten sam schemat strukturalny jest stosowany we wszystkich punktach końcowych: definicja trasy, nazwa procedury składowanej, wywołanie dostępu do danych Dapper, wynik typowany. Tim jest szczery, że produkcyjne API prawdopodobnie doda więcej punktów końcowych, takich jak PATCH do oznaczenia biletu jako zakończonego bez przesyłania całego obiektu przez sieć, lub dedykowany punkt końcowy wyszukiwania. Celem serii jest jednak utrzymanie każdej warstwy skoncentrowanej, aby następna warstwa (interfejs front end) miała czystą powierzchnię do wywołania.

Spójność to, co sprawia, że projekt jest łatwy do odczytania. Opakowanie Dapper, tworzenie wzorca POST, pipeline walidacji i wyniki typowane wszystko razem sprawiają, że każdy nowy punkt końcowy zajmuje mniej więcej tyle samo kodu, niezależnie od tego, który czasownik implementuje. Ta przewidywalność to, co czyni API przyjemnym celem dla pracy front endowej, która następuje.

Wnioski

[8:38 - 8:40] Dodanie punktu końcowego DELETE do minimalnego API wymaga rejestracji MapDelete z segmentem ID w trasie, wywołania procedury składowanej przez wrapper dostępu do danych oraz zwrócenia TypedResults.NoContent(). Punkt końcowy kończy powierzchnię CRUD dla projektu Tiny Ticket i przygotowuje przejęcie na front end w następnej fazie serii.

Nawigacja po serii: Ten artykuł jest częścią serii C# na Linux budującej aplikację Tiny Ticket. Poprzednio: Dodawanie punktu końcowego PUT Update. Następna faza: strony front end korzystające z API.

Przykład wskazówki: Jeśli chcesz bardziej informatywnej odpowiedzi DELETE bez zmieniania procedury składowanej, złap licznik zaktualizowanych wierszy z SaveDataAsync i zwróć TypedResults.NotFound(), gdy licznik wynosi zero. To dodaje ścieżkę 404 bez restrukturyzacji warstwy dostępu do danych.

Obejrzyj pełne wideo na jego kanale YouTube i zdobywaj więcej informacji na temat budowania punktów końcowych CRUD w serii C# on Linux.

Hero Worlddot related to Dodanie punktu końcowego DELETE w .NET Aspire na Linuxie
Hero Affiliate related to Dodanie punktu końcowego DELETE w .NET Aspire na Linuxie

Zarabiaj więcej, dzieląc się tym, co kochasz

Tworzysz treści dla deweloperów pracujących z .NET, C#, Java, Python, czy Node.js? Zamień swoją wiedzę specjalistyczną na dodatkowy dochód!

Zespół wsparcia Iron

Jesteśmy online 24 godziny, 5 dni w tygodniu.
Czat
E-mail
Zadzwoń do mnie