使用動作篩選條件- MVC5

by vivid 20. 五月 2015 04:03

.NET Magazine國際中文電子雜誌
者:許薰尹
稿:張智凱
文章編號:N150516002
出刊日期:2015/05/20
開發工具:Visual Studio 2013 Ultimate Update 4
版本:.NET Framework 4.5.xASP.NET MVC5

動作篩選條件(Action Filter)可以在控制器(Controller)的動作方法(Action Method)執行之前、或之後,以及結果(Action Result)送出之前、之後,來進行自訂的前置動作或後置處理。MVC內建許多的動作篩選條件,有些用來進行輸出快取(Output Cache),有些用在驗證、授權、或錯誤處理。

本篇文章說明如何撰寫動作篩選條件(Action Filter),以便在呼叫控制器的動作方法(Action Method)之前,或是動作方法執行之後執行一些前置作業或後置邏輯。

篩選條件是一個自訂的Attribute類別,可以使用宣告式或程式設計方式來進行套用,可以套用在動作方法(Action Method)、控制器(Controller)或網站等級。

 

建立ASP.NET MVC 5應用程式專案

我們從建立一個ASP.NET MVC5應用程式專案開始。從Visual Studio 2013開發工具 -「File」-「New」-「Project」,在「New Project」對話盒中,確認視窗上方.NET Framework的目標版本為「.NET Framework 4.5.1」,選取左方「Installed」-「Template」-「Visual C#」程式語言,從「Web」分類中,選取「ASP.NET Web Application」。本例設定專案名稱為「ActionFilterDemo」,按下「OK」鍵,請參考下圖所示:

clip_image002

圖 1:建立一個ASP.NET MVC5應用程式專案。

在「New ASP.NET Project」對話盒中選取「Empty」項目,勾選下方的「MVC」項目,然後按下「OK」按鈕建立專案,請參考下圖所示:

clip_image004

圖 2:選取「Empty」的「MVC」項目。

從「Solution Explorer」視窗- 專案 -「Controllers」資料夾上方按滑鼠右鍵,從快捷選單選擇「Add」- 「Controller」,開啟「Add Scaffold」對話盒,選取「MVC 5 Controller - Empty」,然後按下「Add」按鈕,請參考下圖所示:

clip_image006

圖 3:選取「MVC 5 Controller -Empty」項目。

在「Add Controller」對話盒中,設定控制器名稱為「HomeController」;然後按下「Add 」按鈕,請參考下圖所示:

clip_image008

圖 4:加入HomeController。

目前Index動作程式碼如下,叫用控制器的View方法回傳檢視:

public ActionResult Index( ) {
  return View( );
}

修改HomeController類別程式碼,在程式檔案最上方,引用命名空間:

using System.Diagnostics;

在Index方法中,使用Debug類別的WriteLine方法輸出「Home.Index」訊息:

public ActionResult Index( ) {
  Debug.WriteLine( "Home.Index" );
  return new EmptyResult( );
}

選取「Build」-「Build Solution」編譯目前的專案,確認程式碼能正確編譯。按F5以除錯模式執行網站,執行結果參考如下,檢視Visual Studio 2013的「Output」視窗,將印出「Home.Index」字串:

clip_image010

圖 5:輸出除錯訊息到Output視窗。

結束執行程式,回到Visual Studio 開發工具程式編輯畫面。

 

定義MyLogActionFilter類別

接下來我們來談談如何撰寫Action Filter。從Visual Studio 2013開發工具-「Solution Explorer」- 專案根資料夾上方按滑鼠右鍵,從快捷選單選擇「Add」- 「New Item」,開啟「Add New Item」對話盒,從右上方文字方塊輸入「Class」搜尋,選取Class,設定名稱為「MyLogActionFilter」,然後按下「Add」按鈕,建立新類別,請參考下圖所示:

clip_image012

圖 6:加入新類別。

修改MyLogActionFilter.cs檔案中的程式碼,在程式檔案最上方,引用System.Web.Mvc、System.Diagnostics命名空間:

 

using System.Web.Mvc;
using System.Diagnostics;

修改MyLogActionFilter類別中的程式碼,讓MyLogActionFilter類別繼承ValidationAttribute類別:

 

