فیلترها در asp.net-core-mvc
شناسه پست: 3386
بازدید: 1384

فیلترهای موجود در ASP.NET Core MVC این امکان را به ما میدهند تا اقدامات ویژه ای را قبل یا بعد از مراحل خاص در pipeline پردازش request اجرا کنیم. تعدادی فیلترهای توکار در ASP.NET Core وجود دارد. ما همچنین میتوانیم فیلترهای سفارشی خودمان را برای اجرای  اقدامات در مراحل مختلفی از request pipeline بنویسیم. فیلترها همچنین به ما کمک می کنند تا نگرانی های مقطعی (cross-cutting concerns) را مدیریت کنیم و از تکرار کدها جلوگیری کنیم.

در این مقاله، ما انواع فیلترهای موجود در ASP.NET Core MVC و نحوه پیاده سازی هریک از آنها را توضیح خواهیم داد.

اگر برخی از مقالات قبلی این آموزشی سریالی را ندیده اید، توصیه می کنیم صفحه مربوط به این سری آموزشی را مشاهده کنید: آموزش سریالی ASP.NET Core MVC.

برای دانلود سورس کد این مقاله، روی این لینک کلیک کنید: فیلترها در ASP.NET Core MVC.

اول نگاهی به انواع فیلترهای موجود در ASP.NET Core MVC بیندازیم.

انواع فیلترها

هریک از این انواع فیلترها در یک مرحله متفاوت از request pipeline اجرا میشوند.

فیلترهای Authorization اول از همه اجرا میشوند و جهت تعیین اینکه آیا کاربر کنونی مجوز درخواست فعلی را دارد یا خیر مورد استفاده قرار میگیرند. این فیلترها می توانند در صورت غیرمجاز بودن درخواست، pipeline را short-circuit (کوتاه کردن جریان pipeline و نادیده گرفتن مابقی پردازش درخواست در pipeline ) کنند.

فیلترهای Resource درست بعد از authorization قرار میگیرند. ما میتوانیم از این فیلترها برای پیاده سازی caching یا short-circuit کردن pipeline فیلتر، به دلایل performance استفاده کنیم.

فیلترهای action میتوانند کد را بلافاصله قبل و بعد از فراخوانی action method مورد نظر اجرا کنند. ما می‌توانیم از آن برای دستکاری آرگومان‌های ارسال شده به action و نتیجه بازگشتی از action مورد نظر استفاده کنیم. صفحات Razor از فیلترهای Action پشتیبانی نمیکنند.

فیلترهای Exception در حالت عموم، برای handle کردن تمام exception های کنترل نشده در application مورد استفاده قرار میگیرند.

فیلترهای Result، میتوانند کد را بلافاصله قبل و بعد از اجرای نتایج action مورد نظر اجرا کنند. این فیلترها فقط زمانی اجرا میشوند که action method مورد نظر با موفقیت اجرا شده باشد.

فیلتر Authorization

فیلترهای Authorization دسترسی به action method ها را کنترل میکنند. این فیلترها، اولین فیلتری هستند که داخل pipeline فیلتر اجرا میشوند. این فیلترها یک متد قبل از فراخوانی اکشن متد به نام ()OnAuthorization دارند. اما متد بعد از فراخوانی اکشن متد ندارند.

پیاده سازی

اجازه دهید یک فیلتر Authorization را پیاده سازی کنیم. برای این منظور، باید اینترفیس IAuthorizationFilter و متد ()OnAuthorization را درون آن پیاده سازی کنیم.

در این فیلتر، یک فیلد به نام _permission  که از طریق سازنده تامین میشود را تعریف میکنیم. همچنین میتوانیم یک متد ()CheckUserPermission را ایجاد کنیم که کار logic مربوط به مجوز دسترسی را انجام دهد.

جهت تست اهداف، فرض کنیم که کاربر فرضی همیشه فقط مجوز خواندن دارد و مجوز نوشتن ندارد. وقتی کاربر مورد نظر مجوز مورد نظر را جهت دسترسی نداشته باشد، میتوانیم ویژگی Result در HTTP Context را با UnauthorizedResult تنظیم کنیم که باعث میشود جریان اجرای pipeline کوتاه شده و مابقی درخواست اجرا نشود و یا به اصطلاح اجرای short-circuit ،pipeline شود.

