iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 10
1

前言

ASP.Net Core中Model Binding並沒有太大的改變,
比較有感的應該是Action裡面也內建DI了,
也可以讓建構注入乾淨一點。

同步發表於個人點部落 - [鐵人賽Day10] ASP.Net Core MVC 進化之路 - Model Binding

Model Binding

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>
    
    建議可搭配Postman或Fiddler等封包模擬工具測試。

那如果三個資料來源同時存在呢?
筆者後續的範例會使用Postman進行測試。

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

再拿Route跟QueryString比較看看。

由此可知在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)
{
            
}

使用偵錯模式進行觀察。

https://az787680.vo.msecnd.net/user/%E5%B7%A5%E7%A8%8B%E8%89%AF%E7%94%B0/e4a3551a-dc0f-4d8f-a2a6-d7a89746ff0a/1538921004_15801.png

接下來會針對介紹一連串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


上一篇
[鐵人賽Day09] - Dependency Injection實作
下一篇
[鐵人賽Day11] - View(1) / 資料傳遞及Razor語法
系列文
菜鳥練等區-ASP.Net Core MVC進化之路30

尚未有邦友留言

立即登入留言