问题
I have a MVC web project. According to best practice, where is the correct place to add my validation rules and business rules?
Validation rules would be the required fields and required formats.
Business rules would be "this email is already taken in the database"
Here's how I am currently doing it in my register model:
public class RegisterModel : IValidatableObject
{
[Display(Name = "Email address")]
[Required(ErrorMessage = "The email address is required")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var retVal = new List<ValidationResult>();
using (var entity = new AcademicUniteDatabaseEntities())
{
if (entity.UserProfiles.Any(x => x.UserName == this.Email))
{
retVal.Add(new ValidationResult("Email already exist", new List<string> { "Email" }));
}
}
return retVal;
}
}
Here's my register controller:
public ActionResult Register()
{
var model = new RegisterModel();
return this.View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (!this.ModelState.IsValid)
{
return this.View(model);
}
model.CreateAccount();
return this.View("WaitConfirmEmail");
}
Why I'm doing it this way
- When I check ModelState.IsValid in the controller, it will have checked the format of the email and if it already exists in the database. I don't make any calls to the database in my controller, only in my model. (is this best practice?)
- It also binds the "Email already exist" validation result to my Email property so I can display the validation results on my view.
Is this best practice?
- Is this the correct way to add my business rules in MVC?
- Why or why not?
- If this is not best practice, can you provide me of an example of how this register model would be best programmed checking business rules (if email already exists)?
回答1:
One alternative is to create a custom validator attribute. With your solution, you could end up with very big models. Another alternative is to create static helpers that you can plug in the controller. There is no right answer here, it's just the way you'd like to organize code. My preference is to spread the business logic across custom attributes you can plug and play. This way you can refactor seamlessly.
回答2:
Using annotations to validate your models is fine for small applications that require validation, but when your validation rules start to become more and more complicated and your models begin to get more complex then I'd suggest looking at a library like Fluent Validation.
The problem with validate your models via attributes is it can be quite messy and you'll begin to suffer from a lot of repetition; where as the benefits you gain from using a library like Fluent Validation are:
- Allows you to keep you models clean and uncluttered
- Create far more maintainable and reusable validation rules thanks to DI support.
- Isn't tied to the UI like ModelState.IsValid is, so can be reused in your business logic layer, etc.
An example of a typical Fluent Validation rule:
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;
来源:https://stackoverflow.com/questions/29866791/validation-rules-and-business-rules-in-mvc