public class MyLogActionFilter : ActionFilterAttribute {
}

在MyLogActionFilter類別中,改寫OnActionExecuting方法如下,印出一個字串:

 

public override void OnActionExecuting( ActionExecutingContext filterContext ) {
  Debug.WriteLine( "觸發事件: OnActionExecuting" );
}

在MyLogActionFilter類別中,改寫OnActionExecuted方法如下,印出一個字串:

 

public override void OnActionExecuted( ActionExecutedContext filterContext ) {
  Debug.WriteLine( "觸發事件: OnActionExecuted" );
}

在MyLogActionFilter類別中,改寫OnResultExecuting方法如下,印出一個字串:

public override void OnResultExecuting( ResultExecutingContext filterContext ) {
  Debug.WriteLine( "觸發事件: OnResultExecuting" );
}

在MyLogActionFilter類別中,改寫OnResultExecuted方法如下,印出一個字串:

public override void OnResultExecuted( ResultExecutedContext filterContext ) {
  Debug.WriteLine( "觸發事件: OnResultExecuted" );
}

目前MyLogActionFilter 類別的程式碼看起來如下:

 

public class MyLogActionFilter : ActionFilterAttribute {
  public override void OnActionExecuting( ActionExecutingContext filterContext ) {
    Debug.WriteLine( "觸發事件: OnActionExecuting" );
  }
  public override void OnActionExecuted( ActionExecutedContext filterContext ) {
    Debug.WriteLine( "觸發事件: OnActionExecuted" );
  }
  public override void OnResultExecuting( ResultExecutingContext filterContext ) {
    Debug.WriteLine( "觸發事件: OnResultExecuting" );
  }
  public override void OnResultExecuted( ResultExecutedContext filterContext ) {
    Debug.WriteLine( "觸發事件: OnResultExecuted" );
  }
}

 

在動作方法套用動作篩選條件

你可以使用Attribute語法,在動作方法(Action Method)套用動作篩選條件(Action Filter),修改HomeController類別,在Index方法上方套用MyLogActionFilter:

public class HomeController : Controller {
  [MyLogActionFilter]
  public ActionResult Index( ) {
    Debug.WriteLine( "Home.Index" );
    return new EmptyResult( );
  }
}

選取「Build」-「Build Solution」編譯目前的專案,確認程式碼能正確編譯。按F5以除錯模式執行網站,執行結果參考如下,檢視Visual Studio 2013的「Output」視窗,將印出相關的除錯字串:

clip_image014

圖 7:印出相關的除錯字串。

從執行結果可以看出,在Home Index方法執行之前,會先觸發OnActionExecuting事件,Home Index方法執行之後則觸發OnActionExecuted事件。停止程式執行,回到Visual Studio 2013設計畫面。

 

使用ActionExecutingContext讀取路由資訊

觸發OnActionExecuting與OnActionExecuted事件時,ASP.NET會傳入ActionExecutingContext或ResultExecutingContext物件到事件處理常式之中,我們可以利用這些物件來取得路由物件,其中包含控制器以及動作方法資訊。

修改MyLogActionFilter.cs檔案中的程式碼,在程式檔案最上方,引用System.Web.Routing命名空間:

using System.Web.Routing;

在MyLogActionFilter類別之中,加入一個Log方法,利用路由資料讀取控制器與及動作方法資訊儲存到變數之中,並將這些資訊利用Debug類別的WriteLine方法寫到輸Visual Studio的輸出視窗:

private void Log( string methodName , RouteData routeDate ) {
  var controllerName = routeDate.Values[ "controller" ];
  var actionName = routeDate.Values[ "action" ];
  var message = string.Format( "{0} controller:{1}, action:{2}" ,
      methodName , controllerName , actionName );
  Debug.WriteLine( message );
}

修改OnActionExecuting方法,註解方法中原有的程式碼,改成叫用Log方法輸出除錯資訊:

public override void OnActionExecuting( ActionExecutingContext filterContext ) {
  Log( "OnActionExecuting" , filterContext.RouteData );
}

修改OnActionExecuted方法,註解方法中原有的程式碼,改成叫用Log方法輸出除錯資訊:

