简体   繁体   English

Model和ViewModel的验证最佳实践

[英]Validation best practice for Model and ViewModel

I have separate model and viewmodel classes. 我有单独的模型和视图模型类。 Where viewmodel classes only do UI level validation (refer: Validation: Model or ViewModel ). 其中viewmodel类仅进行UI级别验证(请参阅: 验证:模型或ViewModel )。

I can verify on post action in a controller that model (vewmodel) is valid. 我可以在控制器中验证模型(vewmodel)是否有效。

The ask: How do I validate the model (the main entity with data annotations). 问:我如何验证模型(带有数据注释的主要实体)。

I am not developing the viewmodel using model object. 我没有使用模型对象开发viewmodel。 Just duplicating the properties and adding all properties possibly required in that particular view. 只需复制属性并添加该特定视图中可能需要的所有属性。

//Model Class
public class User
{
    [Required]
    public string Email {get; set;}

    [Required]
    public DateTime Created {get; set;}
}

//ViewModel Class
public class UserViewModel
{
    [Required]
    public string Email {get; set;}

    [Required]
    public string LivesIn {get; set;}
}

//Post action
public ActionResult(UserViewModel uvm)
{
    if( ModelState.IsValid)
        //means user entered data correctly and is validated

    User u = new User() {Email = uvm.Email, Created = DateTime.Now};
    //How do I validate "u"?

    return View();
}

Should do something like this: 应该做这样的事情:

var results = new List<ValidationResult>();
var context = new ValidationContext(u, null, null);
var r = Validator.TryValidateObject(u, context, results);

What I am thinking is adding this validation technique in the base class (of business entity), and verify it when I am mapping from viewmodel class to business entity. 我在想的是在基类(业务实体)中添加这种验证技术,并在我从viewmodel类映射到业务实体时验证它。

Any suggestions? 有什么建议?

1) Use fluent validation on the model that the retrieves information from the user. 1)对从用户检索信息的模型使用流畅验证。 it is more flexible then data annotation and easier to test. 它比数据注释更灵活,更容易测试。

2) You might want to look into automapper, by using automapper you don't have to write x.name = y.name . 2)您可能希望查看automapper,通过使用automapper,您不必编写x.name = y.name

3) For your database model I would stick to the data-annotations. 3)对于您的数据库模型,我会坚持使用数据注释。

Everything below is based on the new information 以下所有内容均基于新信息

First and all you should place validation on both location like you did now for the actual model validation this is how I would do it. 首先,您应该像现在一样对两个位置进行验证,以进行实际的模型验证,这就是我要做的。 Disclaimer: this is not the perfect way 免责声明:这不是完美的方式

First and all update the UserViewModel to 首先,将UserViewModel更新为

public class UserViewModel
    {
        [Required()]
        [RegularExpression(@"^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$")]
        public String Email { get; set; }
    }

Then update the action method to 然后将动作方法更新为

        // Post action
        [HttpPost]
        public ActionResult register (UserViewModel uvm)
        {
            // This validates the UserViewModel
            if (ModelState.IsValid)
            {

                try
                {
                    // You should delegate this task to a service but to keep it simple we do it here
                    User u = new User() { Email = uvm.Email, Created = DateTime.Now };
                    RedirectToAction("Index"); // On success you go to other page right?
                }
                catch (Exception x)
                {
                    ModelState.AddModelError("RegistrationError", x); // Replace x with your error message
                }

            }       

            // Return your UserViewModel to the view if something happened               
            return View(uvm);
        }

Now for the user model it gets tricky and you have many possible solutions. 现在对于用户模型来说,它变得棘手,你有很多可能的解决方案。 The solution I came up with (probably not the best) is the following: 我想出的解决方案(可能不是最好的)如下:

public class User
    {
        private string email;
        private DateTime created;

        public string Email
        {
            get
            {
                return email;
            }
            set
            {
                email = ValidateEmail(value);
            }
        }

        private string ValidateEmail(string value)
        {
            if (!validEmail(value))
                throw new NotSupportedException("Not a valid email address");     

            return value;
        }

        private bool validEmail(string value)
        {
            return Regex.IsMatch(value, @"^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$");
        }

Last some unit test to check my own code: 最后一些单元测试来检查我自己的代码:

   [TestClass()]
    public class UserTest
    {

        /// <summary>
        /// If the email is valid it is stored in the private container
        /// </summary>
        [TestMethod()]
        public void UserEmailGetsValidated()
        {
            User x = new User();
            x.Email = "test@test.com";
            Assert.AreEqual("test@test.com", x.Email);
        }

        /// <summary>
        /// If the email is invalid it is not stored and an error is thrown in this application
        /// </summary>
        [TestMethod()]
        [ExpectedException(typeof(NotSupportedException))]
        public void UserEmailPropertyThrowsErrorWhenInvalidEmail()    
       {
           User x = new User();
           x.Email = "blah blah blah";
           Assert.AreNotEqual("blah blah blah", x.Email);
       }


        /// <summary>
        /// Clears an assumption that on object creation the email is validated when its set
        /// </summary>
        [TestMethod()]
        public void UserGetsValidatedOnConstructionOfObject()
        {
            User x = new User() { Email = "test@test.com" };
            x.Email = "test@test.com";
            Assert.AreEqual("test@test.com", x.Email);
        }
    }

I think is better to use Data Annotations look at this 我认为最好使用Data Annotations看看这个

ASP.NET MVC Valdation ASP.NET MVC Valdation

and for server side validation you can use the fluent validation 对于服务器端验证,您可以使用流畅的验证

and look at this question 并看看这个问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM