آموزش Exception Filter در ASP.NET Core و ذخیره Exception ها در دیتابیس با EF Core

آموزش Exception Filter در ASP.NET Core و ذخیره Exception ها در دیتابیس با EF Core

در این مقاله از GitHub Gist استفاده شده است، لطفا شکیبایی کنید تا Gist ها به صورت کامل لود شوند.** اگر Gist ای لود نشده، روی آن یکبار کلیک کنید تا وارد خود Gist شود و سپس برگردید به مقاله.**در فریمورک ASP.NET Core – پنج نوع فیلتر داریم، در این مقاله میخواهیم Exception Filter رو یاد بگیریم و یک سناریو جذاب رو پیاده سازی و اجرا کنیم.این فیلتر رو میتونیم در 3 جا به صورت دستی استفاده کنیم.ControllerAction MethodRazor Pages (per page model)و همچنین می توانیم Exception Filter هارو به صورت global به سرویس های MVC اضافه کنیم تا نیاز نباشد به صورت دستی فیلترهارو اعمال کنیم. در اصل Exception Filter حتی قادر است unhandled exception هایی که در Model Binding و Action Filter ها رخ میدهد را مدیریت کند.پس با Exception Filter میتونیم در اصل خطاهای 5 قسمت رو مدیریت کنیم. ControllersAction MethodsRazor Pages (per page model)Action FiltersModel Bindingمثال اول: من میخوام هر Exception ای که رخ داد و اطلاعات تکمیلی اش مانند زمانی که رخ داده و برای چه کسی رخ داده رو ذخیره کنم در یک جدول مجزا در دیتابیس.مثال دوم: من میخوام هر Exception ای که رخ داد با تمام اطلاعات تکمیلی اش رو به ایمیل خودم ارسال کنم و زودتر از کارفرما از ارور با خبر بشم و بتونم سریع مشکل کد رو Fix کنم و نسخه جدید رو به کارفرما تحویل بدم.مثال سوم: من میخوام اطلاعات Exception رو به یک View بفرستم و اون View رو برگردونم به کاربر. (custom error view)مثال چهارم: من میخوام فیلترهایی بسازیم که حدودا کار Exception Handling Middlware هارو انجام بدن، البته فیلترها انعطاف خیلی کمتری نسبت به Middleware ها دارند.** هر سناریو و سیاستی که در رابطه با Exception ها داشته باشید رو میتونید با این فیلتر پیاده سازی کنید.این فیلتر 2 نسخه دارد، یک نسخه Asynchronous و یک نسخه Synchronous. اگر کدهایی که میخواهیم در یک فیلتر بنویسم async باشند میتوانیم نسخه async این فیلتر را پیاده سازی کنیم.اینترفیس IExceptionFilter برای پیاده سازی یک Exception Filter به صورت Synchronous استفاده میشود.اینترفیس IExceptionFilterاینترفیس IAsyncExceptionFilter برای پیاده سازی یک Exception Filter به صورت Asynchronous استفاده میشود.اینترفیس IAsyncExceptionFilter3 روش برای ساخت یک Exception Filter داریم.روش اول: Implement کردن اینترفیس IExceptionFilterدر بلاک کد پایین مشاهده میکنید که یک کلاس به نام SyncExceptionFilter ساختیم و اینترفیس IExceptionFilter را Implement کرده ایم. https://gist.github.com/ArminShoeibi/f802a103c3ca5e54c837ec0bc5365889 روش دوم: Implement کردن اینترفیس IAsyncExceptionFilterدر بلاک کد پایین مشاهده میکنید که یک کلاس به نام AsyncExceptionFilter ساختیم و اینترفیس IAsyncExceptionFilterرا Implement کرده ایم. https://gist.github.com/ArminShoeibi/ed4b02bfe1055ac79fc74ac15dd87b42 روش سوم: ارث بری از کلاس ExceptionFilterAttributeاین کلاس هر 2 اینترفیس بالا رو پیاده سازی کرده است ولی هیچ پیاده سازی برای متد هایشان انجام نداده است و به هر 2 متد کلمه کلیدی virtual را اضافه کرده است تا بتوانیم با ارث بری از این کلاس پیاده سازی خودمان را جایگزین کنیم برای یکی از این متدها.توجه کنید که وقتی از کلاس ExceptionFilterAttribute ارث بری میکنیم فقط و فقط باید یکی از متدهای آن را override کنیم، یعنی یا نسخه sync یا نسخه async.برتری و مزیت دیگه ای هم که نسبت به اون 2 اینترفیس داره، اینه که از کلاس انتزاعی Attribute ارث بری کرده و در اصل یک Attribute هم هست.همینطور که در قطعه کد های پایین مشاهده میکنید، کلاسی که به نام ApiExceptionFilter ساختیم رو میتونیم به صورت صفت هم استفاده کنیم. https://gist.github.com/ArminShoeibi/8514e28b9fbeea638d072831fda7793f در ادامه از روش سوم یعنی ارث بری از کلاس ExceptionFilterAttribute بهره می بریم و یک سناریو جذاب و مورد نیاز رو پیاده سازی میکنیم.1. در ابتدا یک پروژه ASP.NET Core از نوع Empty میسازیم.ایجاد پروژه ASP.NET Core2. سپس پکیج Microsoft.EntityFrameworkCore.SqlServer را نصب میکنیم و بعد از آن پکیج Microsoft.EntityFrameworkCore.Tools را نصب میکنیم.نصب از طریق NuGet Package Manager ConsoleInstall-Package Microsoft.EntityFrameworkCore.SqlServer -Version 5.0.1
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 5.0.1نصب از طریق NuGet Package Manager3. سپس یک کلاس میسازیم که وظیفه database context بودن را به عهده بگیرد.و تنظیمات مورد نیاز را برای آن انجام میدهیم.در پروژه یک فولدر به نام Data میسازیم.سپس یک کلاس به نام BlogContext به آن اضافه می کنیم.و از کلاس DbContext ارث بری میکنیم و در ادامه یک Constructor سفارشی ایجاد میکنیم و در آن DbContextOptions را به سازندهِ کلاس پدر یعنی DbContext پاس میدهیم. در مرحله بعدی یک کلاس میسازیم به نام UnhandledException و از این کلاس به عنوان یک Entity استفاده میکنیم برای ذخیره کردن Exception و اطلاعات مربوط به آن.این کلاس را باید به صورت یک پراپرتی از نوع DbSet در کلاس BlogContext اضافه کنیم. https://gist.github.com/ArminShoeibi/22e138fd1cdaf3acc53f7ebeed201343 توجه داشته باشید که اگر EF Core 5 استفاده نمیکنید، باید نوع داده پراپرتی RemoteIpAddress و LocalIpAddress را string قرار دهید، چون در ورژن های قبلی این امکان را در اختیار نداریم. ( البته میتوانید آرایه ای از byte هم قرار دهید ).اضافه کردن موجودیت Unhandled Exception به BlogContextدر این مرحله IoC و Http pipeline پروژه را تنظیم میکنیم https://gist.github.com/ArminShoeibi/356d3c95dce34ecdc377dfc52fe98f0f بعد از اینکه سرویس DbContext رو به IoC اضافه کردیم باید اولین Migration مون رو بسازیم و اعمالش کنیم بر روی DBMS.خب در این مرحله باید ExceptionFilter ای که مد نظر داریم رو بسازیم و قسمت اصلی آموزش در اصل از اینجا شروع میشه.اول در پروژه یک Folder میسازیم به نام Filters که هر فیلتری که میسازیم رو در اون قرار بدیم.همونطور که در بالا اشاره شد هر وقت Exception Filter تون قراره از کد های async استفاده کنه، باید از نسخه async رو پیاده سازی کنه.چون این فیلتر از EF Core استفاده میکنه پس ما هم نسخه async رو پیاده سازی میکنیم. ( استفاده از متد OnExceptionAsync )این متدهای OnException و OnExceptionAsync در اصل موقعی اجرا میشن که خطا رخ بده یعنی چیزی به نام اجرا شدن در قبل و بعد ندارن.خب در این آموزش قرار شد از کلاس ExceptionFilterAttribute استفاده کنیم و وقتی از این کلاس استفاده میکنیم باید فقط یکی از متد هاش رو override کنیم که در این آموزش ما متد OnExceptionAsync رو override میکنیم و کدهایی رو می نویسیم که وقتی یک Exception رخ داد اجرا بشن.خب در 3 مرحله قدم به قدم اولین فیلترمون رو میسازیم.1. یک کلاس ایجاد کنید به نام EFCoreLoggerExceptionFilter2. از کلاس انتزاعی ExceptionFilterAttribute ارث بری کنید.3. متد OnExceptionAsync رو override کنید.4. BlogContext رو در Constructor این کلاس تزریق کنید. https://gist.github.com/ArminShoeibi/532eb46bd01e6b35a537193a00f5f6ea توضیحاتی در مورد کلاس ExceptionContextاین کلاس در اصل همه چیز را در اختیار ما میگذارد.1. خود Exception رخ داده2. HttpContext3.ModelState4.RouteData5.ActionDescriptor و…کلاس ExceptionContext کلاس ExceptionContext از کلاس FilterContext ارث بری میکند و خود کلاس FilterContext از کلاس ActionContext ارث بری میکند که باعث میشود بتوانیم در این کلاس به تمامی چیزهایی که نیاز داریم دسترسی داشته باشیم.کدی که باید بنویسیم برای ذخیره کردن Exception و اطلاعات تکمیلی آن به شرح زیر است.در واقع فقط نمونه سازی از کلاس UnhandledException و مقداردهی پراپرتی هایش است و در اخر ذخیره اون نمونه در دیتابیس. https://gist.github.com/ArminShoeibi/36d8d52527d2489a36514a65fc36cd31 توجه داشته باشید که در تصویر بالا دو پراپرتی UserName و UserId را مقداردهی نکردیم به دلیل اینکه پروژه تستی است و مکانیزم احراز هویت را پیاده سازی نکردیم.در پروژه های خودتان باید اون 2 پراپرتی را مقداردهی کنید و بهتر است که یک ارتباط One to Many بین کاربر و UnhandledException بسازید.خب حالا که فیلتر ساخته شد باید فقط فراخوانیش کنیم.1. فراخوانی فیلتر به صورت global2. فراخوانی فیلتر بر روی Controller3. فراخوانی فیلتر بر روی Action Method4. فراخوانی فیلتر بر روی PageModelنکته: چون فیلتر ما به کلاس BlogContext وابسته است اگر بخواهیم بر روی Controller یا PageModel فراخوانیش کنیم باید از صفت TypeFilter استفاده کنیم.در زیر استفاده از این صفت رو بالای کنترلر Home و پیج Index مشاهده میکنید. https://gist.github.com/ArminShoeibi/a514330b7cc5da4bc7a4965caa801c63 خب الان هر Exception ای که رخ بده و اگر اون رو با try-catch مدیریت نکرده باشیم اطلاعاتش کامل در دیتابیس ذخیره میشه.مثال: در اکشن Index یک کدی می نویسیم که باعث رخ دادن یک Exception بشه و پس از رخ دادن خطا میتونید به دیتابیس مراجعه کنید و اطلاعات کاملی که ازش ذخیره شده رو در جدول UnhandledExceptions ببینید.مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom

Author: admin

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *