فیلترهای سرویس، فیلترهای Action، فیلترهای Type در ASP.NET 5 و MVC 6

در این مقاله به معرفی انواع فیلتر های موجود در ASP.NET 5 و MVC 6 خواهیم پرداخت .کار فیلترها اجرای یک سری کد های خاص قبل و یا بعد از اکشن و یا کنترلر خاص است .

فیلترهای سرویس، فیلترهای Action، فیلترهای Type در ASP.NET 5 و MVC 6

کار فیلترها اجرای یک سری کد های خاص قبل و یا بعد از Action و یا کنترلر خاص است .فیلتر های متفاوتی در MVC وجود دارد که به چند مورد اشاره می کنیم .

چهار نوع فیلتر وجود دارد که هریک از آن ها  با interface قابل پیاده سازی میباشد.

1 – فیلتر Authorization توسط اینترفیس IAuthorizationFilter در دسترس خواهد بود

2 - فیلتر ACtion با اینترفیس IActionFilter قابل پیاده سازی است

3 - فیلتر Result با اینترفیس IResultFilter قابل اجرا میباشد

4 -  فیلترException  با  اینترفیس IExceptionFilter قابل اجرا میباشد

IAuthorizationFilter

این نوع فیلتر قبل از اجرای هر متد و یا فیلتر دیگری اجرا می شود.این اینترفیس توسط کلاس AuthorizeAttribute  پیاده سازی شده است .همیشه این امکان وجود دارد که کد های مربوط به فیلتر ها را درون کنترلر انجام داد.ولی برای اینکه از حجم کدهای تکراری کم کنیم از فیلتر ها استفاده میکنیم .

IActionFilter

ActionFilterها پیش (OnActionExecuting) و پس از (OnActionExecuted) اجرا می‌شوند. نحوه رندر یک View و .... را این فیلتر انجام میدهد. این اینترفیس توسط کلاس ActionFilterAttribute پیاده سازی شده است.

ResultFilter
کلاس ActionFilterAttribute، پیاده سازی این اینترفیس را انجام داده است .

IExceptionFilter

بعد از تمام فیلتر ها این نوع فیلتر اجرا می شود.توسط کلاس HandleErrorAttribute پیاده سازی شده است .
اولویت اجرای فیلتر ها به این صورت است که ابتدا AuthorizationFilter اجرا می‌شود و در آخر ExceptionFilter حال  اگر  دو نوع ActionFilter متفاوت به یک متد اعمال شدند، کدامیک ابتدا اجرا می‌شود؟
تمام فیلترها از کلاسی به نام FilterAttribute مشتق می‌شوند که دارای خاصیتی است به نام Order. بنابراین جهت مشخص سازی ترتیب اجرای فیلترها باید این ویژگی را مقداردهی کنیم

فیلترها نسبت به قبل کارایی بیشتری پیدا کرده اند .به صورت خاص تر در این مقاله راجع به فیلترهایی که درMVC 6و Asp.net5  تغییر کرده اند بحث می کنیم .

فیلتر های Action در نسخه جدید MVC نسبت به نسخه قبل یکسان است و تغییر چندانی نداشته است .این فیلتر ها اجازه اجرای کد های خاصی قبل و بعد از Action را می دهد.این فیلتر ها توسط دو اینترفیسی که در زیر کد آن آورده شده است پیاده سازی شده است .

public interface IActionFilter : IFilter
{
    void OnActionExecuting([NotNull] ActionExecutingContext context);

    void OnActionExecuted([NotNull] ActionExecutedContext context);
}

public interface IAsyncActionFilter : IFilter
{
    Task OnActionExecutionAsync([NotNull] ActionExecutingContext context, [NotNull] ActionExecutionDelegate next);
}

فیلتر اکشن اجازه اجرا شدن کدهای خاصی درست بعد و یا قبل از خروجی اکشن مورد نظر را به شما می دهد.

public interface IResultFilter : IFilter
{
    void OnResultExecuting([NotNull] ResultExecutingContext context);

    void OnResultExecuted([NotNull] ResultExecutedContext context);
}

public interface IAsyncResultFilter : IFilter
{
    Task OnResultExecutionAsync([NotNull] ResultExecutingContext context, [NotNull] ResultExecutionDelegate next);
}