یک کلاس AuthorizeActionFilter ایجاد میکنیم:

حالا با استفاده از TypeFilterAttribute، یک Attribute برای فیلتری که در حال حاضر ایجاد کردیم میسازیم:

حالا Authorize attribute خود را آماده جهت استفاده در اختیار داریم. حالا از آن در برنامه خود استفاده میکنیم.

جهت آزمایش attribute، سه اکشن در کنترلر به نامهای Index()Read() و Edit() میسازیم.

متد ()Index را بدون مزین کردن Authorize attribute در بالای آن رها میکنیم. برای متد ()Read، ویژگی Authorize را با مجوز Read تعیین میکنیم. همچنین ویژگی Authorize برای متد ()Edit را با مجوز Write مشخص میکنیم:

آزمایش فیلتر

به یاد داشته باشید که جهت تست هدف خود، فیلتر را طوری که فقط مجوز Read برای تمام کاربران در دسترس است پیاده سازی کرده ایم.

برنامه را اجرا میکنیم و به \home\index میرویم:

صفحه Index در home

از آنجایی که این متد نیازی به مجوز دسترسی ندارد، بدون هیچ گونه مشکلی اجرا میشود.

حالا به \home\read میرویم:

آزمایش مجوز Authorization در asp.net core mvc

این اکشن متد به مجوز Read نیاز دارد که برای کاربر مورد نظر در دسترس است.

اگر یک breakpoint در متد ()OnAuthorized در AuthorizationFilter قرار دهیم، میتوانیم ببینیم که این متد برای متد ()Index اجرا نمیشود ولی برای متد ()Read اجرا میشود.

حالا به \home\edit میرویم:

آزمایش مجوز Authorization در asp.net core mvc

این action به مجوز Write نیاز دارد که برای کاربر مورد نظر در دسترس نیست. از این رو، یک حطای HTTP  401 را throw میکند که نشان دهنده یک درخواست غیرمجاز است.

چیزی که ما اینجا میبینیم صفحه خطای استاندارد مرورگر برای HTTP 401 Response است. ما همچنین میتوانیم جهت تجربه کاربری بهتر، صفحات خطای سفازشی مربوط به کدهای وضعیتهای مختلف را در اپلیکیشن خود بسازیم.

در این قسمت، ما نحوه پیاده سازی یک فیلتر Authorization را یاد گرفتیم.

فیلترهای Resource 

همانطور که ما از نام این فیلتر متوجه میشویم، فیلترهای Resource جهت handle کردن منابع و کمک به کوتاه کردن جریان اجرای request pipeline در صورت نیاز مورد استفاده قرار میگیرد. یک استفاده رایج از این نوع فیلتر، پیاده سازی Caching است. این نوع فیلتر، میتواند زمانیکه یک Cache نتیجه مورد انتظار را دربر دارد از مابقی کد pipeline اجتناب کند.

پیاده سازی

حالا ببینیم که چطور با استفاده از فیلتر resource میتوانیم caching را پیاده سازی کنیم.

اول یک کلاس CacheResourceFilter که اینترفیس IResourceFilter را پیاده سازی میکند را تعریف میکنیم. فیلتر Resource یک متد قبل از فراخوانی اکشن متد به نام ()OnResourceExecuting و یک متد بعد از فراخوانی اکشن متد به نام ()OnResourceExecuted دارد.

یک آبجکت dictionary به نام cache_ برای نگهداری مقدار cache شده و یک متغیر رشته ای به نام _cacheKey برای ذخیره کلید Cache تعریف میکنیم:

در متد ()OnResourceExecuting میتوانیم بررسی کنیم که آیا _cacheKey در دیکشنری _cache موجود است یا خیر. اگر موجود باشد، context.Result را با مقدار cache شده تنظیم میکنیم. میتوانیم با تنظیم کردن ویژگی context.Result، جریان pipeline فیلتر را short-circuit کنیم.

