一、系統過濾器使用說明
1、OutputCache過濾器
OutputCache過濾器用於緩存你查詢結果,這樣可以提高用戶體驗,也可以減少查詢次數。它有以下屬性:
Duration:緩存的時間,以秒為單位,理論上緩存時間可以很長,但實際上當系統資源緊張時,緩存空間還是會被系統收回。
VaryByParam:以哪個字段為標識來緩存數據,比如當“ID”字段變化時,需要改變緩存(仍可保留原來的緩存),那么應該設VaryByParam為"ID"。這里你可以設置以下幾個值:
- * = 任何參數變化時,都改變緩存。
- none = 不改變緩存。
以分號“;”為間隔的字段名列表 = 列表中的字段發生變化,則改變緩存。
Location:緩存數據放在何處。緩存位置很重要,如果存在服務器上,那么所有用戶看到的緩存視圖都會一樣,如果存在客戶端,那么用戶只會看到自己的緩存。比如:如果是一些私人信息,那就不能存在服務器上。你可以設置以下值:
- · Any :默認值,輸出緩存可位於產生請求的瀏覽器客戶端、參與請求的代理服務器(或任何其他服務器)或處理請求的服務器上。
- · Client:輸出緩存位於產生請求的瀏覽器客戶端上。
- · Downstream 輸出緩存可存儲在任何 HTTP 1.1 可緩存設備中,源服務器除外。這包括代理服務器和發出請求的客戶端。
- · Server:輸出緩存位於處理請求的 Web 服務器上。
- · None:對於請求的頁,禁用輸出緩存。
- · ServerAndClient:輸出緩存只能存儲在源服務器或發出請求的客戶端中。代理服務器不能緩存響應。
- NoStore:該屬性定義一個布爾值,用於決定是否阻止敏感信息的二級存儲。
以下給出一個簡單的例子,在頁面上顯示一個時間,設置緩存為10秒,在10秒刷新,輸出的值都不會改變。
[OutputCache(Duration=5)] public ActionResult Index(string name) { return Content(DateTime.Now.ToString()); }
除了直接在Action或者類的定義前加上屬性,也可以使用配置文件,這樣就可以動態配置你的緩存模式了。
在<system.web>節中,添加如下配置:
<outputCacheSettings> <outputCacheProfiles> <add name="Cache1Hour" duration="3600" varyByParam="none"/> </outputCacheProfiles> </outputCacheSettings> </caching>
那么在Controller中可以這樣使用:
[OutputCache(CacheProfile="Cache1Hour")] public string Index() { return DateTime.Now.ToString("T"); }
[擴展]在已經緩存的頁面上添加動態內容
為了提高用戶體驗,我們會使用緩存技術,但是有時我們會需要在頁面上改變內容,如:提供一些動態信息、廣告的變化等。
此時我們可以調用 HttpResponse.WriteSubstitution() 方法。
@Response.WriteSubstitution(News.RenderNews);
其中News.RenderNews是一個靜態方法,它的定義如下,這個方法用來隨機顯示三條廣告詞。
public class News { public static string RenderNews(HttpContext context) { var news = new List<string> { "Gas prices go up!", "Life discovered on Mars!", "Moon disappears!" }; var rnd = new Random(); return news[rnd.Next(news.Count)]; } }
將Response.WriteSubstitution()寫成擴展方法的示例:
public static class AdHelper { public static void RenderBanner(this HtmlHelper helper) { var context = helper.ViewContext.HttpContext; context.Response.WriteSubstitution(RenderBannerInternal); } private static string RenderBannerInternal(HttpContext context) { var ads = new List<string> { "/ads/banner1.gif", "/ads/banner2.gif", "/ads/banner3.gif" }; var rnd = new Random(); var ad = ads[rnd.Next(ads.Count)]; return String.Format("<img src='{0}' />", ad); } }
調用方法如下:
<% Html.RenderBanner(); %>
2、ActionName
ActionName用於規定Action的名稱,當使用此過濾器后,MVC將不再理會路徑中的Action名稱,而是用標記中的ActionName代替方法名中的ActionName。例如:
[ActionName("關於")] public ActionResult About() { return View(); }
此時,當訪問/Home/About時匹配不到Action,需要訪問/Home/關於才能訪問該Action,並且使用的是名為"關於"的視圖。
3、NonAction
NonAction標記一個Action只是一個普通的方法,不作為MVC的Action。如:
public class HomeController : Controller { [NonAction] public ActionResult Index(string name) { return Content(DateTime.Now.ToString()); } }
此時訪問/Home/Index將找不到Action。
4、RequireHttps
強制使用Https重新發送請求;如:
public class HomeController : Controller { [RequireHttps] public ActionResult Index(string name) { return Content(DateTime.Now.ToString()); } }
如果請求:http://localhost/Home/Index 將跳轉到 https://localhost/Home/Index。
5、ValidateInput
該Action是否過濾Html等危險代碼(ASP.NET MVC在aspx中設置<%@ Page 的屬性無法完成等同任務。)
如以下代碼:
public class HomeController : Controller { [ValidateInput(true)] public ActionResult Index(string name) { return Content(DateTime.Now.ToString()); } }
上述代碼表示開啟安全驗證,當輸入以下路徑時:
http://localhost:3833/home/index?name=%3Ca%3E123%3C/a%3E //http://localhost:3833/home/index?name=<a>123</a>
程序報如下錯誤:
“/”應用程序中的服務器錯誤。
從客戶端(name="<a>123</a>")中檢測到有潛在危險的 Request.QueryString 值。
6、AllowHtml
AllowHtml用於禁用某個字段、屬性的驗證,則可以使用MVC3中的AllowHtml屬性實現。如:
namespace Mvc權限控制.Controllers { public class HomeController : Controller { public ActionResult Index(Content c) { return View(); ; } } public class Content { public int Id { get; set; } [AllowHtml] public string Body { get; set; } } }
頁面代碼:
<body> <div> <form action="/Home/Index" method="post"> 請輸入Id:<input type="text" name="Id" /> 請輸入姓名:<input type="text" name="Body" /> <input type="submit" value="提交" /> </form> </div> </body>
注意,如果將上面Body的屬性AllowHtml標記去掉,將報如下錯誤:
“/”應用程序中的服務器錯誤。
從客戶端(Body="<a>123</a>")中檢測到有潛在危險的 Request.Form 值。
7、SessionState自定義Session控制
SessionState只能應用於Controller,不能作用於某一個Action。可選值如下:
- Default = 0,使用默認 ASP.NET 邏輯來確定請求的會話狀態行為。默認邏輯是尋找 System.Web.IHttpHandler 中是否存在標記會話狀態接口。
- Required = 1,為請求啟用完全的讀寫會話狀態行為。此設置將覆蓋已通過檢查請求的處理程序確定的任何會話行為。
- ReadOnly = 2,為請求啟用只讀會話狀態。這意味着會話狀態無法更新。此設置將覆蓋已通過檢查請求的處理程序確定的任何會話狀態行為。
- Disabled = 3,未啟用會話狀態來處理請求。此設置將覆蓋已通過檢查請求的處理程序確定的任何會話行為。
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
8、Http動作過濾器
[HttpGet] [HttpSet] [HttpDelete] [HttpPut] 此4個過濾器用於篩選Http請求,[HttpGet]只處理Get請求,[HttpPost]只處理Post請求,[HttpDelete]只處理Delete請求,[HttpPut]只處理put請求。
9、ValidateAntiForgeryToken
防止跨站請求攻擊時會在cookie添加一個隨機項,然后添加一個隨機數到表單里的<input type="hidden" />而ValidateAntiForgeryToken就是用於檢測兩項是否相等。主要包括:
(1)請求的是否包含一個約定的AntiForgery名的cookie
(2)請求是否有一個Request.Form["約定的AntiForgery名"],約定的AntiForgery名的cookie和Request.Form值是否匹配。
10.AsyncTimeout
異步訪問過期設置
11.HandleError 錯誤過濾器
HandleErrorAttribute中,提供了4種基本屬性:
- ExceptionType:指定處理的異常類型
- View:指定顯示的View
- Master:指定要套用的Master頁面
- Order:設置執行的順序
12.AllowAnonymous
身份驗證過濾器,允許匿名用戶訪問
13.ChildActionOnly
聲明該Action不能直接通過Url 訪問但可以作為某一個Action的子Action訪問。
二、過濾器的類別以及執行順序:
在Asp.net MVC中一共有4種過濾器,並且按照如下順序依次執行。
- 授權篩選器:AuthorizationFilters
- 動作篩選器:ActionFilters
- 響應篩選器:ResultFilters
- 異常篩選器:ExceptionFilters
Controller最終是通過Controller的ExecuteCore完成的,這個方法通過調用ControllerActionInvoker的InvodeAction方法完成最終對於Action的調用。
其時序圖如下:
三、自定義過濾器接口
ActionFilterAttribute的定義如下,允許我們在Action執行之前或者之后,在Action的返回結果被處理之前或者之后進行自定義處理。
using System; namespace System.Web.Mvc { [AttrubiteUsage(ArrtibuteTargets.Class | AttributeTargets.Method,Inherited = true,AllowMultiple = false)] public abstract class ActionFilterAttribute : FilterAttribute,IActionFilter,IResultFilter
{ public virtual void OnActionExecuting(ActionExecutingContext filterContext){} public virtual void OnActionExecuted(ActionExecutedContext filterContext){} public virtual void OnResultExecuting(ResultExecutingContext filterContext){} public virtual void OnResultExecuted(ResultExecutedContext filterContext){} } }
我們需要實現IActionFilter接口:
public interface IActionFilter { void OnActionExecuting(ActionExecutingContext filterContext); void OnActionExecuted(ActionExecutedContext filterContext); }
對於ResultFilter來說,我們需要實現接口IResultFilter
public interface IResultFilter { void OnResultExecuting(ResultExecutingContext filterContext); void OnResultExecuted(ResultExecutedContext filterContext); }
AuthorizationFilter和ExceptionFilter都比較簡單,只有一個方法。
public interface IAuthorizationFilter { void OnAuthorization(AuthorizationContext filterContext); } public interface IExceptionFilter { void OnException(ExceptionContext filterContext); }
1、自定義Filter
自定義Filter需要繼承ActionFilterAttribute抽象類,重寫其中需要的方法,來看下ActionFilterAttribute類的方法簽名。
//表示所有操作-篩選器特性的基類。 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter { protected ActionFilterAttribute(); // 在Action執行之后由 MVC 框架調用。 public virtual void OnActionExecuted(ActionExecutedContext filterContext); // 在Action執行之前由 MVC 框架調用。 public virtual void OnActionExecuting(ActionExecutingContext filterContext); // 在執行Result后由 MVC 框架調用。 public virtual void OnResultExecuted(ResultExecutedContext filterContext); // 在執行Result之前由 MVC 框架調用。 public virtual void OnResultExecuting(ResultExecutingContext filterContext); }
因此自定義過濾器可以選擇適當的方法來重寫方可。下面來舉個簡單的例子:檢查登錄狀態的過濾器,沒有登錄則跳轉到登錄頁
控制器代碼:
[CheckLogin] //此處為自定義屬性,要引用相應的命名空間 public ActionResult Index() { return View(); } public ActionResult Login() //此Action自動往cookie里寫入登錄信息 { HttpCookie hcUserName = new HttpCookie("username","admin"); HttpCookie hcPassWord = new HttpCookie("password","123456"); System.Web.HttpContext.Current.Response.SetCookie(hcUserName); System.Web.HttpContext.Current.Response.SetCookie(hcPassWord); return View(); }
過濾器代碼:
public class CheckLogin : ActionFilterAttribute { //在Action執行之前 亂了點,其實只是判斷Cookie用戶名密碼正不正確而已而已。 public override void OnActionExecuting(ActionExecutingContext filterContext) { HttpCookieCollection CookieCollect = System.Web.HttpContext.Current.Request.Cookies;if (CookieCollect["username"] == null || CookieCollect["password"] == null) { filterContext.Result = new RedirectResult("/Home/Login"); } else { if (CookieCollect["username"].Value != "admin" && CookieCollect["password"].Value != "123456") { filterContext.Result = new RedirectResult("/Home/Login"); } } } }//本示例貪圖方便,將要跳轉到的Action放在同一個Controller下了,如果將過濾器放到Controller類頂部,則永遠也跳不到這個LoginAction。
此過濾器實現的效果是,當用戶Cookie中用戶名和密碼不正確則跳轉到登錄頁,注意過濾器也可以放在整個Controller類的頂部,表示該Controller下的所有Action都執行該項檢查。這樣一來,控制器里的代碼非常漂亮,再也不用所有的Action里都充斥着判斷登錄的代碼了。
2、帶參數的自定義Filter
首先,還是按照之前添加自定義過濾器的方法,添加一個自定義過濾器,只是里面多了一個屬性,代碼如下:
public class FilterAttribute : ActionFilterAttribute { public string Message { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); filterContext.HttpContext.Response.Write("Action執行之前" + Message + "<br />"); } public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); filterContext.HttpContext.Response.Write("Action執行之后" + Message + "<br />"); } public override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); filterContext.HttpContext.Response.Write("返回Result之前" + Message + "<br />"); } public override void OnResultExecuted(ResultExecutedContext filterContext) { base.OnResultExecuted(filterContext); filterContext.HttpContext.Response.Write("返回Result之后" + Message + "<br />"); } }
然后在調用過濾器的時候,添加上該參數,Controller代碼如下:
[Filter(Message="劉備")] //參數給上 public ActionResult Index() { return View(); }
輸出結果如下:
如果標簽打到Controller上的話,TestFilterAttributeFilter將作用到Controller下的所有的Action。
默認情況下Action上打了某個自定義標簽后,雖然在Controller上也打上了此標簽,但它只有Action上的標簽起作用了。
補充:如果Action沒有打上該標簽,那么Controller上的標簽便會被執行。
如果想讓Action上的標簽執行一次,然后Controller上的標簽也執行一次,那么應該如何操作呢?
我們只需在FilterAttribute類的定義上打上標記[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]即可【下面類的最上面紅色字體部分】,也就是讓其成為可以多次執行的Action。代碼如下:
[AttributeUsage(AttributeTargets.All,AllowMultiple = true)] public class FilterAttribute : ActionFilterAttribute { public string Message { get; set; } ...... }
3、全局過濾器
有時我們想有些公共的方法需要每個Action都執行,但是又不想再每一個Controller上都打上Action標簽,怎么辦?幸好Asp。Net MVC3帶來了一個美好的東西,全局Filter。而怎么注冊全局Filter呢?答案就在Global.asax中。讓我們看以下代碼,我是如何將上面我們定義的TestFilterAttribute 注冊到全局Filter中。
public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); //注冊全局過濾器 filters.Add(new TestFilterAttribute() { Message="全局"}); }
這樣就每個Action都會執行此過濾器,而不必每個Controller頂部都加上標簽。