iT邦幫忙

2025 iThome 鐵人賽

DAY 26
0
佛心分享-IT 人自學之術

30 天從 Python 轉職場 C# 新手入門系列 第 27

Day27-Model 與 DTO(ASP.NET Core Web API 教學)

  • 分享至 

  • xImage
  •  

前言

在昨天的文章中,學習到了 Routing 與 Controller 的運作原理。今天,我們要進一步探討 Web API 中非常核心的部分 —— Model 與 DTO(Data Transfer Object),以及 ASP.NET Core 如何透過 Model Binding 自動將用戶輸入的資料轉換成程式可用的物件。


什麼是 Model 與 DTO?

在 ASP.NET Core 的世界中:

  • Model 是與資料結構直接對應的類別,例如資料庫的實體(Entity)。
  • DTO(Data Transfer Object) 則是用於前後端資料傳輸的中介物件,通常只包含要回傳或接收的必要欄位。

而這樣的設計可以避免資料過多暴露(防止 Overposting 攻擊),且可以清楚區分「資料儲存層」與「資料呈現層」,讓 API 的輸入輸出更容易維護與測試。

那Model Binding 是什麼?

Model Binding 是 ASP.NET Core 自動幫我們把 HTTP 請求資料(例如表單、查詢字串、路由參數或 JSON Body)轉換成物件的過程。當我們宣告一個方法像這樣:

[HttpPost]
public IActionResult Create([FromBody] Student student)
{
    ...
}

ASP.NET Core 就會讀取請求的 JSON 內容,依照屬性名稱(Name Matching)對應 Student 類別,自動建立物件實例、驗證輸入是否合法,最後再將結果傳給 Create() 方法,也就是說,開發者不必手動解析 Request Body,框架會自動處理。

Model Binding 的資料來源

Model Binding 可以從以下幾個來源取得值:

來源 範例 用法
Route /students/10 [FromRoute] int id
Query String /students?id=10 [FromQuery] int id
Form POST 表單資料 [FromForm]
Body (JSON/XML) JSON 格式內容 [FromBody]
Header HTTP 標頭 [FromHeader]

若沒有明確指定,框架會依據型別自動判斷,簡單型別(string、int、bool...)→ 查 Route、Query、Form。複合型別(class)→ 查 Body(例如 JSON)。


常用屬性

ASP.NET Core 提供多個屬性來控制綁定行為:

1️⃣ [Bind],只允許特定屬性參與綁定,例如:

[Bind("LastName,FirstName,HireDate")]
public class Instructor { ... }

或直接用在參數:

[HttpPost]
public IActionResult Create([Bind("Name,Age")] Student student)

適合「新增(Create)」場景,用於防止多餘欄位被綁定,但較不建議用在「編輯(Edit)」場景,因為未包含的屬性會變成 null。

2️⃣ [BindRequired],表示該屬性必須被綁定,否則會自動觸發 Model State 錯誤。

public class Instructor
{
    [BindRequired]
    public DateTime HireDate { get; set; }
}

3️⃣ [BindNever],防止某屬性被綁定,常用於保護 Id、CreatedDate 等系統欄位。

public class Instructor
{
    [BindNever]
    public int Id { get; set; }
}

4️⃣ [ModelBinder],自訂綁定邏輯或重新命名屬性。

[HttpPost]
public IActionResult Create(
    [ModelBinder(Name = "instructor_id")] Instructor instructor)

或使用自訂的 binder:

[ModelBinder(typeof(MyCustomBinder))]
public MyType MyProperty { get; set; }

集合與字典的綁定

ASP.NET Core 支援自動綁定陣列與字典:

public IActionResult Post(int[] selectedCourses)

可接受以下輸入:

selectedCourses=1050&selectedCourses=2000
selectedCourses[0]=1050&selectedCourses[1]=2000
selectedCourses[]=1050&selectedCourses[]=2000

同樣地,Dictionary 也能自動綁定:

public IActionResult Post(Dictionary<int, string> selectedCourses)

輸入:

selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics

Record Types 與 Constructor Binding

C# 9 的 Record 型別能讓 Model 更簡潔,ASP.NET Core 原生支援這類型的綁定與驗證。

public record Person(
    [Required] string Name,
    [Range(0, 150)] int Age,
    [BindNever] int Id);

Record 必須有「單一公開建構式」,建構式參數名稱需與屬性名稱一致(區分大小寫),驗證屬性應寫在「建構式參數」上。

而路由與 Query String 會使用 不依文化(Invariant Culture) 的格式進行綁定,如果想改成依據使用者文化設定(例如日期、數字格式),可以自訂 IValueProviderFactory。

以下為一些特殊的資料型別與其說明:

型別 說明
IFormFile / IEnumerable<IFormFile> 上傳檔案
CancellationToken 用於取消長時間執行的請求
FormCollection 直接讀取整個表單資料

輸入格式化(Input Formatters)

Model Binding 處理簡單資料,而 Input Formatter 處理整個 Request Body。ASP.NET Core 內建支援 JSON(System.Text.Json),也可加入 XML 支援。範例:

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
        options.JsonSerializerOptions.WriteIndented = true;
    });

若要支援 XML:

builder.Services.AddControllers()
    .AddXmlSerializerFormatters();

結語

Model 與 DTO 要分開設計,降低耦合度、提升安全性,善用 [BindNever] 與 [BindRequired] 避免意外修改敏感欄位。若需自訂輸入格式,可擴充 Input Formatter 或 Model Binder,對於 Record Type 的驗證屬性,請記得放在建構式參數上。
Model Binding 是 ASP.NET Core Web API 的靈魂之一,它讓開發者不用手動解析輸入資料,就能安全、簡潔地獲得正確型別的物件,在實務專案中,搭配 DTO 設計與驗證屬性,能大幅提升 API 的可維護性與安全性。


上一篇
Day26-Routing 與 Controller(ASP.NET Core Web API 教學)
下一篇
Day28-C#實體框架核心(Entity Framework Core)
系列文
30 天從 Python 轉職場 C# 新手入門29
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言