ASP.NET MVC 数据验证

本篇文章跟大家分享关于ASP.NET MVC的数据验证,主要内容如下:
1. 了解数据验证
2. 显式地添加验证
3. 显示验证结果
4. 使用元数据进行验证
5. 执行客户端验证
6. 避免对ORM对象的标注在重新编译时被覆盖掉

了解数据验证

在使用ASP.NET MVC框架进行开发时,什么时候会进行数据验证呢?首先在用户提交表单钱,需要对用户的输入进行验证,这个过程一般由程序员自己实现。然后,如果这一步验证成功了,用户提交表单,路由系统处理URL请求,然后找到特定的Controller的Action来响应该请求,在调用该Action前,会先将用户提交的所有参数提取出来,作为参数传递给Action,提取并传递的过程称为模型绑定(Model Binding),而MVC框架在执行模型绑定的过程中,难免会进行一些强制类型转换,这也会对输入参数进行验证,当然,这个验证过程是由MVC框架自动完成的。

验证包含两个部分,第一部分是校验用户的输入是否合法有效,第二部分是,如果输入不合法则提示用户进行修改。ASP.NET MVC框架对验证有着非常好的支持。

在使用MVC框架以前,我们最常用的验证方式是JQuery来进行简单的验证,如某个文本框不能为空、必须是数字等,也可以通过Ajax技术来访问数据库以验证某个用户名是否存在。

显式地添加验证

MVC框架提供的对一种方式是通过使用ModelState类来手工地进行验证。在用户将数据POST到后台以后,程序员需要对用户的输入进行判断,如果输入无效,则调用ModelState.AddModelError(name,errorMessage)方法将错误信息记录下来,并返回到页面上,代码如下:
View部分:

@model MvcApp.Models.Appointment
@{
     ViewBag.Title = "Make A Booking";
}
<h4>Book an Appointment</h4>
@using (Html.BeginForm()) {
<p>Your name: @Html.EditorFor(m => m.ClientName)</p>
<p>Appointment Date: @Html.EditorFor(m => m.Date)</p>
<p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms & conditions</p>
<input type="submit" value="Make Booking" />
}

Controller部分:

[HttpPost]
public ViewResult MakeBooking(Appointment appt) {
     if (string.IsNullOrEmpty(appt.ClientName)) {
          ModelState.AddModelError("ClientName", "Please enter your name");
     }
     if (ModelState.IsValidField("Date") && DateTime.Now > appt.Date) {
          ModelState.AddModelError("Date", "Please enter a date in the future");
     }
     if (!appt.TermsAccepted) {
          ModelState.AddModelError("TermsAccepted", 
          "You must accept the terms");
     }
     if (ModelState.IsValid) {
          repository.SaveAppointment(appt);
          return View("Completed", appt);
     } else {
          return View();
     }
}

当用户的输入不合法时,程序会直接return View(),由于View中的文本框是通过@Html.EditorFor方法生成的,所以,该文本框在出错的情况下会显示红色的边框和粉色的背景色,但没有错误消息。

显示验证结果

如果想要显示错误消息,需要在页面上调用@Html.ValidationMessageFor(m => m.ClientName),这个方法是显示某个字段的错误信息的。如果希望显示整个对象的错误信息汇总,则调用@Html.ValidationSummary(),它会把该对象的所有属性的错误消息汇总到一个列表中进行显示。
@Html.ValidationSummary()方法有几个重载的版本,没有参数的版本就默认显示出所有的错误信息,Html.ValidationSummary(bool)这个版本,如果这个参数为True的话,就只显示对象级别的错误信息(下面会说明什么是对象级别的错误信息)。Html.ValidationSummary(string)这个版本,会把这个字符串参数显示在所有错误信息的前面。Html.ValidationSummary(bool,string)这个版本,就不做介绍了,是上面所有版本合体。

所谓对象级别的错误信息,是相对于属性级别的错误信息而言的。ModelState.AddModelError(name,errorMessage)这个方法,如果name参数不为空,则这个错误信息就是属于name所代表的属性的,属于属性级别的错误信息,而如果name属性为空,则这个错误信息是属于当前对象的,就是对象级别的错误信息。

使用元数据进行验证

通过ModelState类来手工地进行验证,验证的逻辑是写在特定的控制器的方法中,没有有效的进行关注点的分离,并且不能很好的进行代码重用,所以,MVC框架还提供了更加整洁的用法,就是DataAnnotation的方式。如:

