位於 System.ComponentModel.DataAnnotations 命名空間中的特性指定對數據模型中的各個字段的驗證。這些特性用於定義常見的驗證模式,例如範圍檢查和必填字段。而 DataAnnotations 特性使 MVC 可以提供客戶端和服務器驗證檢查,使你無需進行額外的編碼來控制數據的有效。javascript
經過爲模型類增長數據描述的 DataAnnotations ,咱們能夠容易地爲應用程序增長驗證的功能。DataAnnotations 容許咱們描述但願應用在模型屬性上的驗證規則,ASP.NET MVC 將會使用這些 DataAnnotations ,而後將適當的驗證信息返回給用戶。html
在DataAnnotations爲咱們所提供的衆多內置驗證特性中,用的最多的其中的四個是:java
(0)[DisplayName]:顯示名 – 定義表單字段的提示名稱jquery
(1)[Required] :必須 – 表示這個屬性是必須提供內容的字段ajax
(2)[StringLength]:字符串長度 – 定義字符串類型的屬性的最大長度正則表達式
(3)[Range]:範圍 – 爲數字類型的屬性提供最大值和最小值編程
(4)[RegularExpression]:正則表達式 – 指定動態數據中的數據字段值必須與指定的正則表達式匹配瀏覽器
假設咱們的Model中有一個UserInfo的實體,其定義以下:安全
1
2
3
4
5
6
|
public class UserInfo
{
public int Id { get; set; }
public string UserName { get; set; }
public int Age { get; set; }
}
|
UserInfo的屬性很簡單,只有三個:Id,UserName和Age三個字段;如今咱們能夠爲其增長驗證特性,看看其爲咱們提供的強大的校驗功能。服務器
(1)非空驗證
添加特性:
1
2
3
4
5
6
7
|
[Display(Name="用戶名")]
[Required(ErrorMessage = "*姓名必填")]
public string UserName { get; set; }
[Display(Name = "年齡")]
[Required(ErrorMessage = "*年齡必填")]
public int Age { get; set; }
|
驗證效果:
(2)字符串長度驗證
添加特性:
1
2
3
4
|
[Display(Name="用戶名")]
[Required(ErrorMessage = "*姓名必填")]
[StringLength(5, ErrorMessage = "*長度必須小於5")]
public string UserName { get; set; }
|
驗證效果:
(3)範圍驗證
添加特性:
1
2
3
4
|
[Display(Name = "年齡")]
[Required(ErrorMessage = "*年齡必填")]
[Range(18, 120)]
public int Age { get; set; }
|
驗證效果:
(4)正則表達式驗證
添加特性:驗證用戶輸入的是不是數字,正則表達式匹配
1
2
3
4
5
|
[Display(Name = "年齡")]
[Required(ErrorMessage = "*年齡必填")]
[Range(18, 120)]
[RegularExpression(@"^\d+$", ErrorMessage = "*請輸入合法數字")]
public int Age { get; set; }
|
驗證效果:
(5)瀏覽所生成的HTML代碼
從上圖能夠看出,咱們在瀏覽器端的校驗都是經過爲html標籤設置自定義屬性來實現的,咱們在Model中爲其添加的各類校驗特性,都會在客戶端生成一個特定的屬性,例如:data-val-length-max=「5」與data-val-length=」*長度必須小於5″對應[StringLength(5, ErrorMessage = "*長度必須小於5")]。而後,經過jquery validate在客戶端每次提交以前進行校驗,若是校驗匹配中有不符合規則的,則將message顯示在一個特定的span標籤(class=」field-validation-valid」)內,並阻止這次表單提交操做。
(1)首先,要確保須要進行校驗的頁面中引入了指定的幾個js文件:
1
2
|
<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>
|
固然,jquery庫的js文件也是必須的,並且在上面這兩個js以前引入;
(2)在 Web.config 的appSettings中,已經默認支持了客戶端驗證(MVC3.0及更高版本中默認支持,MVC2.0則須要修改一下):
1
2
3
|
<!-- 是否啓用全局客戶端校驗 -->
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
|
PS:Unobtrusive Javascript有三層含義:
一是在HTML代碼中不會隨意的插入Javsscript代碼,只在標籤中加一些額外的屬性值,而後被引用的腳本文件識別和處理;
二是經過腳本文件所增長的功能是一種漸進式的加強,當客戶端不支持或禁用了Javsscript時網頁所提供的功能仍然可以實現,只是用戶體驗會下降;
三是可以兼容不一樣的瀏覽器。
(3)在Action中若是要對客戶端是否經過了校驗進行驗證,能夠經過如下代碼實現:
1
2
3
4
5
6
7
8
9
10
|
[HttpPost]
public ActionResult Add(UserInfo userInfo)
{
if (ModelState.IsValid)
{
// To do fun
}
return RedirectToAction("Index");
}
|
若是經過校驗,則ModelState的IsValid屬性(bool類型)會變爲true,反之則爲false。
首先,在ASP.Net MVC中使用此種方式跟普通的WebForm的開發方式是一致的,須要注意的是:Url地址不一樣->請求的是Controller下的Action,例如在WebForm中請求的url一般是/Ajax/UserHandler.ashx,而在MVC中請求的url一般爲:/User/GetAll。
例如,咱們在一個View中添加一個按鈕,用於使用AJAX獲取一個服務器端的時間:
1
2
|
<h1>JQuery Ajax方式</h1>
<input id="btnJQuery" type="button" value="獲取服務器時間" />
|
在Home控制器中增長一個用於返回時間的Action:
1
2
3
|
public ActionResult GetServerDate()
{return Content(DateTime.Now.ToString());
}
|
在View中增長一段JQuery代碼,爲btnJQuery按鈕綁定一個Click事件:
1
2
3
4
5
6
7
8
9
|
$(function () {
$("#btnJQuery").click(function () {
$.post("/Home/GetServerDate", {}, function (data) {
if (data != null) {
$("#spTime").html(data);
}
});
});
});
|
這裏經過JQuery AJAX發送一個異步的POST請求,獲取服務器時間結果,並將其顯示在span標籤內:
至此,一個使用JQuery Ajax的MVC頁面就完成了。可是,這僅是一個最簡單的AJAX示例,在實際開發中每每比較複雜一點。
須要注意的是:
(1)若是你在JQuery AJAX中使用的是get方式的提交,那麼在在使用Json返回JsonResult時注意要將第二個參數設置容許Get提交方式:return Json(「」,JsonRequestBehavior.AllowGet),不然你用get方式是無權執行要請求的Action方法的。
(2)在Ajax開發中要注意Ajax方法體內的參數設置正確,特別是參數名要和Action中的參數名保持一致;
(3)若是在Action中爲其設置了[HttpPost]或[HttpGet],那麼提交方式要跟Action打的標籤一致;
在ASP.Net MVC中除了可使用JQuery AJAX外,Microsoft爲咱們提供了另外一套實用且更簡單的AJAX方案,咱們姑且稱其爲:Microsoft AJAX方式。
(1)首先:
須要將微軟提供的js腳本引入到頁面中:其實就是jquery.unobtrusive-ajax.js
1
2
|
<script src="~/Scripts/jquery-1.7.1.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
|
確保在Web.config中啓用了Unobtrusive JavaScript
1
|
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
|
(2)其次,使用Ajax.BeginForm方法構造一個form表單:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
<h1>Microsoft Ajax方式</h1>
@using (Ajax.BeginForm("GetServerDate", "Home", new AjaxOptions()
{
HttpMethod = "POST",
Confirm = "您肯定要提交?",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "spResult",
OnSuccess = "afterSuccess",
LoadingElementId="loading"
}))
{
<table>
<tr>
<td>用戶名:</td>
<td>
<input id="txtUserName" name="UserName" /></td>
</tr>
<tr>
<td>密 碼:</td>
<td>
<input id="txtPassword" name="Password" /></td>
</tr>
<tr>
<td align="center" colspan="2">
<input id="btnAjax" type="submit" value="提 交" />
</td>
</tr>
<tr>
<td align="center" colspan="2">
<div id="loading" style="display:none">
<img style="vertical-align:middle" src="~/Content/ico_loading2.gif" />正在獲取中,請稍候...
</div>
<span id="spResult"></span>
</td>
</tr>
</table>
}
|
這裏須要注意的是:
①Ajax.BeginForm沒有提供閉合的方法,須要使用Using配合關閉;
②AjaxOptions參數的設置:
HttpMethod表明這次AJAX請求究竟是POST方式仍是GET方式?這裏是POST方式;
Confirm表明點擊提交按鈕後提出的確認對話框,並給出用戶給定的提示語,這裏是:您肯定要提交?
InsertionMode表明請求得到後的數據是要替換仍是追加,通常選擇替換,即Replace;
UpdateTargetId表明須要替換的div標籤的Id,這裏是一個span標籤,表明須要顯示的信息都顯示在這個span內;
OnSuccess表明請求成功後所須要執行的回調方法,是一個js方法,能夠自定義,這裏是一個function afterSuccess()的方法;
1
2
3
|
function afterSuccess(data) {
//alert("您已成功獲取數據:" + data);
}
|
LoadingElementId=」loading」是一個頗有意思的屬性,表明在ajax請求期間爲了提供良好的用戶體驗,能夠給出一個正在加載中的提示,而這個LoadingElementId則表明一個提示的div區域的Id。這裏主要是指id爲loading的這個div,其中有一張gif圖片及一句話:正在獲取中,請稍等…的提示。
1
2
3
|
<div id="loading" style="display:none">
<img style="vertical-align:middle" src="~/Content/ico_loading2.gif" />正在獲取中,請稍候...
</div>
|
爲了顯示加載提示的效果,咱們人爲地修改一下Action方法,使用Thread.Sleep(3000)來延遲一下請求返回時間
1
2
3
4
5
|
public ActionResult GetServerDate()
{
System.Threading.Thread.Sleep(3000);
return Content(DateTime.Now.ToString());
}
|
好了,如今咱們能夠看一下效果如何:
到此,咱們的Microsoft AJAX就算完成了一個最簡單的Demo了。那麼,咱們不由想知道Microsoft AJAX是怎麼作到的?跟校驗同樣,咱們瀏覽一下生成的form表單就知道了:
原來咱們在AjaxOptions中所設置的參數也被解析成了form的自定義屬性,它們的對應關係以下:
AOP:Aspect Oriented Programming(AOP)是較爲熱門的一個話題。AOP,國內大體譯做「面向切面編程」。針對業務處理過程當中的切面進行提取,它所面對的是處理過程當中的某個步驟或階段,以得到邏輯過程當中各部分之間低耦合性的隔離效果。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。主要的功能是:日誌記錄,性能統計,安全控制,事務處理,異常處理等等。
微軟默認爲咱們提供了四種類型的過濾器(Filter),以下圖所示:
這裏,咱們主要來看看ActionFilter(Action過濾器)和ExceptionFilter(異常過濾器)的使用:
(1)Action Filter
ActionFilterAttribute默認實現了IActionFilter和IResultFilter。而ActionFilterAttribute是一個Abstract的類型,因此不能直接使用,由於它不能實例化,因此咱們想使用它必須繼承一下它而後才能使用。
①所以,咱們首先在Models中新建一個類,取名爲:MyActionFilterAttribute(以Attribute結尾比較符合編碼規範),並使其繼承自ActionFilterAttribute,而後重寫基類所提供的虛方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
public class MyActionFilterAttribute : ActionFilterAttribute
{
public string Name { get; set; }
/// <summary>
/// Action 執行以前先執行此方法
/// </summary>
/// <param name="filterContext">過濾器上下文</param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
HttpContext.Current.Response.Write("OnActionExecuting :" + Name);
}
/// <summary>
/// Action執行以後
/// </summary>
/// <param name="filterContext">過濾器上下文</param>
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
HttpContext.Current.Response.Write("OnActionExecuted :" + Name);
}
/// <summary>
/// ActionResult執行以前先執行此方法
/// </summary>
/// <param name="filterContext">過濾器上下文</param>
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
base.OnResultExecuting(filterContext);
HttpContext.Current.Response.Write("OnResultExecuting :" + Name);
}
/// <summary>
/// ActionResult執行以後先執行此方法
/// </summary>
/// <param name="filterContext">過濾器上下文</param>
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
HttpContext.Current.Response.Write("OnResultExecuted :" + Name);
}
}
|
這裏咱們重寫了四個虛方法,他們各自表明了在Action執行以前和以後須要執行的業務邏輯,以及在Result執行以前和以後須要執行的業務邏輯。這裏的Result主要是指咱們在Action中進行return 語句返回結果時(例如:return Content(「Hello Filter!」);),以前和以後要執行的邏輯處理。
好比:咱們想要在每一個Action執行以前進行用戶是否登陸的校驗,能夠在OnActionExecuting中判斷用戶Session是否存在,若是存在則繼續執行Action的具體業務代碼,若是不存在則重定向頁面到登錄頁,後邊的Action業務代碼再也不執行。
②如今有了自定義的過濾器,咱們怎麼將其應用到Action中呢?這裏有三種方式:
一是給某個控制器的某個Action指定此Filter:
1
2
3
4
5
6
|
[MyActionFilter(Name = "Filter Action")]
public ActionResult Filter()
{
Response.Write("<p>Action正在努力執行中...</p>");
return Content("<p>OK:視圖成功被渲染</p>");
}
|
二是給某個控制器的全部Action指定此Filter:
1
2
3
4
|
[MyActionFilter(Name="Home Filter")]
public class HomeController : Controller
{
}
|
可是,要注意的是:若是既給Controller指定了Filter,又給該Controller中的某個Action指定了Filter,那麼具體的這個Action以離其定義最近的Filter爲準,也就是一個優先級的順序問題:Action的Filter優先級高於Controller的Filter。
三是給此項目中的全部控制器即全局指定此Filter:在App_Start中更改FilterConfig類,此種方式優先級最低。
1
2
3
4
5
6
|
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
// 註冊自定義Action過濾器:優先級最低,可是能夠做用到全部的控制器和Action
filters.Add(new MyActionFilterAttribute() { Name = "Global Controller" });
}
|
③如今咱們來看看具體的效果:
能夠看到,咱們的/Home/Filter這個Action中只有兩句代碼,一句Response.Write,另外一句是return Content();在Response.Write以前執行了OnActionExecuting的過濾器方法,以後則執行了OnActionExecuted的過濾器方法;咱們剛剛說了,在Action中的return語句表明了Result,那麼在Result以前執行了OnResultExecuting過濾器方法,以後則執行了OnResultExecuted過濾器方法。這裏僅僅是爲了展現,在實際開發中是須要寫一些具體的業務邏輯處理的,例如:判斷用戶的登陸狀態,記錄用戶的操做日誌等等。
(2)Exception Filter
①一樣,在Models中新建一個類,取名爲:MyExceptionFilterAttribute,並使其繼承自HandleErrorAttribute。
1
2
3
4
5
6
7
8
9
|
public class MyExceptionFilterAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
base.OnException(filterContext);
HttpContext.Current.Response.Redirect("/Home/Index");
}
}
|
這裏,重寫基類的OnException方法,這裏僅僅爲了演示效果,沒有對異常進行處理。在實際開發中,須要獲取異常對象,並將其記錄至日誌中。例如,下面一段代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public override void OnException(ExceptionContext filterContext)
{
base.OnException(filterContext);
//獲取系統異常消息記錄
string strException = filterContext.Exception.Message;
if (!string.IsNullOrEmpty(strException))
{
//使用Log4Net記錄異常信息
Exception exception = filterContext.Exception;
if (exception != null)
{
LogHelper.WriteErrorLog(strException, exception);
}
else
{
LogHelper.WriteErrorLog(strException);
}
}
filterContext.HttpContext.Response.Redirect("~/GlobalErrorPage.html");
}
|
②有了異常過濾器,咱們怎麼來應用到項目中呢?答案也在App_Start中,仍是在FilterConfig類中,新添一句代碼進行註冊:
1
2
3
4
5
6
7
8
9
10
11
|
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
// 註冊自定義Action過濾器:優先級最低,可是能夠做用到全部的控制器和Action
filters.Add(new MyActionFilterAttribute() { Name = "Global Controller" });
// 註冊自定義Exception過濾器
filters.Add(new MyExceptionFilterAttribute());
}
}
|
③爲了測試,咱們新增一個Action,使其可以出現一個異常:DividedByZero
1
2
3
4
5
6
7
|
public ActionResult Exception()
{
int a = 10;
int b = 0;
int c = a / b;
return Content("Exception is happened.");
}
|
④當咱們測試這個Action時,會發現系統執行了自定義的異常過濾器,將咱們的這個請求改成重定向到Index這個Action了。
(1)蔣金楠,《ASP.NET MVC下的四種驗證編程方式》,http://www.cnblogs.com/artech/p/asp-net-mvc-validation-programming.html
(2)蔣金楠,《ASP.NET MVC下的四種驗證編程方式[續篇]》,http://www.cnblogs.com/artech/p/asp-net-mvc-4-validation.html
(3)馬倫,《ASP.NET MVC 2014特供教程》,http://bbs.itcast.cn/thread-26722-1-1.html
(4)w809026418,《MVC中使用 DataAnnotations 進行模型驗證》,http://www.cnblogs.com/haogj/archive/2011/11/16/2251920.html
(5)劉俊峯,《ASP.NET MVC中Unobtrusive Ajax的妙用》,http://www.cnblogs.com/rufi/archive/2012/03/31/unobtrusive-ajax.html