iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 12
1
Modern Web

我與 ASP.NET Core 的 30天系列 第 12

[Day12] 模型繫結與驗證 - 我與 ASP.NET Core 3 的 30天

  • 分享至 

  • xImage
  •  

模型繫結(Model Binding)

模型繫結(Model Binding)主要是Http Request與Action之間的橋樑。
以往不用框架處理來自HTTP Request的做法,就是要一個欄位一個欄位取得內容並轉型後塞入自己定義的類別中,但是這些動作相當繁瑣且容易發生錯誤。ASP.NET Core就透過了模型繫結的方式自動化這個流程。

關於模型繫結:

  • 從各種來源(例如,路由資料、表單欄位和query string)抓取資料。
  • 提供資料給 Razor 方法參數和公用屬性中的控制器和頁面。
  • 將Request回來的字串資料解析並轉換成能辨識的型別。
  • 更新複雜類型的屬性。

接著我們來以API的Controller來介紹Model Binding
首先在API Controller中預設都會套用[ApiController]這個屬性,那套用這個屬性對於Model Binding有什麼影響呢?
套用了[ApiController]會有下列影響

  • 必須要使用屬性路由(Attribute Routing)
  • 只要發生模型驗證失敗,就會自動回應 HTTP 400 (Bad Request)
  • 自動套用模型繫結的預設規則
    • 複雜型別預設就會自動套用[FromBody]屬性
    • 參數型別如果是 IFormFile 或 IFormFileCollection 的話會自動套用[FromForm]屬性,且該屬性也只能用在這兩個型別
    • 簡單模型或任何其他參數,全部都會自動套用[FromQuery]屬性。例如「路由參數」預設會自動套用[FromQuery]

Model Binding也有以下來源屬性可以設定

  • [FromQuery] - 只會從Query String中取值。預設只會套用在簡單模型上
  • [FromRoute] - 只會從路由資料取值。預設只會套用簡單模型,實務上通常不會特別套用
  • [FromForm] - 從Requet body取得值並且只會套用在IFrom或是IFormFileCollection 複雜模型。
  • [FromBody] - 只會從Request body取值。預設套用在複雜模型。
  • [FromHeader] - 取得Request header的值並只能套用簡單模型。
  • [FromService] - 從DI容器中取得服務物件。

以上的預設情況都是在套用[ApiController]的情境之下。

接著我們就來做個範例,測試一下以上幾種來源吧

首先重新建立一個SampleController,並加入以下內容

[ApiController]
Route("api/[controller]")]
public class SampleController : ControllerBase
{
    [HttpGet("{id}")]
    public ActionResult<string> Get([FromRoute] int id, 
                                    [FromQuery] int query,
                                    [FromHeader] string header1)
    {
        return $"query: {query}, route: {route}, header: {header1}";
    }
    
    [HttpPost]
    //public ActionResult<Demo> Post([FromBody]Demo demo)
    public ActionResult<DemoUser> Post(DemoUser demo)
    {
        return demo;
    }

    public class DemoUser
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

首先呼叫Get的Action,看呼叫完的輸出結果
https://ithelp.ithome.com.tw/upload/images/20200926/20129389DlB2hdss3d.png

接著看到Post的Action,因為預設就套用了[FromBody],所以不管有沒有在參數上加上[FromBody]都會有一樣的結果
https://ithelp.ithome.com.tw/upload/images/20200926/20129389j2kQGBkq44.png

模型驗證

在Model Binding的過程中,也可以透過對每個屬性的設定進行驗證
我們使用剛才的範例,並稍微改動一下DemoUser這個類別

using System.ComponentModel.DataAnnotations;
...
public class DemoUser
{
    [Required]
    [StringLength(6, ErrorMessage = "名字長度必須介於 {2} 到 {1}個字", MinimumLength = 2)]
    public string Name { get; set; }
    [Range(5,50)]
    public int Age { get; set; }
    [Required(ErrorMessage = "Email 為必填")]
    [EmailAddress]
    public string Email { get; set; }
    public string Password { get; set; }
    [Compare("Password")]
    public string ConfirmPassword { get; set; }
}

上述的範例我們加上了幾個欄位,並為這些欄位加上驗證屬性(Validate Attribute),驗證屬性可以讓使用者指定模型屬性的驗證規則。
(使用前記得 using System.ComponentModel.DataAnnotations;System.ComponentModel.DataAnnotations;)

接著用Postman呼叫觀看結果
https://ithelp.ithome.com.tw/upload/images/20200926/20129389tczpU97VXP.png
利用模型驗證可以更方便地達到資料格式的驗證,而且在套用[ApiController]之後,Request的資料如果沒有通過模型驗證,就會直接回傳Http 400,並搭配錯誤訊息回應給用戶端。

以下是比較常用的內建驗證屬性:

  • [CreditCard]:驗證屬性是否符合信用卡格式。
  • [Compare]:驗證模型中的兩個屬性是否相符。
  • [EmailAddress]:驗證屬性是否符合電子郵件格式。
  • [Phone]:驗證屬性是否符合電話號碼格式。
  • [Range]:驗證屬性值是否落在指定的範圍內。
  • [RegularExpression]:驗證屬性值是否符合指定的正則運算式。
  • [Required]:驗證欄位不是 null。
  • [StringLength]:驗證字串屬性值不超過指定的長度限制。
  • [Url]:驗證屬性是否具有 URL 格式。
  • [Remote]:在伺服器上呼叫動作方法,以驗證用戶端上的輸入。

相關參考

ASP.NET Core 中的資料繫結
Model validation in ASP.NET Core MVC and Razor Pages


上一篇
[Day11] URL重寫與URL重新導向 - 我與 ASP.NET Core 3 的 30天
下一篇
[Day13] CORS 跨來源資源共用 - 我與 ASP.NET Core 3 的 30天
系列文
我與 ASP.NET Core 的 30天31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言