public override void OnActionExecuted( ActionExecutedContext filterContext ) {
  Log( "OnActionExecuted" , filterContext.RouteData );
}

修改OnResultExecuting方法,註解方法中原有的程式碼,改成叫用Log方法輸出除錯資訊:

public override void OnResultExecuting( ResultExecutingContext filterContext ) {
  Log( "OnResultExecuting" , filterContext.RouteData );
}

修改OnResultExecuted方法,註解方法中原有的程式碼,改成叫用Log方法輸出除錯資訊:


public override void OnResultExecuted( ResultExecutedContext filterContext ) {
  Log( "OnResultExecuted" , filterContext.RouteData );
}

目前MyLogActionFilter類別的程式碼看起來如下:

public class MyLogActionFilter : ActionFilterAttribute {
  public override void OnActionExecuting( ActionExecutingContext filterContext ) {
    Log( "OnActionExecuting" , filterContext.RouteData );
  }
  public override void OnActionExecuted( ActionExecutedContext filterContext ) {
    Log( "OnActionExecuted" , filterContext.RouteData );
  }
  public override void OnResultExecuting( ResultExecutingContext filterContext ) {
    Log( "OnResultExecuting" , filterContext.RouteData );
  }
  public override void OnResultExecuted( ResultExecutedContext filterContext ) {
    Log( "OnResultExecuted" , filterContext.RouteData );
  }

  private void Log( string methodName , RouteData routeDate ) {
    var controllerName = routeDate.Values[ "controller" ];
    var actionName = routeDate.Values[ "action" ];
    var message = string.Format( "{0} controller:{1}, action:{2}" ,
        methodName , controllerName , actionName );
    Debug.WriteLine( message );
  }

}

 

選取「Build」-「Build Solution」編譯目前的專案,確認程式碼能正確編譯。按F5以除錯模式執行網站,執行結果如下圖所示。

clip_image016

圖 8:輸出路由資訊。

套用在控制器層級

動作篩選條件(Action Filter)可以套用在動作方法(Action Method)、控制器,或網站等級。前兩種使用Attribute與法套用。而網站等級要寫一小段程式碼。若要在類別階層套用動作篩選條件,可以註解HomeController類別中Index方法上方套用的MyLogActionFilter Attribute,然後在HomeController類別上方套用:

namespace ActionFilterDemo.Controllers {
  [MyLogActionFilter]
  public class HomeController : Controller {
   //[MyLogActionFilter]
    public ActionResult Index( ) {
      Debug.WriteLine( "Home.Index" );
      return new EmptyResult( );
    }
  }
}

選取「Build」-「Build Solution」編譯目前的專案,確認程式碼能正確編譯。按F5以除錯模式執行網站,執行結果和上例相同。

 

套用在網站應用程式層級

若在網站等級套用動作篩選條件(Action Filter),則網站中所有控制器的Action都會套用此行為。

註解HomeController類別上方套用的MyLogActionFilter Attribute:

namespace ActionFilterDemo.Controllers {
  //[MyLogActionFilter]
  public class HomeController : Controller {
      //[MyLogActionFilter]
public ActionResult Index( ) {
      Debug.WriteLine( "Home.Index" );
      return new EmptyResult( );
    }
  }
}

 

然後在Global.asax檔案中Application_Start方法的最後一行加上套用MyLogActionFilter程式碼,建立一個MyLogActionFilter物件,然後將它加入GlobalFilters.Filters集合之中:

namespace ActionFilterDemo {
  public class MvcApplication : System.Web.HttpApplication {
    protected void Application_Start( ) {
      AreaRegistration.RegisterAllAreas( );
      RouteConfig.RegisterRoutes( RouteTable.Routes );
      GlobalFilters.Filters.Add( new MyLogActionFilter( ) );
    }
  }
}

 

選取「Build」-「Build Solution」編譯目前的專案,確認程式碼能正確編譯。按F5以除錯模式執行網站,執行結果和上例相同。

 

MVC5的動作篩選條件改寫功能

在網站等級套用動作篩選條件(Action Filter),則網站中所有控制器的Action都會套用此行為,若有些動作方法(Action Method)不想套用此行為,可以使用MVC5 動作篩選條件改寫(Filter Override)功能來變更這個行為。

