[鐵人賽Day10] ASP.Net Core MVC 進化之路 - Model Binding

ASP.Net CoreModel Binding並沒有太大的改變,

比較有感的應該是Action裡面也內建DI了,

也可以讓建構注入乾淨一點。

 

 

Model Binding又稱Data Binding

ASP.Net MVC中強大的功能之一,

搭配View宣告的ViewModel(Data Model)使用

,可以無腦的繫結到你想要的資料,

但有時也會因為資料綁定順序問題踩雷踩不完。

Model Binding的對象有三大資料來源

  • Route(路由):瀏覽器上的網址,如https://localhost:44363/Home/Index/1
  • QueryString(查詢字串):URL上問號(?)後面的資訊,如https://localhost:44363/Home/Index?id=2
  • FormData(表單資料):包在<form></form>表單中的<input>,如範例:
    <form>
        <input name='id' type='hidden' value='3' />
    </form>

    建議可搭配PostmanFiddler等封包模擬工具測試。

 

那如果三個資料來源同時存在呢?

筆者後續的範例會使用Postman進行測試。

送出請求後觀察Action收到的值

 

再拿RouteQueryString比較看看

由此可知在Binding優序上FormData > Route > QueryString。

但我們可以使用[FromSource]來限定資料來源對象,

常見的有[FromRoute][FromQuery][FromForm][FromHeader][FromBody]

[FromServices]ASP.Net Core加入的新特色,

只要在Startup ConfigureServices中進行DI註冊,

就可以透過方法注入的方式獲得物件。

 

我們先建立一個MyCustomService,

public class MyCustomService
{
    public string getMyName => "My name is MyCustomService.";
}

 

並於Startup ConfigureServices中註冊DI。

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<MyCustomService>();
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

 

接著在HomeController中新增一個方法進行測試。

public void DITest([FromServices] MyCustomService myService)
{
            
}

 

使用偵錯模式進行觀察

 

接下來會針對介紹一連串Model Binding的詳細操作,

如果你有手刻前端input的需求可以參考看看。

 

當綁定對象是單一屬性時,預設使用key-value(name-value)的方式綁定,

MVCModelBinder會自動忽略大小寫

並從資料來源中尋找與變數同名的值。

 

當綁定對象是一個自訂類別時(通常是ViewModel),

我們依舊可使用key-value的方式綁定。

範例類別:

public class Pokemon
{
    public int Id { get; set; }
    public string Property { get; set; }
    public string Name { get; set; }
}

 

測試結果:

 

我們也可以使用parameter_name.property_name的方式綁定,測試如下:

 

若同時使用則綁定帶有parameter_namekey(綁定敘述較完整者):

 

當綁定的物件不只一層時,

可透過. 的方式進行綁定,

我們修改一下範例類別:

public class Pokemon
{
    public int Id { get; set; }
    public string Property { get; set; }
    public string Name { get; set; }
    public Trainer trainer { get; set; }
}

public class Trainer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Sex { get; set; }
}

 

測試結果:

 

綁定Collection(集合)型資料時,

則使用[index].property_nameparameter_name[index].property_name的方式綁定。

測試[index].property_name

 

測試parameter_name[index].property_name

 

最後講一下筆者很少用到的ModelBinder

預設資料綁定器會尋找一個特定的對象(Model),

而假設你有需要覆寫資料綁定器時,

Model Binder會幫我們從中攔截傳入的值,

修改並回傳綁定的結果。

 

筆者寫了一個簡單的範例,

當傳入參數key值為id時,

比較value後修改回傳值。

public class CustomModelBInder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var values = bindingContext.ValueProvider.GetValue("id");

        var value = Int32.Parse(values.FirstValue);
        if (value> 10)
            bindingContext.Result = ModelBindingResult.Success(value +10);
        else
            bindingContext.Result = ModelBindingResult.Success(value + 100);            

        return Task.CompletedTask;
    }
}

 

接著使用路由傳值的方式傳入id

並在id綁定前套上自訂的ModelBinder。

傳入id = 3,得到結果103:

傳入id = 11,得到結果21:

如果需要更多Model Binder的詳細介紹可參考MSDN

本篇Model Binding就介紹到這,

如果內容有誤的地方再麻煩各路大神不吝指教。

 

參考

https://docs.microsoft.com/zh-tw/aspnet/core/mvc/models/model-binding?view=aspnetcore-2.1

https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding?view=aspnetcore-2.1