1. 程式人生 > >MVC中的數據驗證

MVC中的數據驗證

硬編碼 工作 clas 錯誤提示 con returns 什麽 ali 效果

一 概述

關於數據驗證和數據註解,是任何軟件系統不可小覷的必要模塊,在軟件系統中起到舉足輕重的作用,主要作用是為了保證數據安全性、防止漏洞註入和網絡攻擊。

從數據驗證的驗證方式來說,我們一般分為客戶端驗證和服務端驗證(或者兩種方式相結合),本篇文章主要講解基於ASP.NET MVC框架的數據驗證特性和數據註解。

二 數據驗證

(一)ASP.NET MVC 內置六大類數據驗證特性

  在ASP.NET MVC中,驗證特性定義在 System.ComponentModel.DataAnnotations 命名空間中,我們在使用驗證特性前,首先需要引入命名空間

  ASP.NET MVC內置了六大驗證特性:Required, StringLength, RegularExpression, Range, Compare和 Remote

;

     //定義用戶名必填
        [Required]
        public string UserName { get; set; }
        //定義密碼是必填,且滿足6-20位
        [Required]
        [StringLength(20, MinimumLength = 6, ErrorMessage = "密碼必須是6-20位")]
        public string UserPass { get; set; }

(二)一個簡單的例子