در متد ()OnResourceExecuted بررسی میکنیم که آیا _cacheKey هم اکنون در cache_ موجود است یا خیر. اگر نیست، مقدار context.Result را به درون cache_ درج میکنیم.

حال یک attribute برای این فیلتر ایجاد میکنیم:

در نهایت، اجازه دهید یک کنترلر و یک اکشن متد ایجاد کنیم تا متنی را برگردانیم که زمان تولید محتوا را نشان می‌دهد. همچنین اجازه دهید کنترلر را با ویژگی CacheResource که به تازگی ایجاد کردیم مزین کنیم:

آزمایش فیلتر

حالا برنامه را اجرا میکنیم و به cached/ میرویم:

کش شدن با فیلتر resource

وقتی این URL را برای دفعه اول مورد دسترسی قرار میدهیم، میتوانیم ببینیم که content با timestamp فعلی تولید میشود. سپس برای تمام دسترسی های متوالی بعدی به همین URL، یک ورژن کش شده از resource را دریافت میکنیم. ما میتوانیم این را با بررسی کردن timestamp در عمل ببینیم. همچنین اگر یک breakpoint در اکشن متد درون کنترلر قرار دهیم، میتوانیم ببینیم که این متد فقط برای اولین درخواست اجرا میشود. در درخواستهای بعدی، میتوانیم ببینیم که با استفاده از فیلتر resource، اجرای pipeline را short-circuited کرده ایم.

عالی! ما نحوه پیاده سازی یک فیلتر Resource را یاد گرفتیم.

فیلترهای Action

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

پیاده سازی

اجازه دهید یک فیلتر action ایجاد کنیم که model های نامعتبر را handle کند. اگر model نامعتبر باشد، میخواهیم یک BadRequest response استاندارد به همراه یک پیغام سفارشی برگردانیم.

کلاس اعتبارسنجی سفارشی ما باید از کلاس abstract ActionFilterAttribute ارث بری کند و متدهای OnActionExecuting() و OnActionExecuted() را override کند:

()OnActionExecuting قبل از action method و ()OnActionExecuted بعد از action method اجرا میشود.

در متد ()OnActionExecuting ما بررسی میکنیم که آیا Model برابر با null است یا ModelState نامعتبر است!! در صورتی که یکی از این دو موارد صحیح بود ما میتوانیم یک BadRequest response برگردانیم. به این ترتیب، با این کار، ما می توانیم به جای نوشتن این کد در هر یک از action method ها، این کار را در سراسر برنامه handle کنیم.

حالا این فیلتر را در یک action method استفاده کنیم:

تست فیلتر

اگر این action method را بدون ارائه یک Book model معتبر فراخوانی کنیم، یک BadRequest response استاندارد را به همراه یک پیغام سفارشی دریافت خواهیم کرد:

فیلتر اعتبارسنجی model

تمام! در این مرحله، ما نحوه پیاده سازی یک فیلتر action را بررسی کردیم.

فیلترهای Exception

فیلترهای Exception جهت handle کردن هر نوع Exception کنترل نشده ای که در برنامه ما اتفاق می افتد استفاده میشوند. این فیلترها، متدهای قبل یا بعد ندارند. این فیلترها فقط متد ()OnException را پیاده سازی میکنند. این متد هرزمان که یک exception کنترل نشده در برنامه ما اتفاق بیفتد فراخوانی خواهد شد.

پیاده سازی

برای مشاهده نحوه کارکرد فیلتر exception، یک action method میسازیم که یک exeption مدیریت نشده را تولید میکند:

حالا با پیاده سازی اینترفیس IExceptionFilter، یک فیلتر exception سفارشی ایجاد میکنیم. ما میتوانیم منطق مدیریت exeption مورد نیاز را در متد ()OnException بنویسیم:

در متد ()OnException، میتوانیم ViewResult را با CustomError تنظیم کنیم. سپس میتوانیم جزییات exception را به عنوان ViewData به داخل view پاس دهیم.

بعد از آن، CustomError view را ایجاد کرده و پیغام خطا را به یک شیوه کاربرپسندانه نمایش میدهیم:

سپس باید فیلتر exception را در startup.cs تنظیم کنیم:

تست فیلتر

حالا برنامه را اجرا میکنیم و به home\generateerror\ میرویم:

