亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

MVC Model Binder

系統 2293 0

MVC Model Binder

?

Filter(篩選器)是基于AOP(面向方面編程)的設計,它的作用是對MVC框架處理客戶端請求注入額外的邏輯,以非常簡單優美的方式實現 橫切關注點(Cross-cutting Concerns) 。橫切關注點是指橫越應該程序的多個甚至所有模塊的功能,經典的橫切關注點有日志記錄、緩存處理、異常處理和權限驗證等。本文將分別介紹MVC框架所支持的不同種類的Filter的創建和使用,以及如何控制它們的執行。

本文目錄

四種基本 Filter 概述

MVC框架支持的Filter可以歸為四類,每一類都可以對處理請求的不同時間點引入額外的邏輯處理。這四類Filter如下表:

MVC Model Binder_第1張圖片

在MVC框架調用acion之前,它會先判斷有沒有實現上表中的接口的特性,如果有,則在請求管道的適當的點調用特性中定義的方法。

MVC框架為這些種類的Filter接口實現了默認的特性類。如上表,ActionFilterAttribute 類實現了 IActionFilter 和 IResultFilter 兩個接口,這個類是一個抽象類,必須對它提供實現。另外兩個特性類,AuthorizeAttribute 和 HandleErrorAttribute, 已經提供了一些有用的方法,可以直接使用。

Filter 既能應用在單個的ation方法上,也能應用在整個controller上,并可以在acion和controller上應用多個Filter。如下所示:

          [Authorize(Roles=
          
            "
          
          
            trader
          
          
            "
          
          )]  
          
            //
          
          
             對所有action有效
          
          
            public
          
          
            class
          
          
             ExampleController : Controller { 
 
    [ShowMessage]  
          
          
            //
          
          
             對當前ation有效
          
          
    [OutputCache(Duration=
          
            60
          
          )] 
          
            //
          
          
             對當前ation有效
          
          
            public
          
          
             ActionResult Index() { 
        
          
          
            //
          
          
             ...  
          
          
                } 
}
          
        

注意,對于自定義的controller的基類,應用于該基類的Filter也將對繼承自該基類的所有子類有效。

Authorization Filter

Authorization Filter是在action方法和其他種類的Filter之前運行的。它的作用是強制實施權限策略,保證action方法只能被授權的用戶調用。Authorization Filter實現的接口如下:

          
            namespace
          
          
             System.Web.Mvc {
    
          
          
            public
          
          
            interface
          
          
             IAuthorizationFilter { 
        
          
          
            void
          
          
             OnAuthorization(AuthorizationContext filterContext); 
    } 
} 
          
        

自定義Authorization Filter

你可以自己實現 IAuthorizationFilter 接口來創建自己的安全認證邏輯,但一般沒有這個必要也不推薦這樣做。如果要自定義安全認證策略,更安全的方式是繼承默認的?AuthorizeAttribute 類。

我們下面通過繼承?AuthorizeAttribute 類來演示自定義Authorization Filter。新建一個空MVC應用程序,和往常的示例一樣添加一個 Infrastructure 文件夾,然后添加一個?CustomAuthAttribute.cs 類文件,代碼如下:

          
            namespace
          
          
             MvcApplication1.Infrastructure {
    
          
          
            public
          
          
            class
          
          
             CustomAuthAttribute : AuthorizeAttribute {
        
          
          
            private
          
          
            bool
          
          
             localAllowed;
        
          
          
            public
          
           CustomAuthAttribute(
          
            bool
          
          
             allowedParam) {
            localAllowed 
          
          =
          
             allowedParam;
        }
        
          
          
            protected
          
          
            override
          
          
            bool
          
          
             AuthorizeCore(HttpContextBase httpContext) {
            
          
          
            if
          
          
             (httpContext.Request.IsLocal) {
                
          
          
            return
          
          
             localAllowed;
            }
            
          
          
            else
          
          
             {
                
          
          
            return
          
          
            true
          
          
            ;
            }
        }
    }
}
          
        

這個簡單的Filter,通過重寫 AuthorizeCore 方法,允許我們阻止本地的請求,在應用該Filter時,可以通過構造函數來指定是否允許本地請求。AuthorizeAttribte 類幫我們內置地實現了很多東西,我們只需把重點放在?AuthorizeCore 方法上,在該方法中實現權限認證的邏輯。

為了演示這個Filter的作用,我們新建一個名為?Home 的 controller,然后在 Index action方法上應用這個Filter。參數設置為false以保護這個 action 不被本地訪問,如下:

          
            public
          
          
            class
          
          
             HomeController : Controller {

    [CustomAuth(
          
          
            false
          
          
            )]
    
          
          
            public
          
          
            string
          
          
             Index() {
        
          
          
            return
          
          
            "
          
          
            This is the Index action on the Home controller
          
          
            "
          
          
            ;
    }
}
          
        

