一. 前世此生html
乍眼一看,該標題寫的有點煽情,最近也是在不斷反思,怎麼能把博客寫好,讓人能讀下去,通俗易懂,深刻淺出。前端
接下來幾個章節都是圍繞框架自己提供特性展開,有MVC程序集提供的,也有其它程序集提供;在本章節將重點介紹幾個MVC框架提供的且做用於方法上的特性,而且模仿其源碼自定義特性。jquery
其實早在前面的 DotNet進階章節,就寫過一篇關於特性的文章了,這裏從新總結一下特性核心要點。ajax
1. 什麼是特性?後端
特性是一個類,在不影響程序封裝的狀況下,額外的給程序添加一些信息,用於運行時描述程序或者影響程序的行爲。瀏覽器
2. 特性的做用範圍?cookie
提到特性的做用範圍,就不得不提到 AttributeUsage了,該類自己就是一個特性,繼承了Attribute類,用於約束自定義特性(你能夠看到系統提供的不少特性中,均能看到它的身影),下面先看一 下它的源碼:框架
該特性有一個參數,兩個核心屬性,AttributeTargets參數約束了該給特性能夠做用的範圍,經過右面的代碼可知,能夠做用於:類、方法、參數、屬性、返回值等等,該參數默認爲ALL。前後端分離
AllowMultiple:約束該特性可否同時做用於某個元素(類、方法、屬性等等)屢次,默認爲false。ide
Inherited:約束該特性做用於基類(或其它)上,其子類可否繼承該特性,默認爲true。
3. 如何自定義特性?
簡單來講,聲明一個以Attribute結尾的類,繼承Attribute類,而後加上AttributeTargets特性約束,一個簡單的特性就產生了。
二. MVC中的經常使用特性
有了前面的鋪墊,這裏講解【System.Web.Mvc】程序集下的一些特性就很好理解,理解源碼的同時,主要掌握其如何使用。
MVC中提供的經常使用特性有:【HttpGet】、【HttpPost】、【AcceptVerbs】、【ActionName】、【NoAction】、【AllowAnonymous】、【ValidateAntiForgeryToken】、【ChildActionOnly】、【Bind】這九個特性。
查看源碼可知,其中【HttpGet】【HttpPost】【AcceptVerbs】【ActionName】【NoAction】【ChildActionOnly】均繼承ActionNameSelectorAttribute類,實現了IsValidForRequest這個抽象方法,且特性約束爲: [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)],顯然這些特性均是做用於方法上的,且不須要同時做用,容許子類繼承該特性。
以【HttpGet】的源碼爲例:
其中【AllowAnonymous】直接繼承Attribute類,源碼以下:
其中【ValidateAntiForgeryToken】繼承了FilterAttribute類,實現了IAuthorizationFilter接口,源碼以下:
(擴展一下:AuthorizeAttribute類也是繼承了FilterAttribute類,實現了IAuthorizationFilter接口)
1. HttpGet和HttpPost
(1). HttpGet:只容許Get請求訪問
底層運用的AcceptVerb特性實現的,因此等價於[AcceptVerbs(HttpVerbs.Get)]或[AcceptVerbs("Get")]
測試:前端用Ajax請求,若是非Get請求方式進行請求,則提示404找不到
(2). HttpPost:只容許Post請求訪問
底層運用的AcceptVerb特性實現的,因此等價於[AcceptVerbs(HttpVerbs.Post)]或[AcceptVerbs("Post")]
測試:前端用Ajax請求,若是非Post請求方式進行請求,則提示404找不到
特別注意:若是一個方法要同時容許Get和Post請求[HttpGet]和[HttpPost]同時加載上面是錯誤的!!這個時候就須要使用AcceptVerb特性了(固然方法上若是什麼特性也不加,什麼請求均支持)
2. AcceptVerbs
AccetpVerbs:用於限定請求方式(包括:Get、Post、Put、Delete、Head、Patch、Options)
查看源碼可知:該特性有兩個構造函數,全部兩種寫法,如:只容許Get請求,能夠:[AcceptVerbs(HttpVerbs.Get)]和[AcceptVerbs("Post")]
若要同時支持多種請求,能夠:[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]或[AcceptVerbs("Get", "Post")]
相關測試代碼以下:
1 //1. 下面三種寫法均爲只容許Get請求 2 //[HttpGet] 3 //[AcceptVerbs(HttpVerbs.Get)] 4 //[AcceptVerbs("Get")] 5 6 //2. 下面三種寫法均爲只容許Get請求 7 //[HttpPost] 8 //[AcceptVerbs(HttpVerbs.Post)] 9 //[AcceptVerbs("Post")] 10 11 //3. 下面兩種寫法表示:既容許Get請求也容許Post請求 12 [AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)] 13 //[AcceptVerbs("Get", "Post")] 14 public ActionResult TestMethordWay() 15 { 16 return Content("請求成功"); 17 }
1 //1. 測試只容許Get或Post請求 2 $("#btn1").click(function () { 3 $.ajax({ 4 type: "Put", //Post 、Put 5 url: "TestMethordWay", 6 data: "", 7 success: function (msg) { 8 alert(msg); 9 } 10 }); 11 });
3. ActionName
ActionName:修改Action自己的方法名
測試:請求TestActionName1方法,報404找不到
請求TestActionName2方法,正常訪問
相關測試代碼以下:
1 /// <summary> 2 /// 名字變爲:TestActionName2了 3 /// </summary> 4 /// <returns></returns> 5 [ActionName("TestActionName2")] 6 public ActionResult TestActionName1() 7 { 8 return Content("我是TestActionName1"); 9 }
1 //2. 測試ActionName特性 2 $("#btn2").click(function () { 3 $.ajax({ 4 type: "Get", 5 url: "TestActionName2", //TestActionName1 6 data: "", 7 success: function (msg) { 8 alert(msg); 9 } 10 }); 11 });
4. NoAction
NoAction: 標記控制器中的action將不在是一個方法,不能前端Http請求訪問
測試:前端頁面ajax進行請求,報404找不到
但在其它action中進行調用,能正常調用
相關測試代碼以下:
1 /// <summary> 2 /// 標記該方法將不是一個方法 3 /// </summary> 4 /// <returns></returns> 5 [NonAction] 6 public ActionResult TestNoAction() 7 { 8 return Content("請求成功"); 9 }
1 //3. 測試NoAction特性 2 $("#btn3").click(function () { 3 $.ajax({ 4 type: "Get", 5 url: "TestNoAction", //TestNoAction 6 data: "", 7 success: function (msg) { 8 alert(msg); 9 } 10 }); 11 });
5. AllowAnonymous
AllowAnonymous:該特性用於標記在受權期間要跳過 AuthorizeAttribute 過濾器的驗證
解釋:AuthorizeAttribute是MVC框架自帶的實現IAuthorizationFilter過濾器的一個類,內部有一套自身業務驗證(感興趣的能夠本身研究源碼)
而AllowAnonymous就是爲了標記跨過AuthorizeAttribute驗證的
這裏不作詳細測試
6. ValidateAntiForgeryToken
ValidateAntiForgeryToken:阻止跨站請求僞造攻擊(CSRF).
①. CSRF原理是什麼:
a.用戶mr訪問正規網站A,登陸驗證經過,並在用戶mr處產生Cookie
b.用戶mr在不關閉A網站的狀況下打開危險的B網站,在B網站中要求訪問A網站,發出一個Request請求
c.這時候瀏覽器帶着A網站在mr出產生的Cookie進行訪問A網站
d.這時候A網站就沒法判斷這個cookie是誰產生的,默認就給經過了
詳細見:https://www.cnblogs.com/hechunming/p/4647646.html
②:解決方案
a. 在Controller中的action上加上特性[ValidateAntiForgeryToken]
b. 對於增刪改查操做前端調用: $.ajaxAntiForgery方法進行ajax請求(須要引入jqueryToken的js文件)
這裏不作詳細測試
7. ChildActionOnly
ChildActionOnly:限制操做方法只能由子操做進行調用。
①. 測試直接輸入:http://localhost:7559/SpecialAttribute/Index2, 沒法訪問報錯.
②. 須要經過RenderAction來調用(存在問題,與Unity改造框架衝突衝突)
這裏RenderAction不作詳細測試
8. Bind
①. 源碼的角度分析:該特性能夠做用於類或參數(本章節測試做用於參數)
②. 該特性有三個核心屬性:
a. Exclude:獲取或設置不容許綁定的屬性名稱的列表(各屬性名稱之間用逗號分隔)
b. Include:獲取或設置容許綁定的屬性名稱的列表(各屬性名稱之間用逗號分隔),與Exclude一個道理,一般根據狀況使用一個便可
c. Prefix:獲取或設置在呈現表示綁定到操做參數或模型屬性的標記時要使用的前綴
不適用與ajax提交,適用於razor語法中的@{Html.TextBox(stu.id)},在如今先後端分離盛行的狀況下,有點不適合了
測試:
前端過個ajax傳過來三個參數:id、name、sex, 參數中的stu只能收到id和name,sex爲null,若想收到sex,須要在方法中經過request進行接收
1 /// <summary> 2 /// stu中的sex屬性爲null 3 /// var 中sex爲男 4 /// </summary> 5 /// <param name="stu"></param> 6 /// <returns></returns> 7 public ActionResult TestBindAttribute([Bind(Include = "id,name")]Student stu) 8 { 9 var sex = Request["sex"]; 10 return Content("請求成功"); 11 } 12
1 //4.測試BindAttribute特性 2 $("#btn4").click(function () { 3 $.ajax({ 4 type: "Get", 5 url: "TestBindAttribute", 6 data: {"id":"123","name":"ypf","sex":"男"}, 7 success: function (msg) { 8 alert(msg); 9 } 10 }); 11 });
三. 自定義一個相似的特性
要求:支持Get請求,且必須是Ajax請求
思路:由HttpGet特性能夠知道:須要繼承ActionMethodSelectorAttribute類,而後覆寫IsValidForRequest方法便可
1 public class HttpGetAndAjaxAttribute : ActionMethodSelectorAttribute 2 { 3 public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) 4 { 5 //1.獲取請求方式 6 string HttpWay = controllerContext.HttpContext.Request.GetHttpMethodOverride(); 7 8 //2. 獲取是不是Ajax請求 9 bool isAjax = controllerContext.HttpContext.Request.IsAjaxRequest(); 10 11 if (HttpWay.ToLower() == "get" && isAjax == true) 12 { 13 return true; 14 } 15 return false; 16 17 } 18 } 19 20 [HttpGetAndAjax] 21 public ActionResult GetAndAjax() 22 { 23 return Content("請求成功"); 24 }
1 //5.測試自定義特性 2 $("#btn5").click(function () { 3 $.ajax({ 4 type: "Get", 5 url: "GetAndAjax", 6 data: "", 7 success: function (msg) { 8 alert(msg); 9 } 10 }); 11 });