65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET MVC 中模型数据验证和无侵入式客户端验证的初学者教程

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (18投票s)

2013 年 4 月 15 日

CPOL

6分钟阅读

viewsIcon

161499

downloadIcon

3785

在本文中,我们将尝试了解如何在 ASP.NET MVC 应用程序中使用 DataAnnotations 对模型类执行验证。

引言

在本文中,我们将尝试了解如何在 ASP.NET MVC 应用程序中使用 DataAnnotations 对模型类执行验证。我们将看到如何使用简单的脚本包含,利用相同的 DataAnnotations 属性逻辑提供无侵入式客户端验证。

背景

每当我们创建数据库驱动应用程序时,用户输入验证都是一个非常重要的方面。我们需要验证所有作为用户输入的数据,以确保输入的数据有效,并符合数据库中相应字段的数据类型和长度值。

在 MVC 应用程序中,业务实体表示为 Model 类。现在,如果我们需要在将数据推送到数据库之前验证这些模型类的值,我们有两个选择。第一个选择是编写所有客户端脚本来验证各个字段,并在将数据放入模型之前编写服务器端代码进行验证。如果强类型视图是强类型,这个服务器端有点问题。单独验证所有字段的数据不是一个好主意。

第二个选择更优雅,并且受到 MVC 框架的大力支持。这个选择是使用 DataAnnotation 属性装饰各个属性。现在,使用这些属性,我们可以为模型类的各个字段指定各种验证规则。这样,我们就不必自己编写任何额外的验证代码。指定这些 DataAnnotation 属性将同时处理服务器端和客户端验证。

DataAnnotation 属性依赖于这样一个事实:每当我们尝试添加新记录或更新现有记录时,我们都会检查 ModelState.IsValid 属性。这将使用模型属性中指定的值并检查验证不一致。如果数据无效,则不会采取任何操作,用户将看到数据中的问题。

现在让我们看看如何将简单的 DataAnnotation 属性与 Model 类一起使用,它将处理客户端和服务器端验证。

使用代码

数据库和验证规则

让我们创建一个单表数据库。我们将创建一个名为 Contacts 的单表,其中包含联系人的信息。


现在在这个表中,除 ID 外的所有字段都不能为 null,即它们是用户必需的。此外,我们对各个字段有以下长度限制。

  • FirstName: varchar(50)
  • LastName: varchar(50)
  • Address: varchar(100)
  • PhoneNumber: varchar(15)
  • eMail: varchar(35)

因此,此表的另一个验证规则应该是来自用户的数据不应超过各个字段的最大长度。另一个验证规则是电话号码应仅为数字,并且不应包含字母。电子邮件 ID 应采用正确的格式。

因此,让我们尝试总结此表所需的验证规则。

  1. 所有字段都是必需的。
  2. 用户输入的长度不应超过相应字段的长度。
  3. PhoneNumber 应仅包含数字。
  4. eMail 应采用正确的电子邮件格式。

现在让我们将这些规则作为我们应用程序的验证要求,并开始处理它们。

数据访问

现在要执行数据访问,我们可以使用从经典的 ADO.NET 到 ORM(如实体框架)的任何东西,前提是我们以 Model 类的形式对数据进行建模。让我们使用实体框架,以便为我们生成所有用于数据访问和数据实体的样板代码。

一旦我们为这个数据库添加了 ADO.NET 实体数据模型,我们将创建以下实体作为我们应用程序中的模型类。


创建 MVC 应用程序

现在让我们添加一个简单的控制器,它将在此表/实体上提供 CRUD 操作。让我们使用脚手架生成控制器和视图,以便我们可以专注于数据验证,而不是创建控制器和视图。


控制器代码现在看起来像:

public class ContactController : Controller
{
    private SampleDbEntities db = new SampleDbEntities();

    public ViewResult Index()
    {
        return View(db.Contacts.ToList());
    }

    public ViewResult Details(int id)
    {
        Contact contact = db.Contacts.Single(c => c.ID == id);
        return View(contact);
    }

    public ActionResult Create()
    {
        return View();
    } 

    [HttpPost]
    public ActionResult Create(Contact contact)
    {
        if (ModelState.IsValid)
        {
            db.Contacts.AddObject(contact);
            db.SaveChanges();
            return RedirectToAction("Index");  
        }

        return View(contact);
    }
     
    public ActionResult Edit(int id)
    {
        Contact contact = db.Contacts.Single(c => c.ID == id);
        return View(contact);
    }