運行程序,根據系統生成的默認路由值,將請求 /Home/Index,結果如下:

MVC Model Binder_第2張圖片

我們通過把?AuthorizeAttribute 類作為基類自定義了一個簡單的Filter,那么?AuthorizeAttribute 類本身作為Filter有哪些有用的功能呢?

使用內置的Authorization Filter

當我們直接使用?AuthorizeAttribute 類作為Filter時,可以通過兩個屬性來指定我們的權限策略。這兩個屬性及說明如下:

  • Users屬性,string類型,指定允許訪問action方法的用戶名,多個用戶名用逗號隔開。
  • Roles屬性,string類型,用逗號分隔的角色名,訪問action方法的用戶必須屬于這些角色之一。

使用如下:

          
            public
          
          
            class
          
          
             HomeController : Controller {

    [Authorize(Users 
          
          = 
          
            "
          
          
            jim, steve, jack
          
          
            "
          
          , Roles = 
          
            "
          
          
            admin
          
          
            "
          
          
            )]
    
          
          
            public
          
          
            string
          
          
             Index() {
        
          
          
            return
          
          
            "
          
          
            This is the Index action on the Home controller
          
          
            "
          
          
            ;
    }
}
          
        

這里我們為Index方法應用了Authorize特性,并同時指定了能訪問該方法的用戶和角色。要訪問Index action,必須兩者都滿足條件,即用戶名必須是?jim, steve, jack 中的一個,而且必須屬性 admin 角色。

另外,如果不指定任何用戶名和角色名(即 [Authorize] ),那么只要是登錄用戶都能訪問該action方法。

你可以通過創建一個Internet模板的應用程序來看一下效果,這里就不演示了。

對于大部分應用程序,AuthorizeAttribute 特性類提供的權限策略是足夠用的。如果你有特殊的需求,則可以通過繼承AuthorizeAttribute 類來滿足。

Exception Filter

Exception Filter,在下面三種來源拋出未處理的異常時運行:

  • 另外一種Filter(如Authorization、Action或Result等Filter)。
  • Action方法本身。
  • Action方法執行完成(即處理ActionResult的時候)。

Exception Filter必須實現?IExceptionFilter 接口,該接口的定義如下:

          
            namespace
          
          
             System.Web.Mvc { 
    
          
          
            public
          
          
            interface
          
          
             IExceptionFilter { 
        
          
          
            void
          
          
             OnException(ExceptionContext filterContext); 
    } 
} 
          
        

ExceptionContext 常用屬性說明

在 IExceptionFilter 的接口定義中,唯一的 OnException 方法在未處理的異常引發時執行,其中參數的類型:ExceptionContext,它繼承自?ControllerContext 類,ControllerContext?包含如下常用的屬性:

  • Controller,返回當前請求的controller對象。
  • HttpContext,提供請求和響應的詳細信息。
  • IsChildAction,如果是子action則返回true(稍后將簡單介紹子action)。
  • RequestContext,提供請求上下文信息。
  • RouteData,當前請求的路由實例信息。

作為繼承?ControllerContext?類的子類,ExceptionContext 類還提供了以下對處理異常的常用屬性:

  • ActionDescriptor,提供action方法的詳細信息。
  • Result,是一個?ActionResult 類型,通過把這個屬性值設為非空可以讓某個Filter的執行取消。
  • Exception,未處理異常信息。
  • ExceptionHandled,如果另外一個Filter把這個異常標記為已處理則返回true。

一個Exception Filter可以通過把?ExceptionHandled 屬性設置為true來標注該異常已被處理過,這個屬性一般在某個action方法上應用了多個Exception Filter時會用到。ExceptionHandled 屬性設置為true后,就可以通過該屬性的值來判斷其它應用在同一個action方法Exception Filter是否已經處理了這個異常,以免同一個異常在不同的Filter中重復被處理。

示例演示

在?Infrastructure 文件夾下添加一個?RangeExceptionAttribute.cs 類文件,代碼如下:

          
            public
          
          
            class
          
          
             RangeExceptionAttribute : FilterAttribute, IExceptionFilter {
    
          
          
            public
          
          
            void
          
          
             OnException(ExceptionContext filterContext) {
        
          
          
            if
          
           (!filterContext.ExceptionHandled && 
          
            filterContext.Exception 
          
          
            is
          
          
             ArgumentOutOfRangeException) {
            filterContext.Result 
          
          = 
          
            new
          
           RedirectResult(
          
            "
          
          
            ~/Content/RangeErrorPage.html
          
          
            "
          
          
            );
            filterContext.ExceptionHandled 
          
          = 
          
            true
          
          
            ;
        }
    }
}
          
        

這個Exception Filter通過重定向到Content目錄下的一個靜態html文件來顯示友好的 ArgumentOutOfRangeException 異常信息。我們定義的?RangeExceptionAttribute 類繼承了FilterAttribute類,并且實現了IException接口。作為一個MVC Filter,它的類必須實現IMvcFilter接口,你可以直接實現這個接口,但更簡單的方法是繼承?FilterAttribute 基類,該基類實現了一些必要的接口并提供了一些有用的基本特性,比如按照默認的順序來處理Filter。

在Content文件夾下面添加一個名為RangeErrorPage.html的文件用來顯示友好的錯誤信息。如下所示:

          
            <!
          
          
            DOCTYPE html
          
          
            >
          
          
            <
          
          
            html 
          
          
            xmlns
          
          
            ="http://www.w3.org/1999/xhtml"
          
          
            >
          
          
            <
          
          
            head
          
          
            >
          
          
            <
          
          
            title
          
          
            >
          
          Range Error
          
            </
          
          
            title
          
          
            >
          
          
            </
          
          
            head
          
          
            >
          
          
            <
          
          
            body
          
          
            >
          
          
            <
          
          
            h2
          
          
            >
          
          Sorry
          
            </
          
          
            h2
          
          
            >
          
          
            <
          
          
            span
          
          
            >
          
          One of the arguments was out of the expected range.
          
            </
          
          
            span
          
          
            >
          
          
            </
          
          
            body
          
          
            >
          
          
            </
          
          
            html
          
          
            >
          
        

在HomeController中添加一個值越限時拋出異常的action,如下所示:

          
            public
          
          
            class
          
          
             HomeController : Controller { 
        [RangeException]
        
          
          
            public
          
          
            string
          
           RangeTest(
          
            int
          
          
             id) { 
            
          
          
            if
          
           (id > 
          
            100
          
          
            ) { 
                
          
          
            return
          
           String.Format(
          
            "
          
          
            The id value is: {0}
          
          
            "
          
          
            , id); 
            } 
          
          
            else
          
          
             { 
                
          
          
            throw
          
          
            new
          
           ArgumentOutOfRangeException(
          
            "
          
          
            id
          
          
            "
          
          , id, 
          
            ""
          
          
            ); 
            } 
        } 
    } 
          
        

當對RangeTest應用自定義的的Exception Filter時,運行程序URL請求為?/Home/RangeTest/50,程序拋出異常后將重定向到RangeErrorPage.html頁面:

MVC Model Binder_第3張圖片

由于靜態的html文件是和后臺脫離的,所以實際項目中更多的是用一個View來呈現友好的錯誤信息,以便很好的對它進行一些動態的控制。如下面把示例改動一下,RangeExceptionAttribute 類修改如下:

          
            public
          
          
            class
          
          
             RangeExceptionAttribute : FilterAttribute, IExceptionFilter {
        
          
          
            public
          
          
            void
          
          
             OnException(ExceptionContext filterContext) {
            
          
          
            if
          
           (!filterContext.ExceptionHandled && filterContext.Exception 
          
            is
          
          
             ArgumentOutOfRangeException) {
                
          
          
            
              int
            
             val = (
            
              int
            
            
              )(((ArgumentOutOfRangeException)filterContext.Exception).ActualValue); filterContext.Result 
            
            = 
            
              new
            
            
               ViewResult { ViewName 
            
            = 
            
              "
            
            
              RangeError
            
            
              "
            
            
              , ViewData 
            
            = 
            
              new
            
             ViewDataDictionary<
            
              int
            
            >
          
          
            
              (val) };
            
            
                filterContext.ExceptionHandled 
          
          = 
          
            true
          
          
            ;
            }
        }
    }
          
        

我們創建一個ViewResult對象,指定了發生異常時要重定向的View名稱和傳遞的model對象。然后我們在Views/Shared文件夾下添加一個RangeError.cshtml文件,代碼如下:

          
            @model int


          
          
            <!
          
          
            DOCTYPE html
          
          
            >
          
          
            <
          
          
            html
          
          
            >
          
          
            <
          
          
            head
          
          
            >
          
          
            <
          
          
            meta 
          
          
            name
          
          
            ="viewport"
          
          
             content
          
          
            ="width=device-width"
          
          
            />
          
          
            <
          
          
            title
          
          
            >
          
          Range Error
          
            </
          
          
            title
          
          
            >
          
          
            </
          
          
            head
          
          
            >
          
          
            <
          
          
            body
          
          
            >
          
          
            <
          
          
            h2
          
          
            >
          
          Sorry
          
            </
          
          
            h2
          
          
            >
          
          
            <
          
          
            span
          
          
            >
          
          The value @Model was out of the expected range.
          
            </
          
          
            span
          
          
            >
          
          
            <
          
          
            div
          
          
            >
          
          
             
        @Html.ActionLink("Change value and try again", "Index") 
    
          
          
            </
          
          
            div
          
          
            >
          
          
            </
          
          
            body
          
          
            >
          
          
            </
          
          
            html
          
          
            >
          
        

運行結果如下:

MVC Model Binder_第4張圖片

禁用異常跟蹤

很多時候異常是不可預料的,在每個Action方法或Controller上應用Exception Filter是不現實的。而且如果異常出現在View中也無法應用Filter。如RangeError.cshtml這個View加入下面代碼:

          
            @model int

@{ 
    var count = 0; 
    var number = Model / count; 
} 

...
          
        

運行程序后,將會顯示如下信息:

MVC Model Binder_第5張圖片

顯然程序發布后不應該顯示這些信息給用戶看。我們可以通過配置Web.config讓應用程序不管在何時何地引發了異常都可以顯示統一的友好錯誤信息。在Web.config文件中的<system.web>節點下添加如下子節點:

          
            <
          
          
            system.web
          
          
            >
          
          
            
    
    ...
    
          
          
            <
          
          
            customErrors 
          
          
            mode
          
          
            ="On"
          
          
             defaultRedirect
          
          
            ="/Content/RangeErrorPage.html"
          
          
            />
          
          
            </
          
          
            system.web
          
          
            >
          
        

這個配置只對遠程訪問有效,本地運行站點依然會顯示跟蹤信息。

使用內置的 Exceptin Filter

通過上面的演示,我們理解了Exceptin Filter在MVC背后是如何運行的。但我們并不會經常去創建自己的Exceptin Filter,因為微軟在MVC框架中內置的 HandleErrorAttribute(實現了IExceptionFilter接口) 已經足夠我們平時使用。它包含ExceptionType、View和Master三個屬性。當ExceptionType屬性指定類型的異常被引發時,這個Filter將用View屬性指定的View(使用默認的Layout或Mast屬性指定的Layout)來呈現一個頁面。如下面代碼所示:

          
            ... 
[HandleError(ExceptionType 
          
          = 
          
            typeof
          
          (ArgumentOutOfRangeException), View = 
          
            "
          
          
            RangeError
          
          
            "
          
          
            )] 

          
          
            public
          
          
            string
          
           RangeTest(
          
            int
          
          
             id) { 
    
          
          
            if
          
           (id > 
          
            100
          
          
            ) { 
        
          
          
            return
          
           String.Format(
          
            "
          
          
            The id value is: {0}
          
          
            "
          
          
            , id); 
    } 
          
          
            else
          
          
             { 
        
          
          
            throw
          
          
            new
          
           ArgumentOutOfRangeException(
          
            "
          
          
            id
          
          
            "
          
          , id, 
          
            ""
          
          
            ); 
    } 
} 
... 
          
        

使用內置的HandleErrorAttribute,將異常信息呈現到View時,這個特性同時會傳遞一個HandleErrorInfo對象作為View的model。HandleErrorInfo類包含ActionName、ControllerName和Exception屬性,如下面的 RangeError.cshtml 使用這個model來呈現信息:

          
            @model HandleErrorInfo 
@{ 
    ViewBag.Title = "Sorry, there was a problem!"; 
} 
 

          
          
            <!
          
          
            DOCTYPE html
          
          
            >
          
          
            <
          
          
            html
          
          
            >
          
          
            <
          
          
            head
          
          
            >
          
          
            <
          
          
            meta 
          
          
            name
          
          
            ="viewport"
          
          
             content
          
          
            ="width=device-width"
          
          
            />
          
          
            <
          
          
            title
          
          
            >
          
          Range Error
          
            </
          
          
            title
          
          
            >
          
          
            </
          
          
            head
          
          
            >
          
          
            <
          
          
            body
          
          
            >
          
          
            <
          
          
            h2
          
          
            >
          
          Sorry
          
            </
          
          
            h2
          
          
            >
          
          
            <
          
          
            span
          
          
            >
          
          
            The value @(((ArgumentOutOfRangeException)Model.Exception).ActualValue) 
        was out of the expected range.
          
          
            </
          
          
            span
          
          
            >
          
          
            <
          
          
            div
          
          
            >
          
          
             
        @Html.ActionLink("Change value and try again", "Index") 
    
          
          
            </
          
          
            div
          
          
            >
          
          
            <
          
          
            div 
          
          
            style
          
          
            ="display: none"
          
          
            >
          
          
             
        @Model.Exception.StackTrace 
    
          
          
            </
          
          
            div
          
          
            >
          
          
            </
          
          
            body
          
          
            >
          
          
            </
          
          
            html
          
          
            >
          
        

Action Filter

顧名思義,Action Filter是對action方法的執行進行“篩選”的,包括執行前和執行后。它需要實現?IActionFilter 接口,該接口定義如下:

          
            namespace
          
          
             System.Web.Mvc { 
    
          
          
            public
          
          
            interface
          
          
             IActionFilter { 
        
          
          
            void
          
          
             OnActionExecuting(ActionExecutingContext filterContext); 
        
          
          
            void
          
          
             OnActionExecuted(ActionExecutedContext filterContext); 
    } 
}
          
        

