天天看點

asp.net mvc的Routing、Controller、Filter學習筆記

1、深入學習Routing

首先Routing的處于的位置,一個Http請求過來,Web容器接收到以後,将資訊交給Routing(路由元件),Routing再進行處理~那麼Routing的作用

确定Controller、确定Action、确定其他參數、根據識别出來的資料, 将請求傳遞給Controller和Action.

小提示:asp.net mvc預覽版的時候,Routing元件還是作為asp.net mvc的一部分,後續的版本似乎就微軟将其編譯成一個獨立的元件提供System.Web.Routing.dll,也就是說asp.net mvc項目是開源的,但是Routing元件并沒有開源。Routing元件不僅在asp.net mvc中可以使用,也可以在WebForm中使用

首先我們建立一個asp.net mvc2/3的項目,建立好以後, 直接運作為什麼通路localhost/home/index會傳遞給 HomeController中名為index的action(即HomeController類中的index方法)?怎麼實作的呢? 在我們建立的項目中,Global.asax檔案中有以下方法 注冊路由

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );

        }      

Routes是Application級别(全局)的,在Application開始的時候,程式注冊路由,建立的項目預設隻注冊了一條路由,看下代碼,

     routes.MapRoute(

     "Default", // Route name

     "{controller}/{action}/{id}", // URL with parameters

     new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults

     );

MapRoute第一個參數是此路由的名稱,第二個參數是URL的參數形式,{controller}相當于是一個string.Format方法中的占位符,意思是這裡可以是任意一個controller名稱,

同理,action、id也是一樣的,那為什麼請求的/Home/Index并沒有Id這個參數,第三個參數是路由規則預設值,這條路由預設的controller是home,action是index,而id呢,是可選的~~~當我們請求/Home/Index的時候,會被此路由擷取,而我們直接請求http://localhost的時候,可以到Home/Index的時候,路由參數有預設值

 Ok,到這裡我們學習了如何注冊路由,我們來試着自己寫一條路由

自定義路由

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            //自定義路由,
            routes.MapRoute(
                "myRoute", // Route name
                "{controller}-{action}", // URL with parameters
                new { controller = "Home", action = "Index" } // Parameter defaults
            );
            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );      

然後重新啟動程式,因為Routes是Application級别的,在我們修改Application級别資訊後,應該退出

asp.net mvc的Routing、Controller、Filter學習筆記

,否則不會有效果滴,這個是好多初學者容易犯錯的地方.如果是生産環境,應該重新啟動下Web容器.

我們運作程式以後,請求域名/home-index一樣可以請求頁面,說明我們自定義的路由有效.

這裡路由的規則非常靈活,我們可以自定義,以下的路由規則都可以

     routes.MapRoute(

     "myRoute", // Route name

     "{controller}-{action}-{1}-{2}-{3}", // URL with parameters

     new { controller = "Home", action = "Index" } // Parameter defaults

     );

MapRoute()方法

MapRoute有以下的重載方法

MapRoute( string name, string url);

MapRoute( string name, string url, object defaults);

MapRoute( string name, string url, string[] namespaces);

MapRoute( string name, string url, object defaults, object constraints);

MapRoute( string name, string url, object defaults, string[] namespaces);

MapRoute( string name, string url, object defaults, object constraints, string[] namespaces);

name參數: 規則名稱, 可以随意起名.不可以重名,否則會發生錯誤: 路由集合中已經存在名為“Default”的路由。路由名必須是唯一的。

url參數: url擷取資料的規則, 這裡不是正規表達式,  将要識别的參數括起來即可, 比如: {controller}/{action} 最少隻需要傳遞name和url參數就可以建立一條Routing(路由)規則.比如執行個體中的規則完全可以改為: routes.MapRoute( "Default", "{controller}/{action}");

defaults參數: url參數的預設值.如果一個url隻有controller: localhost/home/ 而且我們隻建立了一條url擷取資料規則: {controller}/{action} 那麼這時就會為action參數設定defaults參數中規定的預設值. defaults參數是Object類型,是以可以傳遞一個匿名類型來初始化預設值: new { controller = "Home", action = "Index" } 執行個體中使用的是三個參數的MapRoute方法: routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "" } // Parameter defaults );

constraints參數: 用來限定每個參數的規則或Http請求的類型.constraints屬性是一個RouteValueDictionary對象,也就是一個字典表, 但是這個字典表的值可以有兩種: 用于定義正規表達式的字元串。正規表達式不區分大小寫。 一個用于實作 IRouteConstraint 接口且包含 Match 方法的對象。 通過使用正規表達式可以規定參數格式,比如controller參數隻能為4位數字: new { controller = @"\d{4}"}

  通過第IRouteConstraint 接口目前可以限制請求的類型.因為System.Web.Routing中提供了HttpMethodConstraint類, 這個類實作了IRouteConstraint 接口. 我們可以通過為RouteValueDictionary字典對象添加鍵為"httpMethod", 值為一個HttpMethodConstraint對象來為路由規則添加HTTP 謂詞的限制, 比如限制一條路由規則隻能處理GET請求: httpMethod = new HttpMethodConstraint( "GET", "POST" )

