فیلتر، یک کلاس سفارشی است که شما میتوانید منطق برنامه را جهت اجرا، قبل یا بعد از اجرای یک اکشن متد، در آن پیاده سازی نمایید. فیلترها میتوانند به یک اکشن متد و یا کنترلری منتسب شوند که در ادامه با این روشها آشنا خواهید شد.
در لیست زیر انواع فیلترها و اینترفیسهایی که باید توسط کلاس سفارشی شما پیاده سازی شوند، معرفی شده است.
نوع | توضیح | فیلتر توکار | اینترفیس |
Authorization | انجام عملیات احراز هویت و سطح دسترسی، قبل از اجرای کد اکشن متد | [Authorize] و [RequireHttps] | IAuthorizationFilter |
Action | اجرای کدهایی قبل از اجرای کدهای اکشن متد | IActionFilter | |
Result | اجرای کدهایی قبل یا بعد از تولید ویو (View result) | [OutputCache] | IResultFilter |
Exception | اجرای کدهایی در صورت وجود استثنای مدیریت نشده | [HandleError] | IExceptionFilter |
در تکه کد زیر نحوهی استفاده از این فیلتر را مشاهده میکنید:
[HandleError] public class HomeController : Controller { public ActionResult Index() { //throw exception for demo throw new Exception("This is unhandled exception"); return View(); } public ActionResult About() { return View(); } public ActionResult Contact() { return View(); } }
نکته: فیلترهای اعمال شدهی به یک کنترلر، به تمام اکشن متدهای آن نیز اعمال میگردند.
در کد بالا خصیصهی HandleError به HomeController اعمال شده است. بنابراین در صورت بروز خطایی در هر کدام از اکشنها، صفحهی Error.cshtml نمایش داده خواهد شد و در تظر داشته باشید که خطاها توسط try-catch هندل نشدهاند.
باید جهت عملکرد صحیح فیلتر توکار HandleErrorAttribute، مقدار customErrors در قسمت System.web فایل web.config مساوی on باشد.
<customErrors mode="On" />
مهیا کنندگان فیلترها
بصورت پیش فرض MVC از سه طریق زیر فیلترها را جهت استفادهی در برنامه فراهم میکند:
- خصیصهی GlobalFilters.Filters برای فیلترهای سراسری
- کلاس FilterAttributeFilterProvider برای فیلترهای خصیصهای
- کلاس ControllerInstanceFilterProvider جهت افزودن کنترلر به یک وهله از FilterProviderCollection
در ادامه با نحوهی ایجاد یک فیلتر، بوسیلهی هر یک از روشهای بالا، با ذکر مثالی بیشتر آشنا خواهید شد.
ترتیب اجرای فیلترها
همانطور که در ابتدا اشاره شد، در MVC چهار نوع فیلتر معرفی شده است که امکان استفادهی از آنها بهصورت همزمان در سطح کنترلر و یا اکشن متد وجود دارد. اما ترتیب اجرای آنها متفاوت و به ترتیب زیر است:
- فیلترهای Authorization
- فیلترهای Action
- فیلترهای Result یا Response
- فیلترهای Exception
فیلترها براساس ترتیب اشاره شدهی در بالا اجرا خواهند شد. در صورتیکه چند فیلتر از یک نوع استفاده شود، جهت تقدم و تاخر در اجرا، از خاصیت Order استفاده خواهد شد. بعنوان مثال در کد زیر بدلیل خاصیت Order=1 ابتدا AuthorizationFilterB و سپس AuthorizationFilterA اجرا میشود.
[AuthorizationFilterA(Order=2)] [AuthorizationFilterB(Order=1)] public ActionResult Index() { return View(); }
public enum FilterScope { First = 0, Global = 10, Controller = 20, Action = 30, Last = 100, }
نکته: مقدار Scope فیلترهای Authorization برابر 0 و فیلترهای Exception برابر 100 میباشد.
ایجاد فیلتر سفارشی
روش اول: پیاده سازی اینترفیس یکی از انواع فیلترها و ارث بری از کلاس FilterAttribute
در این روش متدهایی که باید پیاده سازی شوند متفاوت خواهد بود. به همین جهت متدهای هر نوع بشرح زیر معرفی میشود:
- IAuthorizationFilter
// Called when authorization is required void OnAuthorization(AuthorizationContext filterContext)
- IActionFilter
// Called after the action method executes void OnActionExecuted(ActionExecutedContext filterContext) // Called before an action method executes void OnActionExecuting(ActionExecutingContext filterContext)
- IResultFilter
// Called after an action result executes void OnResultExecuted(ResultExecutedContext filterContext) // Called before an action result executes void OnResultExecuting(ResultExecutedContext filterContext)
- IException
// Called when an exception occurs void OnException(ExceptionContext filterContext)
یادآوری: همانطور که در ابتدای مقاله اشاره شد، فیلترها قبل یا بعد از اجرای اکشن متدها فراخوانی خواهند شد. بنابراین به کامنت بالای متد فیلترها دقت داشته باشید.
مثال: پیاده سازی اینترفیس IExceptionFilter و ارث بری از کلاس FilterAttribute جهت تهیهی فیلتری سفارشی از نوع Exception
class CustomErrorHandler : FilterAttribute, IExceptionFilter { public override void IExceptionFilter.OnException(ExceptionContext filterContext) { Log(filterContext.Exception); base.OnException(filterContext); } private void Log(Exception exception) { //log exception here.. } }
روش دوم: ارث بری از ActionFilterAttribute
کلاس abstract فوق دارای چهار متد زیر جهت تحریف است. همانطور که مشاهده میکنید این کلاس علاوه بر دو متد OnActionExecuted و OnActionExecuting دارای دو متد دیگر OnResultExecuting و OnResultExecuted که بهترتیب قبل و بعد خروجی (Result) اکشن متد اجرا میشوند، نیز میباشد. این نوع فیلترها عموما جنبهی استفاده عمومی داشته و میتوان از آنها جهت logging ،caching و یا authorization استفاده کرد.
// Called by MVC after the action method executes void OnActionExecuted(ActionExecutedContext filterContext) // Called by MVC before the action method executes void OnActionExecuted(ActionExecutedContext filterContext) // Called by MVC after the action result executes void OnResultExecuted(ResultExecutedContext filterContext) // Called by MVC before the action result executes void OnResultExecuting(ResultExecutingContext filterContext)
مثال: کلاس LogAttribute که از کلاس ActionFilterAttribute ارث بری کرده است، عملیات قبل و بعد از اجرای اکشن متد را لاگ میکند.
public class LogAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { Log("OnActionExecuted", filterContext.RouteData); } public override void OnActionExecuting(ActionExecutingContext filterContext) { Log("OnActionExecuting", filterContext.RouteData); } public override void OnResultExecuted(ResultExecutedContext filterContext) { Log("OnResultExecuted", filterContext.RouteData); } public override void OnResultExecuting(ResultExecutingContext filterContext) { Log("OnResultExecuting ", filterContext.RouteData); } private void Log(string methodName, RouteData routeData) { var controllerName = routeData.Values["controller"]; var actionName = routeData.Values["action"]; var message = String.Format("{0}- controller:{1} action:{2}", methodName, controllerName, actionName); Debug.WriteLine(message); } }
روش سوم: پیاده سازی داخل کنترلر
کلاس Controller میتواند هر یک از اینترفیسهای فیلترها را پیاده سازی نماید. به عبارت دیگر در هر کلاس کنترلر میتوانید متدهای زیر را تحریف نمایید.
- OnAuthorization ^
- OnException ^
- OnActionExecuting ^
- OnActionExecuted ^
- OnResultExecuting ^
- OnResultExecuted ^
روش چهارم: ارث بری از کلاس فیلترهای توکار و مهیای در MVC و تحریف متدهای آن
در کد زیر با تحریف و سفارشی سازی متد OnException مخصوص فیلتر توکار HandleError، قابلیتهای آن افزایش یافته است:
class CustomErrorHandler : HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { Log(filterContext.Exception); base.OnException(filterContext); } private void Log(Exception exception) { //log exception here.. } }
رجیستر فیلترها
- سراسری:
درصورتی که قصد داشته باشید فیلتری بصورت سراسری و در کل برنامه فعال گردد باید آن را در رویداد Application_Start فایل Global.asax.cs بوسیلهی متد RegisterGlobalFilters کلاس FiterConfig رجیستر نمایید. بعد از آن فیلتر به کلیهی کنترلرها و اکشن متدها اعمال میگردد.
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); } } // FilterConfig.cs located in App_Start folder public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); // add your new custom filters filters.Add(new LogAttribute()); filters.Add(new CustomErrorHandler()); } }
در کد بالا فیلتر توکار HandleError و البته فیلترهای سفارشی دیگری نیز به صورت سراسری به تمام اکشن متدهای کنترلرها اعمال گردیده است.
- کنترلر: در صورتی که فقط بخواهید یک فیلتر به کل اکشنهای یک کنترلر اعمال گردد. همانند آنچه که در مثال ابتدایی بدان اشاره شد.
[HandleError] public class HomeController : Controller
- اکشن متد: اعمال یک فیلتر به یک اکشن متد خاص کنترلر. در کد زیر فیلتر HandleError فقط به اکشن متد Index کنترلر Home اعمال خواهد شد.
public class HomeController : Controller { [HandleError] public ActionResult Index() { return View(); } }