AI 工作實務
ASP.NET Core API 實作:用依賴注入與 8 個 HTTP 檢查建立清楚的回應契約
一個 API 能回傳資料,還不足以讓其他系統放心串接。這份 .NET 10 練習從案件查詢出發,將路由、服務依賴與輸入驗證分開,並用正常、找不到、格式錯誤和邊界輸入檢查實際 HTTP 回應。

一個 API 能回傳資料,還不足以讓其他系統放心串接。這份 .NET 10 練習從案件查詢出發,將路由、服務依賴與輸入驗證分開,並用正常、找不到、格式錯誤和邊界輸入檢查實際 HTTP 回應。
專題編輯:Derek Chou|AI 技術與架構編輯 · 文件核對 2026.11.09

先確定你說的是哪一代 ASP.NET
舊專案文件只寫「ASP.NET」,不足以判斷它的部署方式與相容性。ASP.NET Core 是現代 .NET 的 Web 框架,可以建立 HTTP API;舊有 .NET Framework 應用與它不能只靠改專案名稱就完成移轉。盤點時應查看目標 framework、NuGet 依賴、作業系統相依功能,以及現有資料庫與身分系統。
本文採 net10.0。截至 2026 年 11 月 9 日,Microsoft 的支援頁將 .NET 10 列為 LTS,並要求支援期內持續跟進修補更新;LTS 不是永遠不必升級,也不是每個第三方套件都獲得相同保證。.NET 官方支援政策
這篇解決的是「如何把一個小 API 的回應契約做清楚」。它沒有聲稱 ASP.NET Core 是所有企業的最佳選擇,也不以供應商名氣替代安全驗收。若團隊已有 C# 能力和 .NET 整合需求,可以從此練習觀察分層;若主要資產是 Python,選型仍應比較維護人才、依賴、測試與營運成本。
一次請求經過哪些地方?
最小 API 可以用三層來理解:Host 管理伺服器、設定與服務;路由把 HTTP 路徑對到處理函式;服務處理應用邏輯。本例讓 GET /cases/1 透過 ICaseService 查詢一筆合成案件,找得到回 200,找不到回 404。
依賴注入讓 endpoint 宣告它需要 ICaseService,實際實作由啟動時的註冊提供。這方便替換儲存來源及撰寫測試,但不會自動產生授權規則。本例用沒有可變狀態的 singleton 服務;如果改成具 request scope 的資料庫存取,就必須重新確認生命週期,不能把 scoped 服務直接塞進 singleton。Microsoft DI 文件
Middleware 則在請求管線處理橫切責任,例如例外處理、認證與授權。本文沒有加入登入管線,這個缺項會明確留在驗收界線中。建立一個 /health 路由,不等於其他 endpoint 已受到保護。
步驟一:準備可建置的專案
需要 .NET 10 SDK 和 Python 3,並具有在本機啟動測試程序的權限。Python 只負責送 HTTP 請求及檢查結果,不是 API 的執行環境。先記錄 dotnet --info、dotnet --version 和 python3 --version,避免把 SDK、runtime 與專案 target framework 混為一談。
下載完整練習檔,內含 CaseApi.csproj、Program.cs、verify.py 與操作說明。專案檔為:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
進入練習目錄後執行:
dotnet build -c Release
預期產生 bin/Release/net10.0/CaseApi.dll。若出現 SDK 不支援目標 framework,先核對 SDK,而不是任意改成更舊 framework 掩蓋相容性問題。若有 restore 或來源連線錯誤,保存完整錯誤並確認套件來源;本文沒有要求使用私人套件或真實金鑰。
步驟二:把案件查詢接到服務
Program.cs 的查詢部分如下:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<ICaseService, InMemoryCaseService>();
var app = builder.Build();
app.MapGet("/health", () => Results.Ok(new { status = "ok" }));
app.MapGet("/cases/{id:int}", IResult (int id, ICaseService service) =>
service.Find(id) is { } item
? Results.Ok(item)
: Results.NotFound());
public interface ICaseService { CaseItem? Find(int id); }
public record CaseItem(int Id, string Title);
public class InMemoryCaseService : ICaseService
{
public CaseItem? Find(int id) =>
id == 1 ? new CaseItem(1, "示範案件資料確認") : null;
}
這段是便於閱讀的摘錄;完整檔案還包含下一步的 POST endpoint 和最後的 app.Run(),請以附件整份檔案建置。資料只有固定案件 1,沒有客戶、授信決策或真實金融業務。InMemory 在此甚至不保存新增狀態,因此重啟程序不會有資料持久化問題可供驗收。
Minimal API 和 Controller 都能建立 HTTP API;本文採 Minimal API 是為了讓小範例的路由與回應直接可見。如果團隊已有 Controller 慣例、複雜擴充需求或大型 API 組織方式,不應因示範程式較短就全面改寫。Microsoft API 建構方式說明
步驟三:定義輸入不合格時的回應
附件另有 POST /cases/validate。它只驗證標題,沒有建立案件;這個名稱刻意讓呼叫者看出功能範圍:
app.MapPost("/cases/validate", IResult (CaseInput item) =>
string.IsNullOrWhiteSpace(item.Title) || item.Title.Length > 100
? Results.BadRequest(new { error = "title_required_max_100" })
: Results.Ok(new { accepted = true, title = item.Title.Trim() }));
public record CaseInput(string? Title);
檢查規則是標題不能缺漏、不能全為空白,而且輸入字串的 Length 不超過 100。C# 的 string.Length 計算 UTF-16 code units,不是人眼看到的文字數量;若產品規格要求「100 個顯示字元」,需要另外定義 grapheme cluster 的計算。本例把邊界寫清楚,避免 UI 和 API 對長度有不同解釋。String.Length 官方說明
合法輸入會去除首尾空白再回傳;空字串、缺少 title 或 101 個 ASCII 字元都應回 400。這只是此樣本明確採用的業務規則,並非宣稱 Minimal API 自動替所有 C# record 完成了完整商業驗證。真實系統還要考慮資料範圍、重複提交、使用者可操作的資源,以及資料庫約束。
另有一個容易誤解的差異:/cases/not-an-int 不符合 {id:int} 路由,因此本例預期 404;/cases/validate 成功匹配 POST 路由後,內容不合格才回 400。路由 constraint 用於匹配,不應被描述成通用輸入驗證器。Microsoft routing 文件
步驟四:用真實 HTTP 執行八個檢查
在練習目錄執行:
python3 verify.py
驗證腳本重新 build,選擇一個本機空閒 port,以 127.0.0.1 啟動編譯後的 DLL,再發送請求。它不只呼叫 C# 函式,也實際經過 HTTP listener、路由與 JSON 序列化,最後結束自己啟動的程序。
| 檢查 | 預期結果 |
|---|---|
GET /health | 200,status=ok |
GET /cases/1 | 200,id 與示範標題完全相符 |
GET /cases/999 | 404 |
GET /cases/not-an-int | 404,路由 constraint 未匹配 |
| POST 空 title | 400 |
| POST 101 個 ASCII 字元 | 400 |
| POST 空 JSON 物件 | 400 |
POST " demo " | 200,回傳 "demo" |
成功時輸出 ASP_NET_LAB_PASS 並保存 evidence/result.json;任一 assertion 不符就以非零狀態結束。若程序啟動失敗,先看 evidence/server.log,區分 build 失敗、port 問題與回應契約不符,不要刪掉失敗案例湊出綠燈。
這份證據能支持什麼,還不能支持什麼?
八個檢查支援的結論是:在記錄的 SDK/runtime 與測試環境中,這組小型 API 的指定請求得到預期回應。它沒有測試登入、角色隔離、資料庫、併發壓力、TLS、反向代理或跨主機部署,也沒有證明任何企業正式服務可用。
下一步若接入資料庫,應先把固定的 ICaseService 換成清楚定義的資料存取實作,確認 DI lifetime,再測試不存在資料、資料庫逾時及交易失敗。若加入登入,應新增匿名、一般使用者和無權讀取他人案件等測試,不能只測合法管理員一路成功。
對技術主管來說,可以要求每次 API 變更附上三件事:端點契約的差異、這次新增或改變的失敗案例,以及實際測試版本。這比「已用框架內建功能」更容易確認交付範圍。對工程師來說,則應把外部依賴和部署組態留在可追溯設定中,不把開發機上的隱藏狀態當成團隊基準。
資料來源與素材說明
官方文件核對日:2026 年 11 月 9 日。這是一個原創的合成案件範例;Derek Chou 是技術專題策展角色,不代表具名真人實測或金融專業審核。圖片為架構概念示意,非 Visual Studio 或正式應用截圖。完整測試範圍、命令及版本留在附件紀錄中。
內容提供:TARAISG。本文由 AI 協助整理與查核;專題編輯為天地人 AI 編輯計畫的策展角色,未宣稱經具名人工或專家審核。
本文由 TARAISG(TIANDIREN AI SOLUTION GROUP)使用 AI 模型產生正文、整理來源並設計實務工具。方法依據與限制列於文末;案例為明確標示的教學假設。未有具名紀錄時,不宣稱經人工或專家審核。
查看內容製作與更正說明