其中,OnActionExecuting方法在action方法執行之前被調用,OnActionExecuted方法在action方法執行之后被調用。我們來看一個簡單的例子。
在Infrastructure文件夾下添加一個ProfileActionAttribute類,代碼如下:

          
            using
          
          
             System.Diagnostics; 

          
          
            using
          
          
             System.Web.Mvc; 


          
          
            namespace
          
          
             MvcApplication1.Infrastructure { 
    
          
          
            public
          
          
            class
          
          
             ProfileActionAttribute : FilterAttribute, IActionFilter { 
        
          
          
            private
          
          
             Stopwatch timer; 
        
          
          
            public
          
          
            void
          
          
             OnActionExecuting(ActionExecutingContext filterContext) { 
            timer 
          
          =
          
             Stopwatch.StartNew(); 
        } 
        
          
          
            public
          
          
            void
          
          
             OnActionExecuted(ActionExecutedContext filterContext) { 
            timer.Stop();
            
          
          
            if
          
           (filterContext.Exception == 
          
            null
          
          
            ) { 
                filterContext.HttpContext.Response.Write( 
                    
          
          
            string
          
          .Format(
          
            "
          
          
            <div>Action method elapsed time: {0}</div>
          
          
            "
          
          
            , timer.Elapsed.TotalSeconds)); 
            } 
        } 
    } 
} 
          
        

在HomeController中添加一個Action并應用該Filter,如下:

          
            ... 
[ProfileAction] 

          
          
            public
          
          
            string
          
          
             FilterTest() { 
    
          
          
            return
          
          
            "
          
          
            This is the ActionFilterTest action
          
          
            "
          
          
            ; 
} 
...
          
        

運行程序,URL定位到/Home/FilterTest,結果如下:

MVC Model Binder_第6張圖片

我們看到,ProfileAction的?OnActionExecuted 方法是在?FilterTest 方法返回結果之前執行的。確切的說,OnActionExecuted 方法是在action方法執行結束之后和處理action返回結果之前執行的。

OnActionExecuting方法和OnActionExecuted方法分別接受ActionExecutingContext和ActionExecutedContext對象參數,這兩個參數包含了ActionDescriptor、Canceled、Exception等常用屬性。

Result Filter

Result Filter用來處理action方法返回的結果。用法和Action Filter類似,它需要實現?IResultFilter?接口,定義如下:

          
            namespace
          
          
             System.Web.Mvc { 
    
          
          
            public
          
          
            interface
          
          
             IResultFilter { 
        
          
          
            void
          
          
             OnResultExecuting(ResultExecutingContext filterContext); 
        
          
          
            void
          
          
             OnResultExecuted(ResultExecutedContext filterContext); 
    } 
} 
          
        

IResultFilter?接口和之前的?IActionFilter 接口類似,要注意的是Result Filter是在Action Filter之后執行的。兩者用法是一樣的,不再多講,直接給出示例代碼。

在Infrastructure文件夾下再添加一個?ProfileResultAttribute.cs 類文件,代碼如下:

          
            public
          
          
            class
          
          
             ProfileResultAttribute : FilterAttribute, IResultFilter { 
    
          
          
            private
          
          
             Stopwatch timer; 
    
          
          
            public
          
          
            void
          
          
             OnResultExecuting(ResultExecutingContext filterContext) { 
        timer 
          
          =
          
             Stopwatch.StartNew(); 
    } 
    
          
          
            public
          
          
            void
          
          
             OnResultExecuted(ResultExecutedContext filterContext) { 
        timer.Stop(); 
        filterContext.HttpContext.Response.Write( 
            
          
          
            string
          
          .Format(
          
            "
          
          
            <div>Result elapsed time: {0}</div>
          
          
            "
          
          
            ,  timer.Elapsed.TotalSeconds)); 
    } 
}
          
        

應用該Filter:

          
            ... 
[ProfileAction] 

            
              [ProfileResult]
            
          
          
            public
          
          
            string
          
          
             FilterTest() { 
    
          
          
            return
          
          
            "
          
          
            This is the ActionFilterTest action
          
          
            "
          
          
            ; 
} 
...
          
        

內置的 Action 和 Result Filter

MVC框架內置了一個?ActionFilterAttribute 類用來創建action 和 result 篩選器,即可以控制action方法的執行也可以控制處理action方法返回結果。它是一個抽象類,定義如下:

          
            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 和?IResultFilter?接口沒什么不同。下面是簡單做個示例。

在Infrastructure文件夾下添加一個?ProfileAllAttribute.cs 類文件,代碼如下:

          
            public
          
          
            class
          
          
             ProfileAllAttribute : ActionFilterAttribute { 
    
          
          
            private
          
          
             Stopwatch timer; 
    
          
          
            public
          
          
            override
          
          
            void
          
          
             OnActionExecuting(ActionExecutingContext filterContext) { 
        timer 
          
          =
          
             Stopwatch.StartNew(); 
    } 
    
          
          
            public
          
          
            override
          
          
            void
          
          
             OnResultExecuted(ResultExecutedContext filterContext) { 
        timer.Stop(); 
        filterContext.HttpContext.Response.Write(
        
          
          
            string
          
          .Format(
          
            "
          
          
            <div>Total elapsed time: {0}</div>
          
          
            "
          
          
            ,  timer.Elapsed.TotalSeconds)); 
    } 
}
          
        

在HomeController中的FilterTest方法上應用該Filter:

          
            ... 
[ProfileAction] 
[ProfileResult] 

            
              [ProfileAll] 
            
          
          
            public
          
          
            string
          
          
             FilterTest() { 
    
          
          
            return
          
          
            "
          
          
            This is the FilterTest action
          
          
            "
          
          
            ; 
} 
...
          
        

運行程序,URL定位到/Home/FilterTest,可以看到一個Action從執行之前到結果處理完畢總共花的時間:

MVC Model Binder_第7張圖片

我們也可以Controller中直接重寫?ActionFilterAttribute 抽象類中定義的四個方法,效果和使用Filter是一樣的,例如:

          
            public
          
          
            class
          
          
             HomeController : Controller { 
    
          
          
            private
          
          
             Stopwatch timer; 
    ...
    
          
          
            public
          
          
            string
          
          
             FilterTest() { 
        
          
          
            return
          
          
            "
          
          
            This is the FilterTest action
          
          
            "
          
          
            ; 
    } 
    
          
          
            protected
          
          
            override
          
          
            void
          
          
             OnActionExecuting(ActionExecutingContext filterContext) { 
        timer 
          
          =
          
             Stopwatch.StartNew(); 
    } 
    
          
          
            protected
          
          
            override
          
          
            void
          
          
             OnResultExecuted(ResultExecutedContext filterContext) { 
        timer.Stop(); 
        filterContext.HttpContext.Response.Write( 
            
          
          
            string
          
          .Format(
          
            "
          
          
            <div>Total elapsed time: {0}</div>
          
          
            "
          
          
            , 
            timer.Elapsed.TotalSeconds)); 
    } 
} 
          
        

注冊為全局 Filter

全局Filter對整個應用程序的所有controller下的所有action方法有效。在App_Start/FilterConfig.cs文件中的RegisterGlobalFilters方法,可以把一個Filter類注冊為全局,如:

          
            using
          
          
             System.Web; 

          
          
            using
          
          
             System.Web.Mvc; 

          
          
            using
          
          
             MvcApplication1.Infrastructure; 
 

          
          
            namespace
          
          
             MvcApplication1 { 
    
          
          
            public
          
          
            class
          
          
             FilterConfig { 
        
          
          
            public
          
          
            static
          
          
            void
          
          
             RegisterGlobalFilters(GlobalFilterCollection filters) { 
            filters.Add(
          
          
            new
          
          
             HandleErrorAttribute()); 
            
            
              filters.Add(
            
          
          
            
              new
            
          
          
            
               ProfileAllAttribute());
            
             
        } 
    } 
}
          
        

我們增加了filters.Add(new ProfileAllAttribute())這行代碼,其中的filters參數是一個GlobalFilterCollection類型的集合。為了驗證?ProfileAllAttribute 應用到了所有action,我們另外新建一個controller并添加一個簡單的action,如下:

          
            public
          
          
            class
          
          
             CustomerController : Controller { 
        
          
          
            public
          
          
            string
          
          
             Index() { 
            
          
          
            return
          
          
            "
          
          
            This is the Customer controller
          
          
            "
          
          
            ; 
        } 
}
          
        

運行程序,將URL定位到?/Customer ,結果如下:

MVC Model Binder_第8張圖片

其它常用 Filter

MVC框架內置了很多Filter,常見的有RequireHttps、OutputCache、AsyncTimeout等等。下面例舉幾個常用的。

  • RequireHttps,強制使用HTTPS協議訪問。它將瀏覽器的請求重定向到相同的controller和action,并加上?https:// 前綴。
  • OutputCache,將action方法的輸出內容進行緩存。
  • AsyncTimeout/NoAsyncTimeout,用于異步Controller的超時設置。(異步Controller的內容請訪問 xxxxxxxxxxxxxxxxxxxxxxxxxxx)
  • ChildActionOnlyAttribute,使用action方法僅能被Html.Action和Html.RenderAction方法訪問。

這里我們選擇?OutputCache 這個Filter來做個示例。新建一個?SelectiveCache controller,代碼如下:

          
            public
          
          
            class
          
          
             SelectiveCacheController : Controller {
    
          
          
            public
          
          
             ActionResult Index() { 
        Response.Write(
          
          
            "
          
          
            Action method is running: 
          
          
            "
          
           +
          
             DateTime.Now); 
        
          
          
            return
          
          
             View(); 
    } 

    [OutputCache(Duration 
          
          = 
          
            30
          
          
            )] 
    
          
          
            public
          
          
             ActionResult ChildAction() { 
        Response.Write(
          
          
            "
          
          
            Child action method is running: 
          
          
            "
          
           +
          
             DateTime.Now); 
        
          
          
            return
          
          
             View(); 
    } 
}
          
        

