ASP.NET Core MVC无法绑定表单数据到Customer领域模型的原因
我有以下领域类Customer:
public class Customer { public int Id { get; set; } public string Name { get; set; } public DateTime BirthDate { get; set; } public bool IsSubscribedToNewsLetter { get; set; } [ForeignKey("MembershipTypeId")] public MembershipType MembershipType { get; set; } public byte MembershipTypeId { get; set; } }
以及MembershipType类:
public class MembershipType { public byte Id { get; set; } public string Name { get; set; } public short SignUpFee { get; set; } public byte DurationInMonths { get; set; } public byte DiscountRate { get; set; } public ICollection<Customer> Customers { get; set; } }
为实现客户注册表单的会员类型下拉选择功能,我创建了视图模型CustomerAndMembershipTypesViewModels:
public class CustomerAndMembershipTypesViewModels { public Customer Customer { get; set; } public IEnumerable<MembershipType>? MembershipTypes { get; set; } }
视图基于该视图模型开发,表单提交至AddCustomer方法。在ASP.NET MVC中,该方法可直接将表单数据绑定到Customer参数,但在ASP.NET Core MVC中报错,请问为何ASP.NET Core MVC无法识别仅提交了领域模型相关值?
核心原因
ASP.NET Core MVC的模型绑定规则比旧版ASP.NET MVC更严格。旧版中,模型绑定会自动从请求数据里提取匹配的属性值,哪怕表单字段是Customer.Name这类嵌套格式,也能绑定到顶级的Customer参数。但ASP.NET Core MVC默认要求请求数据的结构必须和目标参数的结构完全匹配。
你的视图基于CustomerAndMembershipTypesViewModels生成,表单字段名称是Customer.Name、Customer.MembershipTypeId这类嵌套格式。而AddCustomer方法如果直接用Customer作为参数,ASP.NET Core MVC会去寻找顶级的Name、MembershipTypeId字段,找不到就会绑定失败报错。
解决方案
有两种常用解决方式:
修改Action参数为视图模型类型
把AddCustomer的参数改成CustomerAndMembershipTypesViewModels,再从模型里提取Customer对象使用:[HttpPost] public IActionResult AddCustomer(CustomerAndMembershipTypesViewModels model) { if (ModelState.IsValid) { _context.Customers.Add(model.Customer); _context.SaveChanges(); return RedirectToAction("Index"); } model.MembershipTypes = _context.MembershipTypes.ToList(); return View(model); }使用前缀绑定
如果不想修改参数类型,可在Customer参数上添加[Bind(Prefix = "Customer")]特性,告诉模型绑定器从Customer前缀下提取数据:[HttpPost] public IActionResult AddCustomer([Bind(Prefix = "Customer")]Customer customer) { if (ModelState.IsValid) { _context.Customers.Add(customer); _context.SaveChanges(); return RedirectToAction("Index"); } var viewModel = new CustomerAndMembershipTypesViewModels { Customer = customer, MembershipTypes = _context.MembershipTypes.ToList() }; return View(viewModel); }
注意:使用Bind特性时,建议明确指定需要绑定的属性(比如[Bind(Prefix = "Customer", Include = "Name,BirthDate,IsSubscribedToNewsLetter,MembershipTypeId")]),避免过度绑定带来的安全风险。
内容的提问来源于stack exchange,提问作者Sagar Tube