View Code

 routes.MapRoute( 
                 "Default", // Route name 
                 "{controller}/{action}/{id}", // URL with parameters 
                 new { controller = "Home", action = "Index", id = "" }, // Parameter defaults 
                 new { controller = @"\d{4}" , httpMethod = new HttpMethodConstraint( "GET", "POST" ) } 
                 );      

我們注冊這樣的一條路由

自定義帶過濾的路由

routes.MapRoute(
                "test", // Route name
                "{controller}-{action}-{id}", // URL with parameters
                new { controller = "Home", action = "Index" }, // Parameter defaults
                new { controller = @"^\w ", action = @"^\w ", id = @"^\d " }
            );      

編譯且運作,當我們請求/home-index-11可以請求到,而我們/home-index-1x這樣的是不能比對的,

那這樣的規則的又有什麼用處呢?在這裡可以對請求進行過濾,比如我們的id隻能是數字類型,防止一些非法檢測

路由元件的調試

假設我們寫了N條路由,當我們發送請求的時候,并沒有被我們想要的規則所捕獲解析,so..我們需要調試。

在我們的項目添加RouteDebug.dll的引用,并在Global.asax檔案中的Application_Start方法添加以下代碼

啟用路由調試

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RegisterRoutes(RouteTable.Routes);
            //啟動路由表調試
            RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
        }      

編譯後,我們啟動程式,會發現有一個Route Tester頁面,關于RouteDebug比對的頁面資訊,請大家查詢RouteDebug的手冊.

Route Tester中的Route Data請求就是目前請求的路由資訊

asp.net mvc的Routing、Controller、Filter學習筆記

 我們請求/home/index,從圖中可以看到被第4條路由捕獲到.

  那這樣的路由有什麼作用呢?我想大部分做SEO的朋友都有經驗,二級頁面和三級頁面,爬蟲抓取的頻率顯然是不一樣的,這樣我們可以将三級甚至更深層的頁面構造成二級頁面~這個也是SEO的技巧之一

注意:我們在寫路由規則的時候,因為路由規則有前後順序(指注冊的先後順序),也許寫的路由規則被它前面的規則給捕獲到,進行處理。那後面注冊的路由就無效

2、Controller學習

  在ASP.NET MVC中, 一個Controller可以包含多個Action. 每一個Action都是一個方法, 傳回一個ActionResult執行個體.

   ActionResult類包括ExecuteResult方法, 當ActionResult對象傳回後會執行此方法.

  Controller 處理流程:

  1. 頁面處理流程 發送請求 –> UrlRoutingModule捕獲請求 –> MvcRouteHandler.GetHttpHandler() –> MvcHandler.ProcessRequest

  2.MvcHandler.ProcessRequest() 處理流程: 使用工廠方法擷取具體的Controller –> Controller.Execute() –> 釋放Controller對象

  3.Controller.Execute() 處理流程: 擷取Action –> 調用Action方法擷取傳回的ActionResult –> 調用ActionResult.ExecuteResult() 方法

  4.ActionResult.ExecuteResult() 處理流程: 擷取IView對象-> 根據IView對象中的頁面路徑擷取Page類-> 調用IView.RenderView() 方法(内部調用Page.RenderView方法)

  Controller對象的職責是傳遞資料,擷取View對象(實作了IView接口的類),通知View對象顯示.

  View對象的作用是顯示.雖然顯示的方法RenderView()是由Controller調用的,但是Controller僅僅是一個"指揮官"的作用, 具體的顯示邏輯仍然在View對象中.

     注意IView接口與具體的ViewPage之間的聯系.在Controller和View之間還存在着IView對象.對于ASP.NET程式提供了 WebFormView對象實作了IView接口.WebFormView負責根據虛拟目錄擷取具體的Page類,然後調用 Page.RenderView()

Controller中的ActionResult

在Controller中,每一個Aciton傳回都是ActionResult,我們通過檢視ActionResult的定義

ActionResult

// Summary:
    //     Encapsulates the result of an action method and is used to perform a framework-level
    //     operation on behalf of the action method.
    public abstract class ActionResult
    {
        // Summary:
        //     Initializes a new instance of the System.Web.Mvc.ActionResult class.
        protected ActionResult();

        // Summary:
        //     Enables processing of the result of an action method by a custom type that
        //     inherits from the System.Web.Mvc.ActionResult class.
        //
        // Parameters:
        //   context:
        //     The context in which the result is executed. The context information includes
        //     the controller, HTTP content, request context, and route data.
        public abstract void ExecuteResult(ControllerContext context);
    }      