例如在HomeController加入一個About方法:

namespace ActionFilterDemo.Controllers {
  //[MyLogActionFilter]
  public class HomeController : Controller {
     //[MyLogActionFilter]
    public ActionResult Index( ) {
      Debug.WriteLine( "Home.Index" );
      return new EmptyResult( );
    }
    public ActionResult About( ) {
      Debug.WriteLine( "Home.About" );
      return new EmptyResult( );
    }
  }
}

 

則發出「/home/about」請求時,將輸出以下訊息,參考下圖所示:

clip_image018

圖 9:套用在網站層級。

讓我們在About方法上方套用OverrideActionFilters,則執行時便可以蓋掉 MyLogActionFilterAttribute的行為,不會輸出除錯資訊到輸出視窗:

namespace ActionFilterDemo.Controllers {
  //[MyLogActionFilter]
  public class HomeController : Controller {
     //[MyLogActionFilter]
    public ActionResult Index( ) {
      Debug.WriteLine( "Home.Index" );
      return new EmptyResult( );
    }
   [OverrideActionFilters]
    public ActionResult About( ) {
      Debug.WriteLine( "Home.About" );
      return new EmptyResult( );
    }
  }
}

 

執行結果如下,發出home/about請求時,不會觸發OnActionExecuted與OnResultExecuting事件,請參考下圖所示:

clip_image020

圖 10:輸出除錯訊息。

MVC 共有 5 種內建用來做動作篩選條件改寫(Filter Override)的Attribute:

  • OverrideActionFilters
  • OverrideAuthentication
  • OverrideAuthorization
  • OverrideExceptionFilters
  • OverrideResultFilters

修改程式碼,在About方法套用OverrideResultFilters Attribute:

namespace ActionFilterDemo.Controllers {
  //[MyLogActionFilter]
  public class HomeController : Controller {
     //[MyLogActionFilter]
    public ActionResult Index( ) {
      Debug.WriteLine( "Home.Index" );
      return new EmptyResult( );
    }
    [OverrideActionFilters]
   [OverrideResultFilters]
    public ActionResult About( ) {
      Debug.WriteLine( "Home.About" );
      return new EmptyResult( );
    }
  }
}

 

則執行結果如下,發出home/about請求時,不會觸發OnActionExecuted與OnResultExecuting、OnResultExecuting與OnResultExecuted事件,請參考下圖所示:

clip_image022

圖 11:動作篩選條件改寫功能。

自訂動作篩選條件改寫

你也可以自行撰寫自己的動作篩選條件改寫(Filter Override)功能,例如以下自訂一個OverrideLogActionFiltersAttribute ,實做IOverrideFilter 介面,讓FiltersToOverride 屬性回傳欲改寫的型別:

在MyLogActionFilter.cs檔案上方加入以下命名空間:

using System.Web.Mvc.Filters;

加入以下OverrideMyLogActionFiltersAttribute 類別,繼層FilterAttribute 類別,並實作IOverrideFilter 介面,然後改寫FiltersToOverride 方法,回傳IActionFilter 的型別:

public class OverrideMyLogActionFiltersAttribute : FilterAttribute , IOverrideFilter {
  public Type FiltersToOverride {
    get { return typeof( IActionFilter ); }
  }
}

這樣就可以在動作方法(Action Method)中套用它,以下程式碼套用在About方法:

namespace ActionFilterDemo.Controllers {
  //[MyLogActionFilter]
  public class HomeController : Controller {
     //[MyLogActionFilter]
    public ActionResult Index( ) {
      Debug.WriteLine( "Home.Index" );
      return new EmptyResult( );
    }
  
    [OverrideMyLogActionFilters]
    public ActionResult About( ) {
      Debug.WriteLine( "Home.About" );
      return new EmptyResult( );
    }
  }
}

執行時,若發出Home/Index請求,則會輸出除錯的訊息,而發出Home/About請求則不會觸發OnActionExecuting、OnActionExecuted事件,執行結果請參考下圖所示:

clip_image024

圖 12:輸出除錯訊息。

Tags:

.NET Magazine國際中文電子雜誌 | ASP.NET MVC | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List