مدیریت خطا با فیلتر سفارشی در asp.net core mvc

می بینیم که اینجا یک صفحه خطای سفارشی با جزئیات exception نمایش داده می شود که می توانیم آن را سفارشی کنیم تا فقط اطلاعات مورد نیاز را به کاربر نشان دهیم. به این ترتیب، می‌توانیم تجربه بسیار بهتری به کاربر ارائه دهیم و exception ها را در سراسر برنامه به روشی یکپارچه مدیریت کنیم.

Handle کردن Exception – میان افزارها در مقابل فیلترها

علاوه بر این، ما می‌توانیم از میان‌افزار برای مدیریت Exception های کنترل نشده نیز استفاده کنیم. بنابراین، چه زمانی باید از یک میان افزار مدیریت Exception استفاده کنیم و چه زمانی باید به دنبال فیلتر Exception باشیم؟

اگر نگران خطاهایی هستیم که ممکن است خارج از بستر MVC یا کد ما رخ دهد، برای مثال، ممکن است بخواهیم خطایی را که در داخل یک میان افزار یا فیلتر رخ دهد را ثبت کنیم، باید به سراغ یک میان افزار مدیریت Exception برویم. از طرف دیگر، اگر بخواهیم بستر MVC را در حین رسیدگی به Exception ها داشته باشیم و بر اساس آن اقداماتی را انجام دهیم، باید از یک فیلتر Exception استفاده کنیم.

در این قسمت یاد گرفتیم که چگونه فیلتر Exception را در اپلیکیشن خود پیاده سازی کنیم.

فیلترهای Result

ما میتوانیم از فیلترهای Result برای اجرای کد، قبل یا بعد از اجرای نتیجه action در controller استفاده کنیم. این فیلترها فقط زمانی اجرا میشوند که action method مورد نظر با موفقیت اجرا شود. ما می‌توانیم logic مورد نظر خود را حول و محور view یا جهت اعمال برخی سفارشی‌سازی‌ها برای تمام نتایج action های موجود در برنامه‌ خود بنویسیم.

فرض کنید می‌خواهیم یک مقدار خاص را به header تمام action result ها در برنامه خود اضافه کنیم.

ما قصد داریم نحوه پیاده سازی این کار را با استفاده از فیلتر result بررسی کنیم.

اول از همه یک کلاس AddHeaderFilter که اینترفیس IResultFilter را پیاده سازی میکند را ایجاد میکنیم. فیلتر Result یک متد قبل از فراخوانی به نام ()OnResultExecuting و یک متد بعد از فراخوانی به نام ()OnResultExecuted دارد.

در متد ()OnResultExecuting، یک response header سفارشی اضافه میکنیم:

بعد از این، یک attribute برای فیلتر Result که در حال حاضر ایجاد کردیم میسازیم:

در مرحله آخر، یک action method در HomeController میسازیم و attribute فیلتر AddHeader result را در آن اعمال میکنیم:

حال برنامه را اجرا کنیم و به home\testresultfilter\ برویم.

با inspect کردن headers، میتوانیم ببینیم که header سفارشی ما در response آمده است:

فیلتر result در asp.net core mvc

به این ترتیب، ما میتوانیم از فیلتر Result برای سفارشی سازی action result های برنامه استفاده کنیم.

عالی! در این قسمت، ما نحوه پیاده سازی یک فیلتر result را یاد گرفتیم.

نتیجه گیری

در این مقاله، ما عناوین زیر را یاد گرفتیم:

  • انواع مختلف فیلترهای موجود در ASP.NET Core MVC
  • ایجاد یک فیلتر Authorization سفارشی
  • پیاده سازی Caching با استفاده از فیلتر Resource 
  • استفاده از فیلتر Exception برای handle کردن exception های کنترل نشده
  • افزودن یک response header سفارشی به تمام action method های خود با استفاده از فیلتر Resource 

با این مقاله، به پایان سری آموزش سریالی ASP.NET Core MVC رسیدیم. امیدواریم از خواندن این مطلب لذت برده باشید و فرآیند یادگیری خوبی را سپری کرده باشید.

نویسنده

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