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級別信息后,應該退出,否則不會有效果滴,這個是好多初學者容易犯錯的地方.如果是生產環境,應該重新啟動下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請求就是當前請求的路由信息

 我們請求/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 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所有的ActionResult執行前后都會調用我們的CustomHandleErrorAttribute的重寫的方法。

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

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

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM