我们总是被告知Controller应该是瘦的,验证应该在Model中进行,而不是在Controller中进行。但请考虑下面的示例。
这里有一个简单的Model和Controller,用于从编辑屏幕处理POST,我们可以在这个屏幕上编辑Person对象。
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});
}
}Model在这里执行两种验证。它验证FirstName和LastName,但也验证用于访问我们希望更改的记录的私钥(ID)。这种验证是否也应该在Model中完成?
如果我们想要展开验证(就像我们应该做的那样),以包括一个检查,看看这个记录是否存在,该怎么办?
通常,我会在控制器中验证这一点:
[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);
}发布于 2013-11-08 16:55:46
当我们谈到Model时,它包括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();
}这里的目标是将控制器从业务逻辑验证和数据上下文的使用中解放出来。它推送提交的数据,并在发生错误时得到通知。我使用了一个字符串变量,但是您可以根据需要实现错误管理。发展业务规则根本不会影响您的控制器。
发布于 2013-11-08 16:44:04
我一般都喜欢FluentValidation。它也有一个努基特来安装它--在VS中的盒子里。
来自这里的示例验证代码
using FluentValidation;
public class CustomerValidator: AbstractValidator<Customer> {
public CustomerValidator() {
RuleFor(customer => customer.Surname).NotEmpty();
RuleFor(customer => customer.Forename).NotEmpty().WithMessage("Please specify a first name");
RuleFor(customer => customer.Discount).NotEqual(0).When(customer => customer.HasDiscount);
RuleFor(customer => customer.Address).Length(20, 250);
RuleFor(customer => customer.Postcode).Must(BeAValidPostcode).WithMessage("Please specify a valid postcode");
}
private bool BeAValidPostcode(string postcode) {
// custom postcode validating logic goes here
}
}
Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();
ValidationResult results = validator.Validate(customer);
bool validationSucceeded = results.IsValid;
IList<ValidationFailure> failures = results.Errors;看到没?用简洁的方法对任何类型的模型进行流验证是非常容易的。您可以考虑通过FluentValidation文档。
在哪里验证?
假设您有一个模型如下所示:
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
virtual public ICollection<Image> Images { get; set; }
}然后,您将在类似的类库中定义另一个验证器模型,或者最好是一个处理项目中所有模型的验证的新类库。
public class CategoryValidator : AbstractValidator<Category>
{
public CategoryValidator()
{
RuleFor(x => x.Name).NotEmpty().WithMessage("Category name is required.");
}
}因此,您可以在一个单独的验证器模型中这样做,以保持您的方法和域模型尽可能干净。
发布于 2013-11-08 16:57:50
这绝对没什么不对。当涉及到要向用户显示的视图时,您的控制器负责指导控制流。这样做的一部分是确保视图得到一个处于可用状态的模型。
控制器不关心模型是什么,也不关心模型包含什么,但它关心的是它是否有效。这就是为什么ModelState.IsValid如此重要的原因,因为控制器不需要知道验证是如何执行的,或者是什么直接使模型有效。通常,需要在ModelState.IsValid之后进行的任何验证都可以推送到应用程序的另一层,这再次强制分离关注点。
https://stackoverflow.com/questions/19864181
复制相似问题