    [HttpPost]
    public ActionResult Edit(Contact contact)
    {
        if (ModelState.IsValid)
        {
            db.Contacts.Attach(contact);
            db.ObjectStateManager.ChangeObjectState(contact, EntityState.Modified);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(contact);
    }

    public ActionResult Delete(int id)
    {
        Contact contact = db.Contacts.Single(c => c.ID == id);
        return View(contact);
    }

    [HttpPost, ActionName("Delete")]
    public ActionResult DeleteConfirmed(int id)
    {            
        Contact contact = db.Contacts.Single(c => c.ID == id);
        db.Contacts.DeleteObject(contact);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }
}

执行 CRUD 操作的视图看起来像


默认行为

现在当我们运行应用程序时,我们已经有了基本的 CRUD 操作。现在,如果我们尝试创建一个新条目并输入一些无效数据,数据将被提交,因为没有验证。嗯,不完全是这样,实体框架足够智能,可以检测到必填字段并对其进行验证。因此,如果我们尝试在没有任何值的情况下提交,我们将收到一些错误消息。但这些将是实体框架生成的默认消息。

现在对于长度,如果我们尝试输入长度超过限制的数据,将会抛出异常。现在,如果我们可以验证传入数据并检查长度,我们可以简单地避免此异常。

最后,对于电话号码和电子邮件格式,它们无法进行验证。只要它们的长度有效,它们就会被简单地推入数据库。

DataAnnotations

现在让我们不依赖默认行为,而是自己控制验证行为。我们将通过在我们的 Model 类中添加 DataAnnotation 属性来实现这一点。由于我们的模型类是自动生成的,我们将创建一个同名的部分类来添加数据注释。

[MetadataType(typeof(ContactMetaData))]
public partial class Contact
{
}

现在这个类被 MetadataType 属性修饰,该属性存在于 DataAnnotations 命名空间中。这表明此模型类的元数据将存在于 ContactMetaData 类中。因此,ContactMetaData 类将是放置所有验证逻辑的地方。现在,这个元数据类应包含与它关联的 Model 类相同的公共属性。

现在让我们逐一讨论所有要求,并尝试为模型类的各个属性添加相应的属性。

所有字段都是必需的

现在可以通过为属性添加 Required 属性来实现这一点,如下所示

class ContactMetaData
{
    [Required(ErrorMessage="First Name is required")]
    public string FirstName { get; set; }

    [Required(ErrorMessage = "Last Name is required")]
    public string LastName { get; set; }

    [Required(ErrorMessage = "Address is required")]
    public string Address { get; set; }

    [Required(ErrorMessage = "Phone Number is required")]
    public string PhoneNumber { get; set; }

    [Required(ErrorMessage = "eMail is required")]
    public string eMail { get; set; }
}

用户输入的长度不应超过相应字段的长度

现在可以通过为所有属性指定 StringLength 属性来完成此操作。

class ContactMetaData
{
    [Required(ErrorMessage="First Name is required")]
    [StringLength(15, ErrorMessage = "First Name length Should be less than 50")]
    public string FirstName { get; set; }

    [Required(ErrorMessage = "Last Name is required")]
    [StringLength(50, ErrorMessage = "Last Name length Should be less than 50")]
    public string LastName { get; set; }

    [Required(ErrorMessage = "Address is required")]
    [StringLength(100, ErrorMessage = "Address length Should be less than 100")]
    public string Address { get; set; }

    [Required(ErrorMessage = "Phone Number is required")]
    [StringLength(15, ErrorMessage = "Phone Number length Should be less than 15")]    
    public string PhoneNumber { get; set; }

    [Required(ErrorMessage = "eMail is required")]
    [StringLength(35, ErrorMessage = "eMail Length Should be less than 35")]    
    public string eMail { get; set; }
}

电话号码和电子邮件格式

现在下一个验证是针对电话号码和电子邮件格式的。为此,让我们尝试使用正则表达式。所有输入都将根据为相应属性指定的 RegularExpression 属性进行验证。

[RegularExpression(@"^[0-9]{0,15}$", ErrorMessage = "PhoneNumber should contain only numbers")]
public string PhoneNumber { get; set; }

[RegularExpression(@"^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$", ErrorMessage = "eMail is not in proper format")]
public string eMail { get; set; }

注意:我们尚未看到 Range 属性的使用。它用于指定范围,其使用非常简单。

客户端无侵入式验证

现在这些属性将处理服务器端验证。要从客户端使用相同的验证,我们需要包含几个客户端脚本,相同的验证规则也将从客户端工作(向用户提供即时反馈)。

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

测试应用程序

现在我们已经有了实体的验证规则。现在让我们尝试看看它的实际效果。让我们尝试在不输入任何值的情况下提交表单。


现在让我们尝试在某些字段中输入长数据,并以错误的格式输入号码和电子邮件 ID。


但是,如果所有数据都有效,则创建操作将成功,所有验证都通过。


因此,通过在 Model 的元数据类中添加 DataAnnotation 属性,我们为我们的模型类创建了客户端和服务器端验证。

值得关注的点:

在本文中,我们看到了如何使用 DataAnnotation 命名空间来修饰 Model 的元数据类并执行服务器端和客户端无侵入式验证。本文是从初学者的角度编写的。我希望这能提供有用的信息。

历史

  • 2013 年 4 月 15 日:第一版。
© . All rights reserved.