這里的?ChildAction 應用了?OutputCache filter,這個action將在view內被調用,它的父action是Index。

現在我們分別創建兩個View,一個是ChildAction.cshtml,代碼如下:

          
            @{ 
    Layout = null; 
} 
 

          
          
            <
          
          
            h4
          
          
            >
          
          This is the child action view
          
            </
          
          
            h4
          
          
            >
          
        

另一個是它的Index.cshtml,代碼如下:

          
            @{ 
    ViewBag.Title = "Index"; 
} 
 

          
          
            <
          
          
            h2
          
          
            >
          
          This is the main action view
          
            </
          
          
            h2
          
          
            >
          
          
             
 
@Html.Action("ChildAction")
          
        

運行程序,將URL定位到??/SelectiveCache ,過幾秒刷新一下,可看到如下結果:

MVC Model Binder_第9張圖片 ? MVC Model Binder_第10張圖片

?


參考: 《Pro ASP.NET MVC 4 4th Edition》

這篇博客是借助一個自己寫的工程來理解model binder的過程.


MVC通過路由系統,根據url找到對應的Action,然后再執行action,在執行action的時候,根據action的參數和數據來源比對,生成各個參數的值,這就是model binder.

IActionInvoker


MVC中這個核心處理邏輯都在ControllerActionInvoker里,用reflector看,能看能到這個類繼承了IActionInvoker接口

            
              1
            
            
              public
            
            
              interface
            
            
               IActionInvoker

            
            
              2
            
            
                  {

            
            
              3
            
            
              bool
            
             InvokeAction(ControllerContext controllerContext, 
            
              string
            
            
               actionName);

            
            
              4
            
                 }
          

所以咱們可以根據代碼模擬寫出自己的CustomActionInvoker

以下是我自己寫的ActionInvoker類

            
               1
            
            
              public
            
            
              class
            
            
               CustomActionInvoker : IActionInvoker

            
            
               2
            
            
                  {

            
            
               3
            
            
               4
            
            
              public
            
            
              bool
            
             InvokeAction(ControllerContext controllerContext, 
            
              string
            
            
               actionName)

            
            
               5
            
            
                      {

            
            
               6
            
            
              bool
            
             flag = 
            
              false
            
            
              ;

            
            
               7
            
            
              try
            
            
               8
            
            
                          {

            
            
               9
            
            
              //
            
            
              get controller type
            
            
              10
            
                             Type controllerType =
            
               controllerContext.Controller.GetType();

            
            
              11
            
            
              //
            
            
              get controller descriptor
            
            
              12
            
                             ControllerDescriptor controllerDescriptor = 
            
new ReflectedControllerDescriptor(controllerType); 13 // get action descriptor 14 ActionDescriptor actionDescriptor =
controllerDescriptor.FindAction(controllerContext, actionName);
15 Dictionary< string , object > parameters =

new Dictionary< string , object > (StringComparer.OrdinalIgnoreCase); 16 // get parameter-value entity 17 foreach (ParameterDescriptor parameterDescriptor in actionDescriptor.GetParameters()) 18 { 19 Type parameterType = parameterDescriptor.ParameterType; 20 // get model binder 21 IModelBinder modelBinder = new CustomModelBinder(); 22 IValueProvider valueProvider = controllerContext.Controller.ValueProvider; 23 string str = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName; 24 ModelBindingContext bindingContext = new ModelBindingContext(); 25 bindingContext.FallbackToEmptyPrefix = parameterDescriptor.BindingInfo.Prefix == null ; 26 bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType( null , parameterType); 27 bindingContext.ModelName = str; 28 bindingContext.ModelState = controllerContext.Controller.ViewData.ModelState; 29 bindingContext.ValueProvider = valueProvider; 30 parameters.Add(parameterDescriptor.ParameterName,
modelBinder.BindModel(controllerContext, bindingContext));
31 } 32 ActionResult result = (ActionResult)actionDescriptor.Execute(controllerContext, parameters); 33 result.ExecuteResult(controllerContext); 34 flag = true ; 35 } 36 catch (Exception ex) 37 { 38 // log 39 } 40 return flag; 41 } 42 }

以下詳細解釋下執行過程

*Descriptor


執行過程中涉及到三個Descriptor,ControllerDescriptor,ActionDescriptor,ParameterDescriptor

ControllerDescriptor主要作用是根據action name獲取到ActionDescriptor,代碼中使用的是MVC自帶的ReflectedControllerDescriptor,從名字就可以看出來,主要是靠反射獲取到action.

ActionDescriptor,主要作用是獲取parameterDescriptor,然后execute action.

parameterDescriptor,描述的是action的參數信息,包括name、type等

ModelBinder


最核心的方法. 將傳遞的數據和參數一一對應,筆者是自己寫的CustomModelBinder,MVC默認用的是DefaultModelBinder 都實現了接口IModelBinder

            
              1
            
            
              public
            
            
              interface
            
            
               IModelBinder

            
            
              2
            
            
                  {

            
            
              3
            
            
              object
            
            
               BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext);

            
            
              4
            
                 }
          

