【问题标题】:Validation best practice for Model and ViewModelModel 和 ViewModel 的验证最佳实践
【发布时间】:2011-09-22 02:37:37
【问题描述】:

我有单独的模型和视图模型类。 viewmodel 类只做 UI 级别的验证(参考:Validation: Model or ViewModel)。

我可以在控制器中验证模型 (vewmodel) 是否有效。

问: 如何验证模型(带有数据注释的主要实体)。

我没有使用模型对象开发视图模型。只需复制属性并添加该特定视图中可能需要的所有属性。

//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();
}

应该这样做:

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

我的想法是在(业务实体的)基类中添加这种验证技术,并在我从视图模型类映射到业务实体时验证它。

有什么建议吗?

【问题讨论】:

    标签: c# asp.net-mvc-2 model viewmodel asp.net-mvc-2-validation


    【解决方案1】:

    1) 在从用户那里检索信息的模型上使用流利的验证。它比数据注释更灵活,更容易测试。

    2) 你可能想研究一下 automapper,通过使用 automapper 你不必写x.name = y.name

    3) 对于您的数据库模型,我会坚持使用数据注释。

    以下所有内容均基于新信息

    首先,您应该像现在对实际模型验证所做的那样在这两个位置进行验证,这就是我的做法。 免责声明:这不是完美的方式

    首先将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; }
        }
    

    然后更新action方法为

            // 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);
            }
    

    现在对于用户模型,它变得很棘手,您有许多可能的解决方案。我想出的解决方案(可能不是最好的)如下:

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

    最后一些单元测试来检查我自己的代码:

       [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);
            }
        }
    

    【讨论】:

    • prd @Serghei 我实际上想知道如何验证模型类(未绑定到视图)。保持我的视图具有来自不同模型类(在 ViewModel 类中)的属性,以满足对该特定视图的所有要求。
    • @Yahya 你能举个例子吗?更容易指出您应该在哪里以及如何进行验证。
    • prd 我在原始问题中为您添加了示例代码。我希望它现在有意义。
    • @Yahya 我用可能的解决方案更新了我的帖子,但它不是最好的
    • prd 感谢您的解决方案。不幸的是,这看起来不是很整洁。目前,由于我的业务实体不是关键任务,我只是生活,没有对其进行验证。对视图模型进行所有验证。对于映射,我听取了您的建议并正在使用 Automapper。
    【解决方案2】:

    我认为最好使用数据注释 看看这个

    ASP.NET MVC Valdation

    对于服务器端验证,您可以使用fluent validation

    看看这个question

    【讨论】:

      猜你喜欢
      • 2023-03-12
      • 2013-05-09
      • 1970-01-01
      • 2010-10-14
      • 2015-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多