關于ActionResult的派生類,大家可以參考:http://blog.csdn.net/steven_husm/article/details/4641281

3、Filter的學習

mvc項目中,action在執行前或執行後想做一些特殊的操作,比如身份校驗、行為截取等,asp.net mvc提供了以下幾種預設的Filter

ASP.NET MVC 架構支援以下幾種篩選器:

1、授權篩選器– 實作了 IAuthorizationFilter 接口

  這一類的篩選器用來實作使用者驗證和對Action的通路授權。比如Authorize 就屬于Authorization 篩選器。

2、Action 篩選器– 實作了 IActionFilter 接口

  它可以包含一些Action執行前或者執行後的邏輯,比如有一些篩選器專門用來修改Action傳回的資料。

3、Result 篩選器– 實作了 IResultFilter 接口

  它可以包含一些view result生成前或者生成後的邏輯,比如有一些篩選器專門用來修改視圖向浏覽器展現前的結果。

4、異常篩選器– 實作了IExceptionFilter 接口

  它用以用來處理Action或者Result的錯誤,也可以記錄錯誤。

     篩選器的預設執行順序也和上面的列出的序号相同,比如Authorization 篩選器會先于Action 篩選器執行,而Exception 篩選器總會在最後執行。當然你也可以根據需要通過Order屬性設定篩選器執行的順序。

下面通過一個實際的例子來說明應用,建立一個mvc3項目,在項目中新加一個Common檔案夾,并新加一個類LogUserOperationAttribute.cs

LogUserOperationAttribute

public class LogUserOperationAttribute : ActionFilterAttribute
    {
        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            //todo寫日志代碼,這裡注意并發性
            File.AppendAllText(@"C:\log.txt", string.Format("{0}日志", DateTime.Now));
            base.OnResultExecuted(filterContext);
        }
    }      

新加一個HomeControllers,并在HomeControllers中新加一個Action--Index以及視圖檔案

HomeController

public class HomeController : Controller
    {
        //
        // GET: /Home/
        [LogUserOperation]
        public ActionResult Index()
        {
            return View();
        }
    }      

然後儲存代碼,F5運作,當頁面顯示出來以後,在C槽已經有log.txt檔案.

abstract class ActionFilterAttribute這四個方法分别代表,Action執行時,Action執行後,Result傳回時,Result傳回後。(它們的執行順序跟下圖一緻)

asp.net mvc的Routing、Controller、Filter學習筆記

     當然,我們也可以在ASP.NET MVC 3.0中增加Global Action Filter,這個就把此類filter變成全局的filter,所有Controller的action都會通過個filter的規則來執行,它跟我們在Controller或某個action所辨別的屬性有很大的差別,就是全局跟部分的差別。對于Global Action Filter 有着很多的應用,比如系統的權限、系統異常的處理

Gloable Filter實際應用--系統異常處理體系

我們程式運作過程中會有各種不可預料的情況,執行某個Action會發生異常,那我們異常資訊需要記錄下來,我們可以像上面的例子一樣,在Action或Controller上打上标記,但是那麼多action和Controller都去打标記,确實是很痛苦的事情,而asp.net mvc3有一個全局的Filter,我們隻需要将我們自定義的Filter注冊成全局的Filter

在Common檔案夾中,建立一個類CustomHandleErrorAttribute.cs

CustomHandleErrorAttribute

public class CustomHandleErrorAttribute : IExceptionFilter 
    {
        public void OnException(ExceptionContext filterContext)
        {
            File.AppendAllText(@"C:\error.txt", string.Format("{0}錯誤{1}", DateTime.Now, filterContext.Exception.Message));
        }
    }      

在Global.asax檔案中,RegisterGlobalFilters方法寫注冊代碼

注冊全局Filters

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new CustomHandleErrorAttribute());//系統的邏輯錯誤通過這個filters來處理
            filters.Add(new HandleErrorAttribute());
        }      

 在HomeController中新加一個Action--->Error

HomeController

public class HomeController : Controller
    {
        //
        // GET: /Home/
        [LogUserOperation]
        public ActionResult Index()
        {
            return View();
        }
        public ActionResult Error()
        {
            try
            {
                System.IO.File.Open("C:\\111111.exe", System.IO.FileMode.Open);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return View();
        }
    }      

編譯後運作項目~我們請求/Home/Error這個action,,程式出現異常,我們切換到C槽,發現C槽已經有error.txt檔案

注冊到全局的Filters所有的Action和Result執行前後都會調用我們的CustomHandleErrorAttribute的重寫的方法。

GlobalFilters、ControllerFilters、ActionFilters的執行順序問題

GlobalFilters-->ControllerFilters-->ActionFilters《這個是有執行的前置條件的》

   當然這是在CustomHandleErrorAttribute類的定義上打上标記[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]的前提下。不然 如果Action打上了标簽跟Controller的相同則它隻會執行Action上的Filter。

作者: wolfram   原文連結

繼續閱讀