什么是模型绑定?模型绑定是从HTTP请求获取数据的一个过程并且将他们提供给Action方法的参数,模型包含了构成应用程序业务逻辑的数据,它们包含了数据库或者另外数据源中的数据。
namespace AspNetCore.ModelBinding.Models { public class Employee { // 堆代码 duidaima.com public int Id { get; set; } public string Name { get; set; } public DateTime DOB { get; set; } public Address HomeAddress { get; set; } public Role Role { get; set; } } public class Address { public string HouseNumber { get; set; } public string Street { get; set; } public string City { get; set; } public string PostalCode { get; set; } public string Country { get; set; } } public enum Role { Admin, Designer, Manager } }在Models文件夹下创建一个新的文件Repository.cs,定义接口并实现该接口:
namespace AspNetCore.ModelBinding.Models { public interface IRepository { IEnumerable<Employee> Employee { get; } Employee this[int id] { get; set; } } public class EmployeeRepository : IRepository { private Dictionary<int, Employee> employee = new Dictionary<int, Employee> { [1] = new Employee { Id = 1, Name = "John", DOB = new DateTime(1980, 12, 25), Role = Role.Admin }, [2] = new Employee { Id = 2, Name = "Michael", DOB = new DateTime(1981, 5, 13), Role = Role.Designer }, [3] = new Employee { Id = 3, Name = "Rachael", DOB = new DateTime(1982, 11, 25), Role = Role.Designer }, [4] = new Employee { Id = 4, Name = "Anna", DOB = new DateTime(1983, 1, 20), Role = Role.Manager } }; public IEnumerable<Employee> Employee => employee.Values; public Employee this[int id] { get { return employee.ContainsKey(id) ? employee[id] : null; } set { employee[id] = value; } } } }我们创建repository并且创建4个员工,我们使用4个员工的数据帮助我们理解模型绑定的概念
using AspNetCore.ModelBinding.Models; using Microsoft.AspNetCore.Mvc; namespace AspNetCore.ModelBinding.Controllers { public class HomeController : Controller { private IRepository repository; public HomeController(IRepository repo) { repository = repo; } public IActionResult Index(int id = 1) { return View(repository[id]); } } }在Views->Home文件下编辑Index.cshtml 视图文件以至于它能接收到Employee模型并绑定employee属性在HTML表格,Index 代码如下:
@model Employee @{ ViewData["Title"] = "Index"; } <h2>Employee</h2> <table class="table table-bordered align-middle"> <tr><th>Id:</th><td>@Model.Id</td></tr> <tr><th>Name:</th><td>@Model.Name</td></tr> <tr><th>Date of Birth:</th><td>@Model.DOB.ToShortDateString()</td></tr> <tr><th>Role:</th><td>@Model.Role</td></tr> </table>注册Repository作为服务
builder.Services.AddSingleton<IRepository, EmployeeRepository>();在应用中更新下面代码:
using ModelBindingValidation.Models; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddSingleton<IRepository, EmployeeRepository>(); builder.Services.AddControllersWithViews(); var app = builder.Build(); ...现在运行项目并且进入URL– /Home/Index/1,将看到employee为1的员工编号显示在浏览器,图片如下:
app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}" );现在,我们看到Index方法有一个Id参数
public IActionResult Index(int id = 1) { return View(repository[id]); }ASP.NET Core模型绑定处理过程从URL中获取的Id值,并且将Id值通过参数提供给action方法
public IActionResult Index(int id) { if (id == 0) id = 1; return View(repository[Convert.ToInt32(id)]); }我们把参数id默认值移除掉,接下来我们在Index方法中打断点,然后运行应用程序并且进入URL – /Home/Index,注意在这个url中没有给定第三个参数employee id,因此 现在模型绑定将提交默认值,当断点命中时,我们将鼠标移动到id变量上检查一下值,你将看到值0,如下图所示:
public IActionResult Index(int? id) { if (id == null) id = 1; return View(repository[Convert.ToInt32(id)]); }运行你的应用程序并且进入URL– /Home/Index,现在通过断点来检查id的值, 这时你将发现它的值为null 。
public IActionResult Index(int? id) { if (id == null) id = 1; return View(repository[Convert.ToInt32(id)]); }Index方法参数是int类型因此ASP.NET Core 框架会自动转换url中id段变量的值到int值
public IActionResult Create() => View(); [HttpPost] public IActionResult Create(Employee model) => View("Index", model);HTTP GET版本的Create方法选择默认View并且没有传递任何模型给它,HTTP POST 版本的Create方法有一个Employee参数的类型(是一个复杂类型)
@model Employee @{ ViewData["Title"] = "Create Employee"; } <h2>Create Employee</h2> <form asp-action="Create" method="post"> <div class="form-group"> <label asp-for="Id"></label> <input asp-for="Id" class="form-control" /> </div> <div class="form-group"> <label asp-for="Name"></label> <input asp-for="Name" class="form-control" /> </div> <div class="form-group"> <label asp-for="DOB"></label> <input asp-for="DOB" class="form-control" /> </div> <div class="form-group"> <label asp-for="Role"></label> <select asp-for="Role" class="form-control" asp-items="@new SelectList(Enum.GetNames(typeof(Role)))"></select> </div> <button type="submit" class="btn btn-primary">Submit</button> </form>这个View包含了一个表单并且绑定了Employee实体一些公共属性,当表单被提交这些值将被提交到HTTP POST版本的Create方法
<input asp-for="Id" class="form-control" />Name属性被绑定通过下面这种方式
<input asp-for="Name" class="form-control" />模型绑定会把这些值绑定到Employee对象的属性,将Employee对象传递给Create方法的参数,Create方法使用模型绑定技术获取employee类型的参数值
public class Employee { public int Id { get; set; } public string Name { get; set; } public DateTime DOB { get; set; } public Address HomeAddress { get; set; } public Role Role { get; set; } } public class Address { public string HouseNumber { get; set; } public string Street { get; set; } public string City { get; set; } public string PostalCode { get; set; } public string Country { get; set; } }绑定HomeAddress属性时,模型绑定处理过程和前面的相同:
@model Employee @{ ViewData["Title"] = "Create Employee"; } <h2>Create Employee</h2> <form asp-action="Create" method="post"> <div class="form-group"> <label asp-for="Id"></label> <input asp-for="Id" class="form-control" /> </div> <div class="form-group"> <label asp-for="Name"></label> <input asp-for="Name" class="form-control" /> </div> <div class="form-group"> <label asp-for="DOB"></label> <input asp-for="DOB" class="form-control" /> </div> <div class="form-group"> <label asp-for="Role"></label> <select asp-for="Role" class="form-control" asp-items="@new SelectList(Enum.GetNames(typeof(Role)))"></select> </div> <div class="form-group"> <label asp-for="HomeAddress.HouseNumber"></label> <input asp-for="HomeAddress.HouseNumber" class="form-control" /> </div> <div class="form-group"> <label asp-for="HomeAddress.City"></label> <input asp-for="HomeAddress.City" class="form-control" /> </div> <div class="form-group"> <label asp-for="HomeAddress.Street"></label> <input asp-for="HomeAddress.Street" class="form-control" /> </div> <div class="form-group"> <label asp-for="HomeAddress.PostalCode"></label> <input asp-for="HomeAddress.PostalCode" class="form-control" /> </div> <div class="form-group"> <label asp-for="HomeAddress.Country"></label> <input asp-for="HomeAddress.Country" class="form-control" /> </div> <button type="submit" class="btn btn-primary">Submit</button> </form>当绑定HomeAddress属性时候,我们必须包含"HomeAddress",看Helper标签asp-for="HomeAddress.HouseNumber" 绑定HouseNumber属性,我们也为另一些属性使用这种绑定。
@model Employee @{ ViewData["Title"] = "Index"; } <h2>Employee</h2> <table class="table table-sm table-bordered table-striped"> <tr><th>Id:</th><td>@Model.Id</td></tr> <tr><th>Name:</th><td>@Model.Name</td></tr> <tr><th>Date of Birth:</th><td>@Model.DOB.ToShortDateString()</td></tr> <tr><th>Role:</th><td>@Model.Role</td></tr> <tr><th>House No:</th><td>@Model.HomeAddress?.HouseNumber</td></tr> <tr><th>Street:</th><td>@Model.HomeAddress?.Street</td></tr> <tr><th>City:</th><td>@Model.HomeAddress?.City</td></tr> <tr><th>Postal Code:</th><td>@Model.HomeAddress?.PostalCode</td></tr> <tr><th>Country:</th><td>@Model.HomeAddress?.Country</td></tr> </table>现在,运行应用程序并且进入URL– /Home/Create, 填充并提交表单,我们将发现address类型属性的显示,显示图片如下:
<div class="form-group"> <label for="HomeAddress_HouseNumber">HouseNumber</label> <input class="form-control" type="text" id="HomeAddress_HouseNumber" name="HomeAddress.HouseNumber" value="" /> </div> <div class="form-group"> <label for="HomeAddress_City">City</label> <input class="form-control" type="text" id="HomeAddress_City" name="HomeAddress.City" value="" /> </div> <div class="form-group"> <label for="HomeAddress_Street">Street</label> <input class="form-control" type="text" id="HomeAddress_Street" name="HomeAddress.Street" value="" /> </div> <div class="form-group"> <label for="HomeAddress_PostalCode">PostalCode</label> <input class="form-control" type="text" id="HomeAddress_PostalCode" name="HomeAddress.PostalCode" value="" /> </div> <div class="form-group"> <label for="HomeAddress_Country">Country</label> <input class="form-control" type="text" id="HomeAddress_Country" name="HomeAddress.Country" value="" /> </div>HouseNumber输入控件获取属性名字的值HomeAddress.HouseNumber,然而id属性的值变为HomeAddress_HouseNumber,相同的方式,City输入控件获取name属性的值,然而id的属性的值为 HomeAddress_City 。
string houseNo = Request.Form["HomeAddress.HouseNumber"];针对另外一些值也是相同的方式。
namespace AspNetCore.ModelBinding.Models { public class PersonAddress { public string City { get; set; } public string Country { get; set; } } }接下来,在HomeController类中添加一个新的action方法叫DisplayPerson
[HttpPost] public IActionResult DisplayPerson(PersonAddress personAddress) { return View(personAddress); }下一步,修改Create视图中asp-action标签的值,将该值设置为DisplayPerson,代码如下:
<form asp-action="DisplayPerson" method="post"> ... </form>现在,意味着当我们在表单中输入数据并且提交表单时,数据将被提交到DisplayPerson方法 ,在Views->Home文件夹创建一个DisplayPerson视图,这个视图包含PersonAddress类型,并显示城市和国家在模型对象
@model PersonAddress @{ ViewData["Title"] = "Person"; } <h2>Person</h2> <table class="table table-sm table-bordered table-striped"> <tr><th>City:</th><td>@Model.City</td></tr> <tr><th>Country:</th><td>@Model.Country</td></tr> </table>现在运行你的应用程序,并且进入URL- /Home/Create,填充表单并且点击提交按钮
<input class="form-control" type="text" id="HomeAddress_City" name="HomeAddress.City" value=""> <input class="form-control" type="text" id="HomeAddress_Country" name="HomeAddress.Country" value="">为了解决这个问题,我们在action方法的参数中应用[Bind(Prefix)],告诉ASP.NET Core 基于HomeAddress前缀完成模型绑定
public IActionResult DisplayPerson ([Bind(Prefix = nameof(Employee.HomeAddress))] PersonAddress personAddress) { return View(personAddress); }再次提交表单,这次我们将发现City和Country值并将他们显示在浏览器,看下面图片:
public IActionResult DisplayPerson([Bind(nameof(PersonAddress.City), Prefix = nameof(Employee.HomeAddress))] PersonAddress personAddress) { return View(personAddress); }测试一下该方法,你将看到仅仅City值显示,如下图所示:
using Microsoft.AspNetCore.Mvc.ModelBinding; namespace AspNetCore.ModelBinding.Models { public class PersonAddress { public string City { get; set; } [BindNever] public string Country { get; set; } } }
using Microsoft.AspNetCore.Mvc; namespace ModelBindingValidation.Controllers { public class FileUploadController : Controller { private IWebHostEnvironment hostingEnvironment; public FileUploadController(IWebHostEnvironment environment) { hostingEnvironment = environment; } public IActionResult Index() => View(); [HttpPost] public async Task<IActionResult> Index(IFormFile file) { string path = Path.Combine(hostingEnvironment.WebRootPath, "Images/" + file.FileName); using (var stream = new FileStream(path, FileMode.Create)) { await file.CopyToAsync(stream); } return View((object)"Success"); } } }添加一个IWebHostEnvironment 的依赖用来获取"wwwroot"文件夹的全部路径, Index方法通过模型绑定技术将上传的文件绑定到IFormFile类型参数。方法内部将文件保存到应用程序wwwroot/Images文件夹内。
@model string @{ ViewData["Title"] = "Upload File"; } <h2>Upload File</h2> <h3>@Model</h3> <form method="post" enctype="multipart/form-data"> <div class="form-group"> <input type="file" name="file" /> </div> <button type="submit" class="btn btn-primary">Submit</button> </form>浏览器看上如下: