3.4 模型 (Model) 与模型绑定 (Model Binding) 3.4 模型 (Model) 与模型绑定 (Model Binding) 在ASP.NET Core MVC中,模型(Model)是应用程序数据表示的核心。模型绑定(Model Binding)是将HTTP请求数据转换为应用程序可以使用的模型的过程。理解模型和模型绑定对于构建健壮、可维护的ASP.NET Core MVC应用程序至关重要。 3.4.1 模型 (Model) 的概念 模型是应用程序中数据的表示。它可以是简单的类、复杂的对象图,甚至是数据库实体。模型的职责包括: 数据封装: 将相关的数据字段组合在一起。 数据验证: 定义数据的有效性规则,确保数据的完整性。
在ASP.NET Core MVC中,模型(Model)是应用程序数据表示的核心。模型绑定(Model Binding)是将HTTP请求数据转换为应用程序可以使用的模型的过程。理解模型和模型绑定对于构建健壮、可维护的ASP.NET Core MVC应用程序至关重要。
模型是应用程序中数据的表示。它可以是简单的类、复杂的对象图,甚至是数据库实体。模型的职责包括:
数据封装: 将相关的数据字段组合在一起。
数据验证: 定义数据的有效性规则,确保数据的完整性。
业务逻辑: 可以包含与数据相关的业务逻辑,例如计算属性或数据转换。
在ASP.NET Core MVC中,模型通常被用于:
从数据库检索数据: 模型可以映射到数据库表,用于读取和写入数据。
向视图传递数据: 模型被传递给视图,用于显示数据。
接收用户输入: 模型可以绑定到HTTP请求数据,用于接收用户输入。
示例:一个简单的模型类
public class Product { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public bool IsAvailable { get; set; } }
模型绑定是将HTTP请求数据(例如查询字符串、表单数据、路由数据、请求头等)转换为应用程序可以使用的模型的过程。ASP.NET Core MVC框架会自动执行模型绑定,从而简化了开发过程。
模型绑定的流程:
HTTP请求: 客户端向服务器发送HTTP请求,其中包含数据。
模型绑定器: ASP.NET Core MVC框架根据请求的Content-Type和参数类型选择合适的模型绑定器。
数据源: 模型绑定器从不同的数据源(例如查询字符串、表单数据、路由数据、请求体)读取数据。
模型创建和填充: 模型绑定器根据数据源中的数据创建模型的实例,并填充其属性。
控制器操作: 模型绑定器将填充后的模型传递给控制器操作作为参数。
模型绑定的数据来源:
查询字符串: URL中的参数,例如?id=123&name=MyProduct。
表单数据: HTML表单提交的数据,通常用于POST请求。
路由数据: URL路由中定义的参数,例如/products/{id}。
请求体: HTTP请求的body部分,通常用于JSON或XML格式的数据。
请求头: HTTP请求的header部分,例如Content-Type。
3.4.3.1 绑定到查询字符串
// Controller Action public IActionResult Details(int id, string name) { // id 和 name 将从查询字符串中绑定 // 例如:/Products/Details?id=123&name=MyProduct ViewBag.Id = id; ViewBag.Name = name; return View(); }
3.4.3.2 绑定到表单数据
// Model public class ProductViewModel { public string Name { get; set; } public decimal Price { get; set; } } // Controller Action (GET) public IActionResult Create() { return View(); } // Controller Action (POST) [HttpPost] public IActionResult Create(ProductViewModel model) { if (ModelState.IsValid) { // 处理模型数据 // 例如:保存到数据库 return RedirectToAction("Index"); } return View(model); }
<!-- View (Create.cshtml) --> <form asp-action="Create" method="post"> <div class="form-group"> <label asp-for="Name">Name:</label> <input asp-for="Name" class="form-control" /> <span asp-validation-for="Name" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Price">Price:</label> <input asp-for="Price" class="form-control" /> <span asp-validation-for="Price" class="text-danger"></span> </div> <button type="submit" class="btn btn-primary">Create</button> </form>
3.4.3.3 绑定到路由数据
// Controller Action [Route("products/{id}")] public IActionResult Details(int id) { // id 将从路由数据中绑定 // 例如:/products/123 ViewBag.Id = id; return View(); }
3.4.3.4 绑定到请求体 (JSON)
// Model public class ProductApiModel { public string Name { get; set; } public decimal Price { get; set; } } // Controller Action [HttpPost] [Route("api/products")] public IActionResult Create([FromBody] ProductApiModel model) { if (ModelState.IsValid) { // 处理模型数据 // 例如:保存到数据库 return Ok(model); } return BadRequest(ModelState); }
客户端发送的JSON数据:
{ "name": "MyProduct", "price": 19.99 }
3.4.3.5 绑定到复杂对象
模型绑定器可以绑定到包含其他对象的复杂对象。
public class Order { public int OrderId { get; set; } public DateTime OrderDate { get; set; } public Customer Customer { get; set; } public List<OrderItem> OrderItems { get; set; } } public class Customer { public int CustomerId { get; set; } public string Name { get; set; } public string Email { get; set; } } public class OrderItem { public int OrderItemId { get; set; } public int ProductId { get; set; } public int Quantity { get; set; } public decimal Price { get; set; } }
要绑定到这个复杂的 Order 对象,你可以使用表单数据或JSON数据。 例如,使用表单数据,你可以这样发送数据:
<form asp-action="CreateOrder" method="post"> <input type="hidden" name="OrderId" value="1" /> <input type="date" name="OrderDate" value="2024-01-01" /> <input type="text" name="Customer.Name" value="John Doe" /> <input type="email" name="Customer.Email" value="john.doe@example.com" /> <input type="number" name="OrderItems[0].ProductId" value="1" /> <input type="number" name="OrderItems[0].Quantity" value="2" /> <input type="number" name="OrderItems[0].Price" value="19.99" /> <input type="number" name="OrderItems[1].ProductId" value="2" /> <input type="number" name="OrderItems[1].Quantity" value="1" /> <input type="number" name="OrderItems[1].Price" value="29.99" /> <button type="submit">Create Order</button> </form>
对应的Controller Action:
[HttpPost] public IActionResult CreateOrder(Order order) { if (ModelState.IsValid) { // 处理 order 对象 return Ok(order); } return BadRequest(ModelState); }
模型验证是确保模型数据符合预定义规则的过程。ASP.NET Core MVC提供了强大的模型验证机制,可以方便地验证用户输入的数据。
3.4.4.1 数据注解 (Data Annotations)
数据注解是System.ComponentModel.DataAnnotations命名空间中的属性,可以应用于模型的属性,用于定义验证规则。
常用的数据注解:
[Required]: 指定属性是必需的。
[StringLength]: 指定字符串属性的最大和最小长度。
[Range]: 指定数值属性的范围。
[EmailAddress]: 验证属性是否为有效的电子邮件地址。
[RegularExpression]: 使用正则表达式验证属性。
[Compare]: 验证两个属性是否相等。
示例:使用数据注解进行模型验证
public class User { [Required(ErrorMessage = "用户名是必需的")] [StringLength(50, MinimumLength = 3, ErrorMessage = "用户名长度必须在3到50个字符之间")] public string Username { get; set; } [Required(ErrorMessage = "密码是必需的")] [DataType(DataType.Password)] public string Password { get; set; } [DataType(DataType.Password)] [Compare("Password", ErrorMessage = "密码和确认密码不匹配")] public string ConfirmPassword { get; set; } [EmailAddress(ErrorMessage = "无效的电子邮件地址")] public string Email { get; set; } }
3.4.4.2 自定义验证 (Custom Validation)
除了数据注解,还可以创建自定义验证规则,以满足特定的业务需求。
示例:创建自定义验证属性
public class ValidAgeAttribute : ValidationAttribute { private readonly int _minimumAge; public ValidAgeAttribute(int minimumAge) { _minimumAge = minimumAge; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { if (value is DateTime birthDate) { var age = DateTime.Now.Year - birthDate.Year; if (birthDate > DateTime.Now.AddYears(-age)) age--; if (age >= _minimumAge) { return ValidationResult.Success; } else { return new ValidationResult($"必须年满{_minimumAge}岁才能注册."); } } return new ValidationResult("无效的出生日期."); } }
使用自定义验证属性:
public class User { [ValidAge(18, ErrorMessage = "必须年满18岁才能注册")] public DateTime BirthDate { get; set; } }
3.4.4.3 验证结果 (ModelState)
在控制器操作中,可以通过ModelState.IsValid属性来检查模型是否有效。如果模型无效,可以使用ModelState.AddModelError()方法添加自定义错误信息。
[HttpPost] public IActionResult Register(User model) { if (ModelState.IsValid) { // 处理模型数据 return RedirectToAction("Index"); } else { // 显示验证错误 return View(model); } }
在视图中,可以使用asp-validation-summary和asp-validation-for标签助手来显示验证错误信息。
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="Username">Username:</label> <input asp-for="Username" class="form-control" /> <span asp-validation-for="Username" class="text-danger"></span> </div>
虽然ASP.NET Core MVC提供了内置的模型绑定器,但在某些情况下,可能需要创建自定义的模型绑定器来处理特定的数据类型或绑定逻辑。
3.4.5.1 创建自定义模型绑定器
public class CustomDateModelBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } // 获取字段名称 string fieldName = bindingContext.FieldName; // 从ValueProvider中获取值 IValueProvider valueProvider = bindingContext.ValueProvider; ValueProviderResult valueProviderResult = valueProvider.GetValue(fieldName); if (valueProviderResult == ValueProviderResult.None) { return Task.CompletedTask; } bindingContext.ModelState.SetModelValue(fieldName, valueProviderResult); string value = valueProviderResult.FirstValue; // 尝试解析日期 if (DateTime.TryParseExact(value, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsedDate)) { bindingContext.Result = ModelBindingResult.Success(parsedDate); return Task.CompletedTask; } // 如果解析失败,则添加错误 bindingContext.ModelState.AddModelError(fieldName, "无效的日期格式 (yyyy-MM-dd)"); bindingContext.Result = ModelBindingResult.Failed(); return Task.CompletedTask; } }
3.4.5.2 注册自定义模型绑定器
可以通过多种方式注册自定义模型绑定器:
使用[ModelBinder]属性: 将[ModelBinder]属性应用于控制器操作的参数或模型的属性。
public IActionResult Details([ModelBinder(typeof(CustomDateModelBinder))] DateTime date) { // ... }
使用ModelBinderProvider: 创建一个实现IModelBinderProvider接口的类,并在Startup.cs文件中注册它。
public class CustomDateModelBinderProvider : IModelBinderProvider { public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (context.Metadata.ModelType == typeof(DateTime)) { return new CustomDateModelBinder(); } return null; } }
在Startup.cs文件中注册:
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(options => { options.ModelBinderProviders.Insert(0, new CustomDateModelBinderProvider()); }); }
模型和模型绑定是ASP.NET Core MVC开发中的重要概念。通过理解模型和模型绑定的工作原理,可以构建更健壮、更易于维护的应用程序。
模型定义了应用程序的数据结构和验证规则。
模型绑定将HTTP请求数据转换为模型实例。
模型验证确保模型数据符合预定义的规则。
自定义模型绑定器允许处理特定的数据类型或绑定逻辑。
通过掌握这些概念,可以有效地处理用户输入,并构建高质量的ASP.NET Core MVC应用程序。