2013-11-08 4 views
2

Нам всегда говорят, что Controller должен быть тощим и что валидация должна быть выполнена в Model, а не Controller. Но рассмотрим следующий пример.Лучшая практика для проверки сложных случаев в ASP.NET/MVC?

Вот простой 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}); 
} 

ли это плохая практика? Должен ли я проверять, существует ли запись в каком-то сложном пользовательском методе проверки в модели? Должен ли я разместить его где-то еще в другом месте?

UPDATE

На соответствующую записку. Должны ли в 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); 
} 
+0

Что вы делаете в последнем примере, это не то, что я бы назвал валидацией. «PersonEditModel» по-прежнему действует. Проблема в том, что человека не существует. Это другая ошибка и правильно обрабатывается в контроллере. –

+0

Это выглядит хорошо, никаких проблем. Если кто-то говорит «нет», спросите причину PLS :) –

+0

Итак, это должно быть в контроллере? – roryok

ответ

1

Когда мы говорим о 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(); 
} 

Здесь цель состоит в том, чтобы освободить контроллер от бизнеса логическая проверка и использование контекста данных. Он подталкивает предоставленные данные и уведомляется об ошибках. Я использовал строковую переменную, но вы можете реализовать управление ошибками по своему усмотрению. Разработка ваших бизнес-правил не повлияет на ваш контроллер вообще.

2

Я вообще предпочитаю FluentValidation для всех целей. Он также имеет Nuget, чтобы установить его из коробки в VS.

образца Код подтверждения от here:

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; 

см ?? Очень легко проверить любые модели с помощью Fluent Validation с помощью чистых методов. Вы можете рассмотреть возможность пройти через FluentValidation Documentation.

Где проверить?

Предположим, что у вас есть модель, как показано ниже:

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."); 
    } 
} 

Таким образом, вы можете сделать это в отдельной модели валидатора, сохраняя при этом свои методы и модели домена максимально чистыми.

+0

Вопрос в том, где проверить. –

+0

Я использовал FluentValidation раньше, и это, безусловно, помогает в более сложных случаях, но на самом деле это не отвечает на вопрос. Является ли проверка наличия записей в модели хорошей или плохой идеей? – roryok

+0

@roryok Я не думаю, что вы раньше использовали FluentValidation, так как FluentValidation работает из коробки, если вы установили валидатор для конкретной модели, тогда он будет автоматически вызываться при условии, что вы ввели в ваши интерфейсы Ивалификатор. –

1

В этом нет ничего плохого. Ваши контроллеры несут ответственность за управление потоком управления, когда дело доходит до того, какой вид будет отображаться вашим пользователям. Частью этого является обеспечение того, что представление получает модель, которая находится в пригодном для использования состоянии.

Контроллер не заботится о том, что такое модель, или о том, что содержит модель, но это действительно так. Именно поэтому ModelState.IsValid так важен, потому что контроллер не должен знать , как, что проверка выполняется или что делает модель действительно действительной. Как правило, любая проверка, которая должна произойти после ModelState.IsValid, может быть перенесена на другой уровень вашего приложения, что опять же приведет к разделению проблем.

Смежные вопросы