اصولا فیلترهای Action ای که استفاده می کنید از این دو اینترفیس پیاده سازی شده اند.اما همان طور که می بینید هیچ کدام از این اینترفیس ها فیلترها را مرتب نمی کنند.مرتب نبودن فیلتر ها در MVC وجود دارد ولی همچنان و در نسخه های جدیدتر هم در Web Api وجود ندارد.در MVC مرتب سازی فیلتر ها و ترتیب دادن به آنها توسط اینترفیس IOrderedFilter انجام میشود.این اینترفیس توسط کلاس ActionFilterAttribute پیاده سازی شده است .

در کد زیر پیاده سازی یک فیلتر خاص به نام ValidateModelStateAttribute نشان داده شده است .

public class ValidateModelStateAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext actionContext)
    {
        if (!actionContext.ModelState.IsValid)
        {
            actionContext.Result = new BadRequestObjectResult(actionContext.ModelState);
        }
    }
}

در زیر نحوه استفاده از این فیلتر نمایش داده شده است .قبل از اجرا شدن Action این فیلتر Model State  را مورد ارزیابی قرار می دهد.و زمانی Action اجرا خواهد شده که Model state مقدار Valid داشته باشد.(یعنی پارامتر های ضروری در مدل وجود داشته باشند و یا Validation های ما درست باشد.)در این Action یک فیلتر دیگر هم وجود دارد ولی این فیلتر باعث چک شدن Model State نخواهد شد.

    [HttpPut("{id}")]
    [ValidateModelState]
    public void Put(int id, Item item)
    {
        contact.ContactId = id;
        repository.Update(contact);
    }

فیلتر های سرویس

فیلتر های مختلفی در Web Api وجود دارد از جمله فیلتر های احراز هویت.فیلترهای احراز هویت درخواست های رسیده از http را اعتبار سنجی می کنند.قبل از اجرا شدن Action مورد نظر یک لیست از فیلتر های احراز هویت مربوط به این Action تهیه می شود.درخواست رسیده برای این اکشن به هر کدام از این فیلتر ها داده می شود و اگر توانست از سد این فیلتر ها عبور کند به انتهای درخواست یک IPrincipal اضافه می شود.و اگر درخواست نتوانست از فیلتر رد شود یک خطا نمایش داده می شود.

فیتلر باید در container ثبت شود.فیلتر می تواند طول عمری داشته باشد که توسط شما تنظیم شود.تزریق سازنده امکانی است که هم اکنون در فیلتر های سرویس وجو.د دارد.

همچنین نکته ای که بسیار مهم است این است که فیلترهای Web Api در سیستم Cashe میشوند.

به کد زیر توجه کنید.

public class LogFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var logger = actionContext.Request.GetDependencyScope().GetService(typeof(ILog)) as ILog;
        if (logger != null)
        {
            logger.Log(actionContext.Request);
        }
    }
}

اگر قرار بود این کد در MVC6  نوشته شود باید دوباره نویسی می شد، در کلاسی که از تزریق وابستگی استفاده می کرد.به شکل زیر :

public class LogFilter : ActionFilterAttribute
{
    private readonly ILog logger;

    public LogFilter(ILog logger)
    {
        this.logger = logger;
    }

    public override void OnActionExecuting(ActionExecutingContext actionContext)
    {
        this.logger.Log(actionContext.HttpContext.Request);
    }
}

فیلترهای Type

این نوع فیلتر ها در سرویس و در MVC دقیقا مشابه هستند .پیاده سازی این نوع فیلتر توسط اینترفیسی به نام IFilterFactory می باشد.این نوع فیلتر های اجازه اعمال Dependency Injection به سایر فیلتر ها را به ما میدهند.پیاده سازی این اینترفیس در کد زیر نشان داده شده است .

  

public class TypeFilterAttribute : Attribute, IFilterFactory, IOrderedFilter
{
    private ObjectFactory factory;

    public TypeFilterAttribute([NotNull] Type type)
    {
        ImplementationType = type;
    }

    public object[] Arguments { get; set; }

    public Type ImplementationType { get; private set; }

    public int Order { get; set; }

    public IFilter CreateInstance([NotNull] IServiceProvider serviceProvider)
    {
        if (this.factory == null)
        {
            var argumentTypes = Arguments?.Select(a => a.GetType())?.ToArray();

            this.factory = ActivatorUtilities.CreateFactory(ImplementationType, argumentTypes ?? Type.EmptyTypes);
        }

        return (IFilter)this.factory(serviceProvider, Arguments);
    }
}       
دانلود نسخه ی PDF این مطلب