Models:UserInfo.cs

    public class UserInfo
    {
        
//定義用戶名必填 [Required] public string UserName { get; set; } //定義密碼是必填,且滿足6-20位 [Required] [StringLength(20, MinimumLength = 6, ErrorMessage = "密碼必須是6-20位")] public string UserPass { get; set; } [Required] [Compare("UserPass", ErrorMessage = "兩次密碼不一致
")] public string ConfirmUserPass { get; set; } //郵箱必填且滿足,郵箱格式 [Required] [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage = "郵箱格式不正確!")] public string Email { get; set; } //年齡必填,且滿足1~120 [Required] [Range(1, 120, ErrorMessage = "年齡為1到120歲之間")] public int Age { get; set; } }

Controller:DefaultController:

技術分享圖片
     public class UserInfoController : Controller
    {
        //暫時不用Ioc解耦,直接new一個服務實例
        UserInfoService userService = new UserInfoService();
        // GET: UserInfo
        public ActionResult Index()
        {
            //GetUserList:獲取用戶列表的
            List<UserInfo> userList = userService.GetUserList();
            return View(userList);
        }
        /// <summary>
        /// 添加用戶
        /// </summary>
        /// <returns></returns>
        public ActionResult AddUserInfo()
        {
            return View();
        }
        [HttpPost]
        public ActionResult AddUserInfo(UserInfo user)
        {
            //使用ModelState.IsValid判斷字段格式是否正確
            if (ModelState.IsValid)
            {
                //TryAddUserInfo:添加用戶的服務方法,返回值為bool
                if (userService.TryAddUserInfo(user))
                {
                    return Json(new { IsSucess = 1, Message = "添加成功!" });
                    //return View("Index");前後端不分離時,直接跳轉到列表頁
                }
                else
                {
                    return Json(new { IsSucess = 0, Message = "添加失敗" });
                }
            }
            else
            {
                //字段不符合格式
                string error = "";
                ViewBag.ModelState = ModelState;
                foreach (var item in ModelState)
                {
                    if (item.Value.Errors.Count > 0)
                    {
                        error += item.Value.Errors.FirstOrDefault().ErrorMessage;
                    }
                }
                return Json(new { IsSucess=0,Message=error});
                //return View("AddUserInfo");//一般前後端不分離的項目中這樣使用最簡單
            }
        }
    }
Controller
 1      public class UserInfoController : Controller
 2     {
 3         //暫時不用Ioc解耦,直接new一個服務實例
 4         UserInfoService userService = new UserInfoService();
 5         // GET: UserInfo
 6         public ActionResult Index()
 7         {
 8             //GetUserList:獲取用戶列表的
 9             List<UserInfo> userList = userService.GetUserList();
10             return View(userList);
11         }
12         /// <summary>
13         /// 添加用戶
14         /// </summary>
15         /// <returns></returns>
16         public ActionResult AddUserInfo()
17         {
18             return View();
19         }
20         [HttpPost]
21         public ActionResult AddUserInfo(UserInfo user)
22         {
23             //使用ModelState.IsValid判斷字段格式是否正確
24             if (ModelState.IsValid)
25             {
26                 //TryAddUserInfo:添加用戶的服務方法,返回值為bool
27                 if (userService.TryAddUserInfo(user))
28                 {
29                     return Json(new { IsSucess = 1, Message = "添加成功!" });
30                     //return View("Index");前後端不分離時,直接跳轉到列表頁
31                 }
32                 else
33                 {
34                     return Json(new { IsSucess = 0, Message = "添加失敗" });
35                 }
36             }
37             else
38             {
39                 //字段不符合格式
40                 string error = "";
41                 ViewBag.ModelState = ModelState;
42                 foreach (var item in ModelState)
43                 {
44                     if (item.Value.Errors.Count > 0)
45                     {
46                         error += item.Value.Errors.FirstOrDefault().ErrorMessage;
47                     }
48                 }
49                 return Json(new { IsSucess=0,Message=error});
50                 //return View("AddUserInfo");//一般前後端不分離的項目中這樣使用最簡單
51             }
52         }
53     }

View:Index.cshtml

技術分享圖片
    @model Model.UserInfo
@{
    ViewBag.Title = "AddUserInfo";
}
<h2>添加用戶</h2>
@using (Html.BeginForm("AddUserInfo", "UserInfo",FormMethod.Post))
{
    <div>
        @Html.Label("用戶名"): @Html.TextBoxFor(m => m.UserName)
        @Html.ValidationMessageFor(m => m.UserName)
    </div>
    <div>
        @Html.Label("密碼"):@Html.TextBox("UserPass")
        @Html.ValidationMessageFor(m => m.UserPass)
    </div>

    <div>
        @Html.Label("確認密碼"):@Html.TextBox("ConfirmUserPass")
        @Html.ValidationMessageFor(m => m.ConfirmUserPass)
    </div>
    <div>
        @Html.Label("郵件"):@Html.TextBox("Email")
        @Html.ValidationMessageFor(m => m.Email)
    </div>
    <div>
        @Html.Label("年齡"):@Html.TextBox("Age")
        @Html.ValidationMessageFor(m => m.Age)
    </div>
    <div><input type="submit" value="提交" /></div>
}
View
 1     @model Model.UserInfo
 2   @{
 3       ViewBag.Title = "AddUserInfo";
 4   }
 5   <h2>添加用戶</h2>
 6 @using (Html.BeginForm("AddUserInfo", "UserInfo",FormMethod.Post))
 7 {
 8     <div>
 9         @Html.Label("用戶名"): @Html.TextBoxFor(m => m.UserName)
10         @Html.ValidationMessageFor(m => m.UserName)
11     </div>
12     <div>
13         @Html.Label("密碼"):@Html.TextBox("UserPass")
14         @Html.ValidationMessageFor(m => m.UserPass)
15     </div>
16 
17     <div>
18         @Html.Label("確認密碼"):@Html.TextBox("ConfirmUserPass")
19         @Html.ValidationMessageFor(m => m.ConfirmUserPass)
20     </div>
21     <div>
22         @Html.Label("郵件"):@Html.TextBox("Email")
23         @Html.ValidationMessageFor(m => m.Email)
24     </div>
25     <div>
26         @Html.Label("年齡"):@Html.TextBox("Age")
27         @Html.ValidationMessageFor(m => m.Age)
28     </div>
29     <div><input type="submit" value="提交" /></div>
30 }

測試效果如下:

技術分享圖片

6.為什麽要把Remote剔出來單獨講解呢?

我們知道,除Remote以外的五大驗證特性,命名空間均為System.ComponentModel.DataAnnotations,而Remote特性的命名空間卻是System.Web.Mvc。

Remote,從字面意思可以看出,“遠程”,即遠程驗證。Remote特性指利用服務器端的回調函數執行客戶端的驗證邏輯(當執行到有Remote特性的元數據時,會自動地調用相應的控制器下的Action)。

舉個例子:新會員註冊時,一般手機號是不允許重復的,檢查DB中是否已存在手機號,可以使用Remote特性來驗證。

技術分享圖片

Model:UserInfo.cs

技術分享圖片 View Code

DefaultController

技術分享圖片 View Code

Index.cshtml

技術分享圖片 View Code

測試結果:

技術分享圖片

技術分享圖片 給大家留一個思考題:如何驗證多個參數?

在實際項目開發中,一般我們驗證的不僅僅是一個參數,而是多個參數,如用戶名和手機號,身份證號等一起驗證,關於多參數驗證,Remote驗證特性又是怎麽處理的呢?

(二) 驗證錯誤提示

技術分享圖片

1.什麽是驗證錯誤提示?

指驗證字段在驗證不通過時,反饋給用戶的提示信息,如密碼不能低於6位,手機號必須為11位,年齡限制在1-130歲之間等,通過驗證特性的ErroMessage實現。

[Required]
[StringLength(128,MinimumLength =6,ErrorMessage ="密碼不能低於6位數")]

2.錯誤驗證提示大致分為兩大類:默認錯誤提示和自定義錯誤提示。

(1)默認錯誤提示:當我們不指定ErroMessage的值時,ASP.NET MVC框架會指定默認值。

//定義密碼必填,且滿足6位
[Required]
[StringLength(128,MinimumLength =6)]
 public string Password { get; set; }

Result:

技術分享圖片

(2)自定義值:我們為ErrorMessage指定具體自定義的值“密碼不能低於6位數”

[Required]
[StringLength(128,MinimumLength =6,ErrorMessage ="密碼不能低於6位數")]
 public string Password { get; set; }

Result:

技術分享圖片

3.為什麽要有自定義錯誤提示?

(1)為用戶呈現友好提示,我們來看一下2中的默認值和自定義值;

默認值:字段Password必須是一個字符串,其最小長度為6,最大長度為128(這麽一句話,要是給不懂程序的用戶看了,肯定會瘋掉,

很簡單,對程序員來說,“字段”二字再基礎不過,可對用戶來說,他可能會問,字段是什麽東東?)

自定義值:密碼不能低於6位數(無論是程序員還是用戶,都能看得明白)

(2)提高通用性,比如對美國提供英語提示,對俄羅斯提供俄語提示等;

4.如何實現通用性國際化?

在如上的自定義驗證錯誤提示中,我們使用的是硬編碼的形式,然而,面向國際市場開發的,這種硬編碼錯誤消息提示是不實用的,因為我們要為不同地區顯示

不同內容,實現國際化,慶幸的是,所有驗證特性都允許為本地化的錯誤消息提示指定資源類型名稱和資源名稱,感興趣的讀者朋友,請參照How to:Set the

Cultrue and UI Cultrue for ASP.NET Page Globalization(sites:http://msdn.microsoft.com/en-us/library/bz9tc508.aspx)

技術分享圖片 思考題,如何實現錯誤消息通用性國際化?

(三) 驗證原理

關於數據驗證,我們思考這樣一個問題:驗證是什麽時候發生的?如何才能知道驗證失敗?

本節我們將來回答這個問題。

技術分享圖片

1.要想充分理解驗證原理,我們應該先熟悉幾個基本概念:模型綁定器,模型元數據,模型驗證器和模型狀態(這部分內容,本篇文章不論述,大家知道這幾個概念即可,具體詳情內容,

將在接下來的文章中與大家分享:【ASP.NET MVC系列】淺談ASP.NET MVC 模型)

2.默認情況下,ASP.NET MVC框架在模型綁定時就執行驗證邏輯,在執行驗證時,分為隱式執行和顯示執行。

(1)隱式執行:一般指在控制器的Action中帶有參數時,就會隱式執行模型驗證。如下方法帶有參數,因此就隱式執行模型綁定。

1 public ActionResult DataValidateDemo(UserInfo userInfo)
2         {
3             UserInfo _userInfo = new UserInfo();
4             _userInfo.UserName = userInfo.UserName;
5             return View("Index");
6         }

(2)顯示執行:只利用控制器的UpdateModel或TryUpdateModel方式時,顯示執行模型綁定。

3.模型綁定器一旦使用新值更新模型屬性時,就會利用當前的模型元數據獲得模型的所有驗證器;

4.ASP.NET MVC運行時,DataAnnotationsModelValidator與數據驗證一起工作;

5.DataAnnotationsModelValidator驗證器會找到所有的驗證特性並執行它所包含的驗證邏輯;

6.模型綁定器捕獲所有失敗的驗證規則,並把他們放入模型狀態中;

7.模型綁定主要的副產品是模型狀態,模型狀態包含如下內容:

(1)包含用戶放入模型屬性中的所有值;

(2)包含每個屬性相關聯的所有錯誤;

(3)包含所有與模型對象本身有關的錯誤;

8.如果模型狀態中存在錯誤,ModelState.IsValid就返回false;

9.控制操作和驗證錯誤是怎樣執行的?

技術分享圖片

控制器操作決定模型驗證失敗和驗證成功時的執行流程。

(1)驗證成功時:當驗證成時,操作通常會執行必要的步驟來保存或更新用戶信息;

(2)驗證失敗時:當驗證失敗時,操作一般會重新渲染提交模型值得視圖;

(四)自定義驗證

ASP.NET MVC之所以強大,在於其提供強大的自定義和擴展性,關於這個內容,會在後續的文章:“【SP.NET MVC系列】淺談ASP.NET MVC八大類擴展”中深入講解這兩個強大的特性。

技術分享圖片

1.基於ASP.NET MVC的自定義驗證,一般分為兩大類型:將驗證邏輯封裝在自定義數據中和將驗證邏輯封裝在模型對象中。

(1)將驗證邏輯封裝在自定義數據中:復雜,但可復用性高;

(2)將驗證邏輯封裝在模型對象中:簡單,但可復用性低;

2.將驗證邏輯封裝在自定數據中(會在後續的文章:“【ASP.NET MVC系列】淺談ASP.NET MVC八大類擴展”中深入講解)

3.將驗證邏輯封裝模型對象中(會在後續的文章:“【ASP.NET MVC系列】淺談ASP.NET MVC八大類擴展”中深入講解)

三 數據註解

(一)七大類型ASP.NET MVC內置數據註解

技術分享圖片

1.Dispaly特性:(1)模型屬性設置友好的顯示名稱 (2)控制UI上屬性的顯示順序;

2.ScaffoldColumn特性:隱藏HTML輔助方法;

3.DisplayFormat特性:處理屬性的各種格式化選項;

4.ReadOnly特性:確保默認的模型綁定器不使用新值來更新;

5.DataType特性:提供關於屬性的特定信息;

6.UIHint特性:(1)為ASP.NET MVC運行時提供模板名稱,以備調用模板輔助方法渲染輸出時使用 (2)自定義模板輔助方法;

7.HiddenInput特性:渲染type為hidden的元素;

四 參考文獻

【01】ASP.NET MVC5 高級編程(Jon Galloway,Brad Wilson,K.Scott Allen,David Matson 著 ,孫遠帥 譯)

【02】ASP.NET MVC5編程實戰(第3版)(Dino Esposite 著,潘麗丞 譯)

MVC中的數據驗證