.NET Aspire で Linux 上に DELETE エンドポイントを追加する
[[academy-video-youtube({"vid": "x10CYBXrLxg", "start_time": "0", "title": "Adding a DELETE Endpoint in .NET Aspire on Linux", "creator": "Tim Corey", "length": "8m 40s"})]]
すべての CRUD API は最終的にレコードを削除する方法を必要とし、DELETE 動詞は GET、POST、PUT と並ぶ 4 つの基本的な HTTP 動作を完了します。 他の動詞と比較して、DELETE は構造的に最も単純です: リクエストボディなし、検証パイプラインなし、複雑な戻り値タイプなし。 それが導入するのは、削除を要求したレコードが存在しない場合に何を返すべきかという、普遍的な正解のない設計上の問題です。
ビデオ"Adding a DELETE Endpoint in .NET Aspire on Linux"では、Tim Corey が Tiny Ticket API の最終エンドポイントを追加し、GET-by-ID ストアドプロシージャで静かに存在していたパラメータの大文字・小文字の不一致を修正し、存在しないレコードに対して 404 か 204 を返すべきかを議論します。 エピソードはまた、C# on Linux シリーズの次の段階の焦点となるフロントエンドへの移行をプレビューします。 シリーズに従ってきた人や、最小限の API で DELETE をワイヤーアップするのが初めての人にとって、このアーティクルは一貫したパラメータバインディングをプロジェクト全体で実現した小さなリファクタリングを含むフルエンドポイントの手順を示します。
DELETE エンドポイントのマッピング
[1:02 - 2:14] エンドポイントの登録は他のルートと同じ形を持ち、2 つの調整点があります。 ルートにはMapPutは使用されません。 入力レコードは存在しません。ID 以外は必要ないためです。
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();
});ハンドラーはDapperラッパーを通じてspTickets_Deleteストアドプロシージャを呼び出し、IDを持つ匿名オブジェクトを渡します。 TypedResults.NoContent()を返すと204ステータスが生成され、操作が成功したことを示し、返すレスポンスボディがないことを意味します。 前のエピソードの PUT エンドポイント を反映する戻り値型の宣言です。両方の操作はフレームワークの視点から同じ結果セットを持つためです。
パラメータの大文字・小文字の不一致を修正する
[2:14 - 4:32] DELETE 呼び出しをワイヤアップ中に、Tim はシリーズの早い段階で導入した一貫性の欠如に気付きます。 Id = idの割り当てが必要です。 Idを使用します。 しかし、GET-by-IDエンドポイントの背後のプロシージャであるidを使用します。 その小文字のバリアントにより、元のハンドラーは明示的な代入なしにnew { id }を渡すことができ、その時は便利に感じましたが、コードベースは一貫性を欠いていました。
非対称性を引きずるのではなく、SQL Server Management Studioを開いて、GETプロシージャを大文字のIdを使用するように変更します。
ALTER PROCEDURE spTickets_Get
@Id int
AS
BEGIN
SELECT Id, Title, Description, DateCompleted, Priority, CreatedDate
FROM dbo.Tickets
WHERE Id = @Id;
ENDプロシージャが更新されたので、Program.csのGETハンドラーはDELETEおよびPUTハンドラーが使用するのと同様の明示的なマッピングが必要になり、new { Id = id }に変更します。 変更は機械的ですが、理由は重要です: ストアドプロシージャ間での一貫したパラメータの大文字小文字は、すべてのエンドポイントがパラメータを同じ方法でバインドすることを意味し、後でデータアクセスレイヤーを読む際に小さくとも実質的な混乱の元を取り除きます。 四つの場所のうち一つにしか存在しない慣習は慣習ではありません。
存在しないレコードに対して 204 と 404 のどちらを返すか
[4:46 - 5:46] エンドポイントがコンパイルされると、Tim は DELETE 実装の際に毎回出てくる設計上の問題に立ち止まります。 呼び出し元が存在しない ID を渡した場合、API は何を返すべきでしょうか? 合理的な回答は二つあります。
行が削除されているかどうかに関わらず 204 NoContent を返すことは、リクエストを冪等とみなします。 呼び出し元の視点からは、リソースは無くなっており、それが目標でした。 これが現在のハンドラーが行うことであり、それが Tiny Ticket プロジェクトに含まれる予定です。 存在しないレコードに対して 404 NotFound を返すと、呼び出し元に詳細情報を提供しますが、ストアドプロシージャが行が実際に削除されたかどうかを報告する必要があり、通常、ハンドラーが検査して返すべきレスポンスを決定する前に、行数カウントを返すことで達成されます。
フロントエンドがすでにどの ID が存在するかを知っている内部 CRUD API では(リストを読み込んだばかりだから)、204 が適しています。ID を推測する可能性がある呼び出し元がいるパブリック API では、データが存在しないときに削除されたような誤解を防ぐために 404 が適しています。 Tim は、404 を返すことが、データベースに存在する ID に関する情報を漏らす可能性があると指摘していますが、削除操作では、すでにエンドポイントを利用することが書き込みアクセスを暗黙的に示唆しているため、実際的なリスクは低いです。
Swagger を通してのテスト
[5:46 - 7:08] データベースが起動している状態で、Tim は API を立ち上げて Swagger を開きます。 彼は最初に GET all を実行し、現在のデータ: 元のシードからの記録 1, 2, 3、および以前の挿入テストから残っている 107, 109, 110 を確認します。
彼は 107 に対し DELETE を実行し、204 を受け取ります。110 についても同様です。存在しないレコードの動作を確認するため、1011 (データベースに存在しなかった ID) に対して DELETE を実行します。 レスポンスは依然として 204 であり、何も削除されなかったことを示す兆候はありません。 これは、前のセクションで議論されたトレードオフであり、実際の API レスポンスで確認できます。
2 度目の GET all で最終状態を確認します: 記録 1, 2, 3, および 109 は残っています。 DELETE エンドポイントは有効な ID に対しては動作し、無効な ID に対しては静かに失敗します。これは実装に正確に指定されています。
まとめ: CRUD 完成
[7:08 - 8:38] DELETE を追加することで、Tiny Ticket API の 4 つの CRUD 動詞が完成します。 同じ構造的パターンがすべてのエンドポイントにわたって続きます: ルート定義、ストアドプロシージャ名、Dapper データアクセスコール、型付き結果。 Tim は、生産用 API では、オブジェクト全体をワイヤー経由で送信せずにチケットを完了済みにマークするための PATCH や専用の検索エンドポイントを追加する可能性が高いことを率直に述べています。 しかしながら、シリーズの目的は、次の層(フロントエンド)が呼び出し相手としてクリーンな表面を持てるように、各層を集中させたままにすることです。
一貫性がプロジェクトを読みやすくし、心地よくします。 Dapper ラッパー、POST 挿入パターン、検証パイプライン、および型付けされた結果がすべて結びついて、新しいエンドポイントごとに、それがどの動詞を実装していてもほぼ同じ行数のコードが必要になります。 その予測可能性が、後続のフロントエンド作業に対して API を心地よいターゲットにしています。
結論
[8:38 - 8:40] ミニマルAPIにDELETEエンドポイントを追加するには、ルートにIDセグメントを含むTypedResults.NoContent()返却が必要です。 エンドポイントは Tiny Ticket プロジェクトの CRUD サーフェスを完成させ、シリーズの次の段階でフロントエンドへのシフトを設定します。
シリーズナビゲーション: このアーティクルは、Tiny Ticket アプリを構築する C# on Linux シリーズの一部です。前回: PUT 更新エンドポイントを追加する。 次の段階: API を使用するフロントエンドページ。
例のヒント:ストアドプロシージャを変更せずにより情報量の多いDELETEレスポンスを望む場合は、TypedResults.NotFound()を返します。 これにより、データアクセスレイヤを再構築することなく 404 パスが追加されます。
C# on Linux シリーズで CRUD エンドポイントを構築するためのさらなる洞察を得るために、YouTube の彼の チャンネル で完全な ビデオ を視聴してください。