public class Appointment {
     [Required]
     public string ClientName { get; set; }
     [DataType(DataType.Date)]
     [Required(ErrorMessage="Please enter a date")]
     public DateTime Date { get; set; }
     [Range(typeof(bool), "true", "true", ErrorMessage="You must accept the terms")]
     public bool TermsAccepted { get; set; }
}

这个数据标注的功能是相当强大,它不仅可以自动完成验证的过程,并且还同时实现了客户端和服务器端的验证。当用户提交数据时,首先会在客户端进行校验,有错误信息时会直接显示出来,并且不会把无效数据提交给服务器。如果客户端验证通过了,(或者用户禁用了JS,)则会提交数据到服务器。这个请求首先由路由系统处理。当这个请求被某个路由匹配以后,ModelBinder会对参数进行处理,这个过程中会调用所有的验证类,如果有错误就会在ModelState中记录下来。如果ModelState.IsValid为true的话,参数才会被传递到控制器的方法中,否则会把错误信息返回到页面上进行显示,显示的方法上文已经介绍过了。也就是说,标注一个属性,这个验证规则会在浏览器段和服务器端都进行校验,并且这个过程是自动完成的。

执行客户端验证

ASP.NET MVC框架自动开启了客户端的验证,在配置文件中

<configuration>
    <appSettings>
        <add key="ClientValidationEnabled" value="true"/>
        <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
    </appSettings>
......

那么,客户端的验证是怎样自动实现的呢?
如果开启了客户端,调用Html.EditorFor时,会生成类似下面的HTML代码

<input class="text-box single-line" data-val="true" 
    data-val-length="The field ClientName must be a string with a minimum length of 3 and a maximum length of 10." 
    data-val-lengthmax="10" data-val-length-min="3" 
    data-val-required="The ClientName field is required." 
    id="ClientName" name="ClientName" type="text" value="" 
/>

这些特殊的属性是约定好的用来验证的属性,MVC项目中的JQuery代码已经对这些特定的属性进行了处理,甚至,我们可以手动的给HTML元素添加这些约定好的属性,有兴趣的可以自行研究。

但是遗憾的是,客户端自动生成的HTML属性只支持系统定义好的这些验证类,下面是几个常用的系统定义的验证类的介绍:

Required用来指明某个属性是必填的,StringLength规定了某个字段的最大长度,Range来指明某个属性的值的范围,RegularExpression规定了某个属性的值必须符合特定的正则表达式。

这些个验证类能够完成一些常见的验证,但是并不能满足所有要求,幸运的是,MVC框架可以很轻松的自定义一个验证规则,只需要实现ValidationAttribute接口,例如:

public class MustBeTrueAttribute : ValidationAttribute {
     public override bool IsValid(object value) {
     return value is bool && (bool)value;
     }
}

这样使用

[MustBeTrue(ErrorMessage="You must accept the terms")]
public bool TermsAccepted { get; set; }

MustBeTrueAttribute是一个验证属性的类,当然也可以自定义一个用来验证对象的类,验证对象的类可以对这个对象的多个属性进行综合验证,实现的方法与验证属性的类非常类似,区别在于,IsValid方法的参数是一个对象,而不是一个属性的值,并且,这个验证类需要标注在一个实体类的上方,而不是属性的上方。如:

[XXXValidation]
public class TestClass
{

}

自定义的验证类也能够实现在客户端的验证,但需要自己来编写一些JS代码,过程有些许的曲折,这里就不做介绍了。

避免对ORM对象的标注在重新编译时被覆盖掉

还有一种情况需要介绍。如果实体类是通过类似EntityFramework等ORM框架自动生成的,那么,直接在实体类上进行标注的话,重新编译后,标注的代码就会消失。解决方法如下:
这个是自动生成的类

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated from a template.
//
//     Manual changes to this file may cause unexpected behavior in your application.
//     Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace ValidationDemo
{
    using System;
    using System.Collections.Generic;

    public partial class Employee
    {
        public Employee()
        {
            this.Employees1 = new HashSet<Employee>();
            this.Orders = new HashSet<Order>();
            this.Territories = new HashSet<Territory>();
        }

        public int EmployeeID { get; set; }
        public string LastName { get; set; }
        public string FirstName { get; set; }
        public string Title { get; set; }
     }
}

我们需要编写一个元数据类,然后再将这个元数据类标记为Employee部分类的元数据,代码如下:

namespace ValidationDemo
{
    public partial class EmployeeMetaData
    {
        [Required]
        public string LastName { get; set; }
          [Required]
          [StringLength(20)]
        public string FirstName { get; set; }
    }

    [MetadataType(typeof(EmployeeMetaData))]
    public partial class Employee
    {
    }
}

这样就解决了元数据被覆盖的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值