C# 在ASP.NET/MVC中验证复杂案例的最佳实践?
我们总是被告知C# 在ASP.NET/MVC中验证复杂案例的最佳实践?,c#,asp.net,asp.net-mvc,validation,C#,Asp.net,Asp.net Mvc,Validation,我们总是被告知控制器应该很瘦,验证应该在模型中进行,而不是在控制器中进行。但是请考虑下面的例子。 这里有一个简单的模型和控制器,用于从编辑屏幕处理帖子,我们可以在上面编辑人物对象 public class PersonEditModel { [Required(ErrorMessage = "No ID Passed")] public int ID { get; set; } [Required(ErrorMessage = "First na
控制器
应该很瘦,验证应该在模型
中进行,而不是在控制器
中进行。但是请考虑下面的例子。
这里有一个简单的模型
和控制器
,用于从编辑屏幕处理帖子
,我们可以在上面编辑人物
对象
public class PersonEditModel
{
[Required(ErrorMessage = "No ID Passed")]
public int ID { get; set; }
[Required(ErrorMessage = "First name Required")]
[StringLength(50,ErrorMessage = "Must be under 50 characters")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Last name Required")]
[StringLength(50,ErrorMessage = "Must be under 50 characters")]
public string LastName { get; set; }
}
public class PersonController : Controller
{
// [HttpGet]View, [HttpGet]Edit Controller methods omitted for brevity
[HttpPost]
public ActionResult Edit(PersonEditModel model)
{
// save changes to the record
return RedirectToAction("View", "Person", new { ID = model.ID});
}
}
模型在这里执行两种验证。它验证FirstName
和LastName
,但它也验证用于访问我们希望更改的记录的私钥(ID
)。该验证是否也应在模型中进行
如果我们想要扩展验证(正如我们应该做的那样)以包含一个检查来查看此记录是否存在,该怎么办
通常,我会在控制器中验证这一点:
[HttpPost]
public ActionResult Edit(PersonEditModel model)
{
using(DatabaseContext db = new DatabaseContext())
{
var _person = db.Persons.Where(x => x.ID == model.ID);
if(_person == null)
{
ModelState.AddError("This person does not exist!");
// not sure how we got here, malicious post maybe. Who knows.
// so since the ID is invalid, we return the user to the Person List
return RedirectToAction("List", Person");
}
// save changes
}
// if we got here, everything likely worked out fine
return RedirectToAction("View", "Person", new { ID = model.ID});
}
这是坏习惯吗?我是否应该检查该记录是否存在于模型中某种复杂的自定义验证方法中?我应该把它完全放在别的地方吗
更新
在相关的注释上。ViewModel
是否应包含填充数据的方法
以下哪一项是更好的练习
public class PersonViewModel
{
public Person person { get; set; }
public PersonViewModel(int ID){
using(DatabaseContext db = new DatabaseContext())
{
this.person = db.Persons.Where(x => x.ID == ID);
}
}
}
[HttpPost]
public ActionResult View(int ID)
{
return View("View", new PersonViewModel(ID));
}
还是这个
public class PersonViewModel
{
public Person person { get; set; }
}
[HttpPost]
public ActionResult View(int ID)
{
PersonViewModel model = new PersonViewModel();
using(DatabaseContext db = new DatabaseContext())
{
model.person = db.Persons.Where(x => x.ID == ID);
}
return View("View", model);
}
总的来说,我更喜欢。它还有一个在VS中开箱即用的安装方法
样本验证代码来自:
使用FluentValidation;
公共类CustomerValidator:AbstractValidator{
公共CustomerValidator(){
RuleFor(customer=>customer.姓氏).NotEmpty();
RuleFor(customer=>customer.Forename).NotEmpty().WithMessage(“请指定名字”);
规则(customer=>customer.Discount).NotEqual(0).When(customer=>customer.HasDiscount);
RuleFor(customer=>customer.Address).Length(20250);
RuleFor(customer=>customer.Postcode).Must(BeAValidPostcode).WithMessage(“请指定有效的邮政编码”);
}
私有布尔BeAValidPostcode(字符串邮政编码){
//自定义邮政编码验证逻辑在这里
}
}
客户=新客户();
CustomerValidator validator=新CustomerValidator();
ValidationResult=validator.Validate(客户);
bool validationSucceeded=results.IsValid;
IList failures=结果。错误;
看??用干净的方法进行流畅的验证,很容易验证任何类型的模型。你可以考虑通过。
在哪里验证?
假设您有一个如下所示的模型:
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
virtual public ICollection<Image> Images { get; set; }
}
公共类类别
{
公共int ID{get;set;}
公共字符串名称{get;set;}
虚拟公共ICollection映像{get;set;}
}
然后,您将在一个类似的类库中定义另一个验证器模型,或者最好是一个处理项目中所有模型的验证的新类库
public class CategoryValidator : AbstractValidator<Category>
{
public CategoryValidator()
{
RuleFor(x => x.Name).NotEmpty().WithMessage("Category name is required.");
}
}
public类CategoryValidator:AbstractValidator
{
公共类别验证器()
{
RuleFor(x=>x.Name).NotEmpty().WithMessage(“需要类别名称”);
}
}
因此,您可以在一个单独的验证器模型中完成这项工作,使您的方法和域模型尽可能干净。我通常更愿意用于所有目的。它还有一个在VS中开箱即用的安装方法
样本验证代码来自:
使用FluentValidation;
公共类CustomerValidator:AbstractValidator{
公共CustomerValidator(){
RuleFor(customer=>customer.姓氏).NotEmpty();
RuleFor(customer=>customer.Forename).NotEmpty().WithMessage(“请指定名字”);
规则(customer=>customer.Discount).NotEqual(0).When(customer=>customer.HasDiscount);
RuleFor(customer=>customer.Address).Length(20250);
RuleFor(customer=>customer.Postcode).Must(BeAValidPostcode).WithMessage(“请指定有效的邮政编码”);
}
私有布尔BeAValidPostcode(字符串邮政编码){
//自定义邮政编码验证逻辑在这里
}
}
客户=新客户();
CustomerValidator validator=新CustomerValidator();
ValidationResult=validator.Validate(客户);
bool validationSucceeded=results.IsValid;
IList failures=结果。错误;
看??用干净的方法进行流畅的验证,很容易验证任何类型的模型。你可以考虑通过。
在哪里验证?
假设您有一个如下所示的模型:
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
virtual public ICollection<Image> Images { get; set; }
}
公共类类别
{
公共int ID{get;set;}
公共字符串名称{get;set;}
虚拟公共ICollection映像{get;set;}
}
然后,您将在一个类似的类库中定义另一个验证器模型,或者最好是一个处理项目中所有模型的验证的新类库
public class CategoryValidator : AbstractValidator<Category>
{
public CategoryValidator()
{
RuleFor(x => x.Name).NotEmpty().WithMessage("Category name is required.");
}
}
public类CategoryValidator:AbstractValidator
{
公共类别验证器()
{
RuleFor(x=>x.Name).NotEmpty().WithMessage(“需要类别名称”);
}
}
因此,您可以在一个单独的验证器模型中实现这一点,使您的方法和域模型尽可能干净。当我们谈论模型时,它包括您的DAL和业务层。对于小型应用程序或演示,在控制器中看到此类代码并不罕见,但通常情况下,您应该将该角色赋予业务层或数据层:
[HttpPost]
public ActionResult Edit(PersonEditModel model)
{
// Validation round one, using attributes defined on your properties
// The model binder checks for you if required fields are submitted, with correct length
if(ModelState.IsValid)
{
// Validation round two, we push our model to the business layer
var errorMessage = this.personService.Update(model);
// some error has returned from the business layer
if(!string.IsNullOrEmpty(errorMessage))
{
// Error is added to be displayed to the user
ModelState.AddModelError(errorMessage);
}
else
{
// Update successfull
return RedirectToAction("View", "Person", new { ID = model.ID});
}
}
// Back to our form with current model values, as they're still in the ModelState
return View();
}
这里的目标是将控制器从业务逻辑验证和数据上下文的使用中解放出来。它推送提交的数据,并在发生错误时收到通知。我使用了一个字符串变量,但是您可以根据需要实现错误管理。改进您的业务规则根本不会影响您的控制器。当我们谈论模型时,它包括您的DAL和您的业务层。对于小型应用程序或演示,在控制器中看到此类代码并不罕见,但通常情况下,您应该将该角色赋予业务层或数据层:
[HttpPost]
public ActionResult Edit(PersonEditModel model)
{
// Validation round one, using attributes defined on your properties
// The model binder checks for you if required fields are submitted, with correct length
if(ModelState.IsValid)
{
// Validation round two, we push our model to the business layer
var errorMessage = this.personService.Update(model);
// some error has returned from the business layer
if(!string.IsNullOrEmpty(errorMessage))
{
// Error is added to be displayed to the user
ModelState.AddModelError(errorMessage);
}
else
{
// Update successfull
return RedirectToAction("View", "Person", new { ID = model.ID});
}
}
// Back to our form with current model values, as they're still in the ModelState
return View();
}
这里的目标是将控制器从业务逻辑验证和数据上下文的使用中解放出来。它推送提交的数据,并