其中CustomModelBinder的代碼如下

            
               1
            
            
              public
            
            
              class
            
            
               CustomModelBinder : IModelBinder

            
            
               2
            
            
                  {

            
            
               3
            
            
               4
            
            
              public
            
            
              object
            
            
               BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

            
            
               5
            
            
                      {

            
            
               6
            
            
              return
            
            
              this
            
            
              .GetModel(controllerContext, bindingContext.ModelType, bindingContext.ValueProvider, bindingContext.ModelName);

            
            
               7
            
            
                      }

            
            
               8
            
            
               9
            
            
              public
            
            
              object
            
             GetModel(ControllerContext controllerContext, Type modelType, IValueProvider valueProvider, 
            
              string
            
            
               key)

            
            
              10
            
            
                      {

            
            
              11
            
            
              if
            
             (!
            
              valueProvider.ContainsPrefix(key))

            
            
              12
            
            
                          {

            
            
              13
            
            
              return
            
            
              null
            
            
              ;

            
            
              14
            
            
                          }

            
            
              15
            
            
              return
            
            
               valueProvider.GetValue(key).ConvertTo(modelType);

            
            
              16
            
            
                      }

            
            
              17
            
                 }
          

注:我只是實現了簡單的基本類型

中間有最核心的方法

            valueProvider.GetValue(key).ConvertTo(modelType)
          

ValueProvider


MVC默認提供了幾種ValueProvider,每種都有對應的ValueProviderFactory,每種ValueProvider都對應著自己的數據源

            
              1
            
                 ValueProviderFactoryCollection factorys = 
            
              new
            
            
               ValueProviderFactoryCollection();

            
            
              2
            
                 factorys.Add(
            
              new
            
            
               ChildActionValueProviderFactory());

            
            
              3
            
                 factorys.Add(
            
              new
            
            
               FormValueProviderFactory());

            
            
              4
            
                 factorys.Add(
            
              new
            
            
               JsonValueProviderFactory());

            
            
              5
            
                 factorys.Add(
            
              new
            
            
               RouteDataValueProviderFactory());

            
            
              6
            
                 factorys.Add(
            
              new
            
            
               QueryStringValueProviderFactory());

            
            
              7
            
                 factorys.Add(
            
              new
            
             HttpFileCollectionValueProviderFactory());
          

注冊ActionInvoker


上述過程講完之后,還缺一個怎么應用上自己寫的ActionInvoker,在Controller里提供了虛方法CreateActionInvoker

            
              1
            
            
              protected
            
            
              override
            
            
               IActionInvoker CreateActionInvoker()

            
            
              2
            
            
                      {

            
            
              3
            
            
              return
            
            
              new
            
            
               CustomActionInvoker();

            
            
              4
            
                     }
          

到此,整個過程已講完。

?
?
分類:? Asp.Net

MVC Model Binder


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。?!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 手机在线观看亚洲国产精品 | 精品乱人伦一区二区 | 中文在线视频 | 欧美又乱又伦观看 | 国产美女一级高清免费观看 | 成年女人aaaaa毛片 | 国产精品入口麻豆高清在线 | 一级做a毛片免费视频 | 亚洲日韩欧美综合 | 日本一级毛片免费播放 | 亚洲欧美国产一区二区三区 | 播放一级毛片 | 911精品国产91久久久久 | 中文字幕在线观看不卡视频 | 国产精亚洲视频 | 神马影院我不卡手机 | 国产一级爱c片免费播放 | 久久久不卡国产精品一区二区 | 免费中文字幕一级毛片 | 日本人成年视频在线观看 | 奇米影视777888 | 日本视频播放免费线上观看 | 亚洲国产精| 国产精品亚洲综合一区在线观看 | 天堂一区二区三区在线观看 | 亚洲国产精品自产拍在线播放 | 天码毛片一区二区三区入口 | 日本一区二区三区四区公司 | 久久久亚洲欧洲日产国码二区 | 伊人丁香狠狠色综合久久 | 五月婷婷亚洲综合 | 亚洲综合中文 | 四虎影库网址 | 激情五月婷婷在线 | 91福利一区二区在线观看 | 国产成人精品久久二区二区 | 日本在线观看中文字幕 | 久久精品一区二区免费看 | 九热视频 | 久久激情视频 | 伊人久久综合谁合综合久久 |