چکیده: مدیریت خطاها در ASP.NET MVC و ثبت آنها در دیتابیس با استفاده از Action Filter های مربوط به خطاها
هنگام توسعه اپلیکیشن، بهتر است درستی و نادرستی منطق کدهایمان را هنگام ارسال درخواستهای کاربران یا هنگام ارسال داده ها توسط آنها در نظر بگیریم. بعضی اوقات حتی بعد از تست کردن کدها، کد ما ممکن است به درستی، پاسخگوی درخواست های کاربران نباشد. با توجه به این نکته، ما کدمان را با استفاده از تکنیکهای مدیریت خطاها مانند بلاک try-catch-finally پیاده سازی میکنیم. این بلاک به طور موفقیت آمیز برای تمام انواع خطاهای شناخته شده ممکن از جمله خطاهای نوشتن/خواندن در فایلها، خطاهای اتصال به دیتابیس و غیره اجرا میشود. اما اگر در طول یک فرایند، درخواست مورد نظر به هردلیلی از جمله نبود منابع درخواست شده یا اینکه داده های ارسال شده توسط کاربر نهایی در مقابل تعدادی قوانین کسب و کار شکست بخورد، توسعه دهنده نرم افزار باید منطقی را برای مدیریت خطاهای سفارشی جهت اداره چنین خطاهایی پیاده سازی نماید. این منطق باید طوری انعطاف پذیر باشد که هر زمان در هر نقطه از اپلیکیشن چنین خطایی رخ داد، خطاهای مورد نظر را مدیریت نموده و برای تجزیه و تحلیل های آینده آنها را ثبت نماید.
اپلیکیشن MVC میتواند از رویداد Application_Error استفاده نماید. در این مورد، هر رویداد مدیریت نشده با MVC میتواند با رویداد Application_Error از Asp.net مدیریت شود. این یک رویکرد عالی برای مدیریت خطاها در سطح اپلیکیشن است. فقط مشکل این رویکرد این است که از آنحایی که آن در سطح اپلیکیشن است و شیوه خاصی را برای مدیریت خطاها در اختیار قرار میدهد داده های Context مربوط به Controller ها برای مدیریت بهتر خطاها در اختیار ما قرار نمیگیرد. این بدان معناست که مدیریت زیادی در اپلیکیشن MVC نمیتوانیم داشته باشیم. در صورتی که ما نیاز داریم که تمام خطاها در سطح MVC را مدیریت نماییم.
شیوه های ساده ای برای مدیریت خطاها در MVCوجود دارد که برای آشنایی بیشتر میتوانید مقاله ASP.NET MVC 5 – مدیریت خطاها با چند راه ساده را مطالعه نمایید.
قسمت 1: چرخه اجرای Action Filter
در ASP.NET MVC، ما یک فیلتر به نام ActionFilter داریم که توسط آن میتوانیم درخواست های ورودی را ارزیابی کرده و آنها را در مقابل فیلترهایی مانند Authentication، Exceptions، Results و غیره پردازش نماییم. ما میتوانیم از Exception Filter برای مانیتور کردن هر خطایی که در حین پردازش Request اتفاق می افتد استفاده نماییم. در MVC، یک Request به سمت کنترلر می آید، سپس ActionMethod موجود در کنترلر اجرا میشود. Exception ActionFilter اینجا اعمال میشود. این Action Filter در حین اجرای Action Method، اجرا میشود. در نمودار زیر، چرخه ActionFilter برای اداره Exception ها نشان داده شده است:
این exception filter روی اکشن متد اعمال میشود (همچنین ما میتوانیم این فیلتر را ر روی کنترلر حاوی اکشن متدهای مورد نظر نیز اعمال نماییم). این فیلتر در scope داخل اکشن متد اجرا میشود. اگر اکشن متد با موفقیت اجرا شود سپس Action Result تولید خواهد شد. در غیر اینصورت در صورت وقوع خطا، exception filter روی اکشن متد اجرا خواهد شد. در اپلیکیشن ها نه تنها مدیریت حطاها مهم است بلکه بهتر است آنها را جایی مانند دیتابیس ذخیره نموده و جزییات آنها را به مسئول اپلیکیشن ایمیل کنیم تا مسئول مربوطه بتواند خطاها را در سطح اپلیکیشن رفع نماید که این باعث میشود برنامه ما بیشتر قابلیت نگه داری داشته باشد.
در این مقاله ما سعی داریم که یک فیلتر سفارشی برای خطاها بنویسیم و خطاها را به همراه جزییات در جدولی درون دیتابیس ذخیره نماییم.
قسمت 2 – Log کردن خطاها در اپلیکیشن ASP.NET MVC
ما این مثال را با استفاده از VS 2015 پیاده سازی خواهیم نمود. شما میتوانید آن را با VS 2013 نیز پیاده سازی نمایید.
مرحله 1: ویژوال استودیو را باز نمایید و یک پروژه جدید از نوع ASP.NET Web Application ایجاد نمایید و نام آن را MVC_LoggingExcepion_with_ExceptionFilter بگذارید. بر روی ok کلیک نمایید. سپس Template را از نوع Empty مانند شکل زیر انتخاب نمایید:
مرحله 2: در پروژه یک دیتابیس از نوع sql server با نام ApplicationDB.mdf ایجاد نمایید. دیتابیس را خالی بگذارید. ما از معماری Entity Framework Code first استفاده مینماییم.
مرحله 3: در پروژه بر روی پوشه Models راست کلیک کرده و یک ADO.NET Entity Data Model جدید با نام ApplicationModel اضافه نمایید. در ویزاردی که باز میشود در پنجره Choose Model Contents، گزینه Code First from Database را همانند شکل زیر انتخاب نمایید:
روی دکمه Next کلیک نمایید و سپس دیتابیس ApplicationDB.mdf را از پنجره انتخاب نمایید. finish را کلیک کنید. سپس کلاس ApplicationModel که از کلاس DbContext مشتق شده است ایجاد میشود.
مرحله 4: در پوشه Model، یک کلاس جدید با نام EmployeeInfo.cs ایجاد نمایید و کدهای زیر را درون آن قرار دهید:
using System.ComponentModel.DataAnnotations; namespace MVC_LoggingExcepion_with_ExceptionFilter.Models { public class EmployeeInfo { [Key] public int EmpNo { get; set; } public string EmpName { get; set; } public string Designation { get; set; } public decimal Salary { get; set; } } } |
این یک کلاس موجودیت برای کارمندان است. ما از این برای ایجاد جدول EmployeeInfo با استفاده از رویکرد Code-First در دیتابیس استفاده مینماییم. فیلد EmpNo در کلاس بالا، با استفاده از از Key Attribute کلید اصلی جدول به حساب می آید.
مرحله 5: از آنحایی که ما در این مثال قصد Log کردن خطاها در دیتابیس را داریم سپس به یک جدول برای ذخیره این اطلاعات نیاز داریم. در پوشه Models یک کلاس جدید با نام ExceptionLogger.cs ایجاد نمایید و کدهای زیر را در آن قرار دهید:
using System; using System.ComponentModel.DataAnnotations; namespace MVC_LoggingExcepion_with_ExceptionFilter.Models { public class ExceptionLogger { [Key] public int Id { get ; set ; } public string ExceptionMessage { get ; set ; } public string ControllerName{ get ; set ; } public string ExceptionStackTrace { get ; set ; } public DateTime LogTime { get ; set ; } } } |
کد بالا شامل خصوصیاتی برای ذخیره جزییات خطاها میباشد.
مرحله 6: در فایل کلاس ApplicationModel.cs، خصوصیات مشخص شده زیر را درون کلاس ApplicationModel اضافه نمایید:
public partial class ApplicationModel : DbContext { public ApplicationModel() : base ( "name=ApplicationModel" ) { } public DbSet Employees { get ; set ; } public DbSet ExceptionLoggers{ get ; set ; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { } } |
این خصوصیات، دیتابیس را با جداول Employees و ExceptionLoggers به روز مینماید.
مرحله 7: در پروژه، یک پوشه به نام CustomFilter اضافه نمایید. در این پوشه، یک کلاس به نام ExceptionHandlerAttribute با کدهای زیر ایجاد نمایید:
using MVC_LoggingExcepion_with_ExceptionFilter.Models; using System; using System.Web.Mvc; namespace MVC_LoggingExcepion_with_ExceptionFilter.CustomFilter { public class ExceptionHandlerAttribute : FilterAttribute, IExceptionFilter { public void OnException(ExceptionContext filterContext) { if (!filterContext.ExceptionHandled) { ExceptionLogger logger = new ExceptionLogger() { ExceptionMessage = filterContext.Exception.Message, ExceptionStackTrace = filterContext.Exception.StackTrace, ControllerName = filterContext.RouteData.Values[ "controller" ].ToString(), LogTime = DateTime.Now }; ApplicationModel ctx = new ApplicationModel(); ctx.ExceptionLoggers.Add(logger); ctx.SaveChanges(); filterContext.ExceptionHandled = true ; } } } } |
کلاس بالا از کلاس FilterAttribute مشتق شده است. این به این معنی است که کلاس ExceptionHandlerAttribute به عنوان ActionFilter در چرخه MVC Filter Attribute بیان شده در قسمت 1 استفاده میشود. این کلاس، رابط کاربری IExceptionFilter و متد OnException() آن را پیاده سازی مینماید. این مند شامل منطقی برای ذخیره جزییات خطا در جدول ExceptionLoggers موجود در دیتابیس میباشد.
مرحله 8: ما باید exception filter را در اپلیکیشن MVC ثبت نماییم. به این منظور، در پوشه App_Start، یک کلاس جدید به نام FilterConfig اضافه نمایید. در این کلاس، کدهای زیر را قرار دهید:
using MVC_LoggingExcepion_with_ExceptionFilter.CustomFilter; using System.Web.Mvc; namespace MVC_LoggingExcepion_with_ExceptionFilter.App_Start { public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add( new ExceptionHandlerAttribute()); } } } |
توجه: از آنجایی که ما یک MVC template از نوع Empty را در مرحله 1، انتخاب کرده بودیم به همین خاطر کلاس بالا در اپلیکیشن ما موجود نبود. اما اگر شما یک Template آماده MVC را در این مرحله انتخاب میکردید در نتیجه این کلاس در اپلیکیشنمان وجود خواهد داشت. این کلاس برای افزودن تمام Action Filter ها در global configuration مورد استفاده قرار میگیرد. زمانیکه اپلیکیشن start میشود این کلاس نمونه سازی خواهد شد.
در Global.asax، کدهای مشخص شده زیر را در Application_Start اضافه نمایید:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); } |
مرحله 8: در پوشه Controllers، یک کنترلر جدید با نام EmployeeController به همراه اکشن متدهای زیر ایجاد نمایید:
using MVC_LoggingExcepion_with_ExceptionFilter.CustomFilter; using MVC_LoggingExcepion_with_ExceptionFilter.Models; using System; using System.Linq; using System.Web.Mvc; namespace MVC_LoggingExcepion_with_ExceptionFilter.Controllers { public class EmployeeController : Controller { ApplicationModel ctx = new ApplicationModel(); // GET: Employee public ActionResult Index() { var Emps = ctx.Employees.ToList(); return View(Emps); } public ActionResult Create() { return View( new EmployeeInfo()); } [ExceptionHandler] [HttpPost] public ActionResult Create(EmployeeInfo Emp) { if (Emp.Designation == "Manager" && (Emp.Salary < 40000 || Emp.Salary > 80000)) { throw new Exception( "Salary is not Matching with Manager Designatgion" ); } else if (Emp.Designation == "Operator" && (Emp.Salary < 10000 || Emp.Salary > 20000)) { throw new Exception( "Salary is not Matching with Operator Designatgion" ); } else { ctx.Employees.Add(Emp); ctx.SaveChanges(); } return View(Emp); } } } |
متد Index، تمام کارمندان را برمیگرداند. متد Create با HttpPost، یک آبجکت از نوع Employee را به عنوان ورودی دریافت میکند. این متد شامل یک سرس قوانین Business روی فیلد حقوق کارمند مبتنی بر ویژگی Designation میباشد. اگر این قوانین fail شود، سپس یک Exception اتفاق می افتد. اگر داده های Employee در تمام این قوانین درست باشد، سپس آن در جدول Employee ذخیره خواهد شد.
مرحله 9: یک ویوی Index و Create از اکشن متدهای Index و Create را Scaffold نمایید.در RouteConfig.cs، روت را همانند کد مشخص شده در زیر تغییر دهید:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute( "{resource}.axd/{*pathInfo}" ); routes.MapRoute( name: "Default" , url: "{controller}/{action}/{id}" , defaults: new { controller = "Employee" , action = "Index" , id = UrlParameter.Optional } ); } |
کلاس بالا، اکشن متد Index از EmployeeController را اجرا میکند که Index view را نمایش خواهد داد.
مرحله 10: breakpoint را درون متد Create از نوع HttpPost در EmployeeController و در متد OnException در کلاس ExceptionHandlerAttribute قرار دهید. اپلیکیشن را اجرا نمایید که در نتیجه Index View نمایش داده میشود. روی لینک Create New برای نمایش Create View کلیک نمایید. داده ها را در Create view نمایش داده شده در شکل زیر وارد نمایید:
داده های وارد شده در تصویر بالا را وارد نمایید و سپس بر روی Create کلیک نمایید. برنامه وارد debug Mode خواهد شد. در نتیجه condition برای Operator همانند شکل زیر منجربه ایجاد exception خواهد شد:
F5 را بزنید. برنامه وارد ExceptionHandlerAttribute خواهد شد جاییکه عملیات Log کردن دیتابیس اتفاق خواهد افتاد. اپلیکیشن را ببندید. جدول ExceptionLoggers در دیتابیس، پیغام Log شده در تصویر زیر را نمایش خواهد داد:
نتیجه گیری
در یک وب اپلیکیشن، مدیریت خطا و log کردن آنها خیلی مهم است. ASP.NET MVC این کار را با استفاده از Exception Action Filter آسان میسازد. ما میتوانیم از action filter سفارشی برای log کردن این خطاها با توجه به نیازهای بیزنسمون استفاده نماییم.
برای دانلود سورس برنامه اینجا کلیک نمایید.