مدیریت خطاها در mvc
شناسه پست: 1305
بازدید: 1687

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

ما هنگام توسعه برنامه با استفاده از ASP.NET MVC، با انواع مختلغی از اعتبارسنجی های Model تا مدیریت خطاها روبه رو هستیم. MVC از ویژگی Action Filter برای پیاده سازی منطق برنامه از جمله request Logging، authorization و HandlerError استفاده مینماید. HandlerError برای مدیریت خطاها در حین اجرای اکشن متدها استفاده میشود.

معمولا مرسومه که ما از بلاک try-catch-finally برای مدیریت خطاها استفاده میکنیم. اما در مورد ASP.NET MVC، از آنجایی که با اکشن متدها کار میکنیم، ما معمولا کدی را برای هدایت به Index View با استفاده از RedirectToAction(“Index”) مینویسیم. ولی اگر در برنامه ما با خطایی روبه رو شدیم و نیاز داشته باشیم که کاربر را به Error View هدایت کنیم باید چه کاری انجام دهیم؟ این چیزی است که ممکن در تمام اکشن متدها در کنترلرها نیاز داشته باشیم.

در مثال زیر ما قصد داریم راه های مختلفی را برای مدیریت خطاها به شما نشان دهیم:

مرحله 1: Visual Studio را باز نمایید و یک اپلیکیشن جدید از نوع MVC 5 ایجاد نمایید و نام آن را MVC5_Exceptions بزارید.

ایجاد اپلیکیشن در MVC

سپس یک پروژه MVC با پوشه هایی برای Model، View, App_Data و Controllers و غیره ایجاد میشود. پوشه View یک زیرپوشه به نام Shared در درون خودش دارد که شامل یک View به نام Error.cshtml میباشد. این یک error view با یک Model Class به نام HandleErrorInfo میباشد. این کلاس شامل سازنده با پارامترهای زیر میباشد:

public HandleErrorInfo(Exception exception, string controllerName, string actionName);

 

این سازنده، نوع خطا، نام کنترلر و نام اکشن متد را برای نمایش جزییات خطا در Error View به عنوان ورودی دریافت میکند. این کلاس بنابراین خصوصیاتی را برای خطا، کنترلر و اکشن متد در اختیار ما قرار میدهد که ما میتوانیم از این خصوصیات برای پیاده سازی منطق در سطح View استفاده نماییم. (ما آن را در مراحل زیر خواهیم دید.)

مرحله 2: در پوشه App_Data، یک دیتابیس جدید از نوع Sql Server به نام Application  بسازید و جدول EmployeeInfo را در آن ایجاد نمایید:

CREATE TABLE [dbo].[EmployeeInfo] (
    [EmpNo]       INT          NOT NULL,
    [EmpName]     VARCHAR (50) NOT NULL,
    [DeptName]    VARCHAR (50) NOT NULL,
    [Designation] VARCHAR (50) NOT NULL,
    [Salary]      DECIMAL (18) NOT NULL,
    PRIMARY KEY CLUSTERED ([EmpNo] ASC)
);

 

مرحله 3: در پوشه Models ، راست کلیک نمایید و یک ADO.NET Entity Data Model جدید به نام ApplicationEDMX ایجاد نمایید. در پنجره Wizard مورد نظر، Application.mdf و سپس جدول EmployeeInfo  را انتخاب نمایید. بعد از پایان Wizard، جدول مپ شده به صورت زیر نمایش داده خواهد شد:

جدول Employee

مرحله 4: در پوشه کنترلر، یک کنترلر جدید با نام EmployeeInfoController ایجاد نمایید. حال اکشن متدهای زیر را در آن ایجاد نمایید که در آن EF که ایجاد کرده بودیم را فراخوانی مینماید:

public class EmployeeInfoController : Controller
{
 
    ApplicationEntities ctx;
 
    public EmployeeInfoController()
    {
        ctx = new ApplicationEntities(); 
    }
 
    // GET: EmployeeInfo
    public ActionResult Index()
    {
       var Emps = ctx.EmployeeInfoes.ToList();
        return View(Emps);
    }
 
    
 
    // GET: EmployeeInfo/Create
    public ActionResult Create()
    {
        var Emp = new EmployeeInfo();
        return View(Emp);
    }
 
    // POST: EmployeeInfo/Create
    [HttpPost]
    public ActionResult Create(EmployeeInfo Emp)
    {
 
            ctx.EmployeeInfoes.Add(Emp);
            ctx.SaveChanges();
 
            return RedirectToAction("Index");
 
    }
}

 

از کد بالا،  ویوهای Index و Create را scaffold نمایید.

پیاده سازی Exceptionها در ASP.NET MVC

به عنوان یک توسعه دهنده، ما باید جایی را که امکان رخداد خطا وجود دارد را درک نماییم. خوشبختانه ما بلاک try-catch را برای مدیریت خطاها در اختیار داریم. در ASP.NET MVC در کلاس کنترلر، خطاها میتوانند توسط شیوه های زیر مدیریت شوند:

  1. اسفاده از بلاک try-catch در هر اکشن متد
  2. override کردن متد OnException از کلاس پایه Controller 
  3. مدیریت خطاها در سطح global با استفاده از کلاس FilterConfig 

از آنجایی که MVC یک الگو برای توسعه اپلیکیشنهای وب است، این وظیفه ماست که در مورد استفاده از مناسب ترین شیوه بالا در اپلیکیشنمان فکر نماییم.

استفاده از مکانیسم آسان مدیریت خطا با استفاده از روش سنتی مانند try-catch

مرحله 5: در EmployeeInfoController، پیاده سازی اکشن متد [HttpPost] Create را همانند شکل زیر تغییر دهید:

[HttpPost]
public ActionResult Create(EmployeeInfo Emp)
{
    try
    {
        ctx.EmployeeInfoes.Add(Emp);
        ctx.SaveChanges();
 
        return RedirectToAction("Index");
    }
    catch(Exception ex)
    {
     return View("Error",new HandleErrorInfo(ex,"EmployeeInfo","Create"));
    }
}

 

در کد بالا، ما از یکی از overload های ()View استفاده مینماییم. این نام View را به عنوان اولین ورودی و Model را به عنوان دومین ورودی دریافت میکند. (در مرحله 1، ما در مورد Error View و کلاس مدل HandleError صحبت کردیم). این کد، Error view را برمیگرداند. این اکشن متد سعی میکند که آبجکت EmployeeInfo را با استفاده از EF ذخیره نماید. بنابراین نوع خطایی که امکان دارد اینجا اتفاق بیفتد از نوع DbUpdateException میباشد که در صورت وارد نمودن کلید اصلی به صورت تکراری یا هرنوع مشکل دیگری از بروزرسانی مربوط به دیتابیس رخ میدهد.

مرحله 6: Error.Cshtml را باز نمایید و کد زیر را در آن قرار دهید:

@model System.Web.Mvc.HandleErrorInfo

class="text-danger">Error.@Model.Exception.Message

  

class="text-danger">An error occurred while processing your request.

 
@Html.ActionLink("Back", @Model.ActionName, @Model.ControllerName)

 

این کد از آبجکت مدل HandleErrorInfo برای نمایش پیغامهای خطا در View استفاده مینماید.

برای تست، اپلیکیشن را اجرا نمایید و توسط url زیر به Create View بروید.

http:///EmployeeInfo/Create

 

داده ها را با یک کلید تکراری برای فیلد EmpNo وارد نمایید. سپس روی دکمه Submit کلیک نمایید. در این صورت، صفحه error به شکل زیر نمایش داده خواهد شد:

خطای دیتابیس mvc

زمانیکه روی لینک Back کلیک کنید، همان create view دومرتبه نمایش داده خواهد شد. در برنامه نویسی سنتی، این رویکرد، زمانیکه، exception مختص به اکشن متد خاصی میباشد، رویکرد خوبی به نظر میرسد در غیر اینصورت ما نیاز داریم که آن را در هر اکشن متد به صورت جداگانه بنویسیم که کار غیرمعقولانه ای به نظر میرسد.

پیاده سازی Exception Handling با override کردن متد OnException در کلاس پایه Controller 

Controller مهمترین قسمت MVC میباشد و نقش اصلی پردازش request را بازی میکند. از آنجایی که نام کنترلر و اکشن متد در url برای پردازش request در MVC مورد استفاده قرار میگیرد، ما میتوانیم کنترلر را برای بکارگیری تعدادی منطق رایج در اجرا که برای مدیریت request مورد استفاده قرار میگیرد پیکربندی نماییم. از آنجایی که exception handling، قسمتی از پردازش Request به حساب می آید، از این جهت برای handle کردن exception ها در طول اجرای اکشن متدها، میتوانیم کنترلر را برای اجرای منطق خطا در برنامه، برنامه ریزی نماییم. برای این منظور باید متد OnException از کلاس پایه Controller را  override نماییم.

مرحله 1: EmployeeInfoController را باز نمایید و متد OnException  را override نمایید:

protected override void OnException(ExceptionContext filterContext)
{
   Exception exception = filterContext.Exception;
   //Logging the Exception
   filterContext.ExceptionHandled = true;
 
 
   var Result = this.View("Error", new HandleErrorInfo(exception,
       filterContext.RouteData.Values["controller"].ToString(),
       filterContext.RouteData.Values["action"].ToString()));
 
   filterContext.Result = Result;
    
}

 

متد بالا، کلاس ExceptionContext را به عنوان ورودی میگیرد. این کلاس، context مورد نظر را برای استفاده از اطلاعات خطای اتفاق افتاده در پردازش request کنونی در اختیار ما قرار میدهد. این کلاس دارای ویژگی Exception میباشد که با استفاده از آن، خطایی که در طول اجرای اکشن متد اتفاق افتاده است، شناخته میشود. ویژگی ExceptionHandled  مشخص میکند که آیا exception مدیریت شده است یا خیر. ویژگی Result برای مشخص کردن اکشن مورد نظر برای نمایش مورد استفاده قرار میگیرد. کد بالا یک آبجکت ViewResult را با استفاده از متد ()View در کلاس کنترلر میسازد. در این آبجکت، نام Error view و یک نمونه از کلاس HandlerErrorInfo به همراه نام اکشن و کنترلر در route  کنونی ازآبجکت filterContext  پاس داده شده است. در آخر، آبجکت ViewResult به ویژگی Result در آبجکت filterContext برای نمایش Error View پاس داده میشود.

مرحله 2: در کلاس EmployeeInfoController، متد Create ازنوع HttpPost را به صورت زیر تغییر دهید:

[HttpPost]
public ActionResult Create(EmployeeInfo Emp)
{
    try
    {
        ctx.EmployeeInfoes.Add(Emp);
        ctx.SaveChanges();
 
        return RedirectToAction("Index");
    }
    catch(Exception ex)
    {
        throw ex;
    }
}

 

برنامه را اجرا نمایید و سعی کنید یک رکورد تکراری در جدول EmployeeInfo  با کلید تکراری در فیلد EmpNo ثبت نمایید. یک exception همانند شکل زیر اتفاق خواهد افتاد:

خطای mvc

f5 را بزنید. متد OnException اجرا خواهد شد و Error view زیر نمایش داده خواهد شد:

خطای دیتابیس mvc

رویگرد بالا، رویکرد خوبیست. اما محدودیتی که اینجا وجود دارد این است که محدود به کنترلر خاصی میباشد و برای تک تک کنترلرها باید به صورت جداگانه نوشته شود.

در ورژن های ASP.NET MVC 4 و 5، ما یک مکانیسمی را برای مدیریت خطاها در سطح global ارائه داده ایم به طوریکه بتوانیم کنترلر را برای مدیریت خطاها بدون هیچ گونه تلاشی برنامه ریزی نماییم.

مدیریت خطاها به صورت Global با استفاده از FilterConfig در MVC 4 و 5

زمانیکه ما یک اپلیکیشن MVC 4 یا 5 را با استفاده از ویژوال استودیو میسازیم، پروژه ما، کلاس FilterConfig در پوشه App_Start را به صورت زیر شامل میشود:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
}

 

این کلاس شامل متد ایستاتیک RegisterGlobalFilters با پارامتر ورودی GlobalFilterCollection میباشد. این کلاس شامل تمام فیلترهای global است. هماکنون HandleErrorAttribute در آن اضافه شده است. HandleErrorAttribute متفاوت از Application_Error میباشد. به این دلیل که HandleErrorAttribute  برای مدیریت خطاها در طول اجرای Controller در MVC مورد استفاده قرار میگیرد. درحالیکه Application_Error زمانیکه request خارج از MVC است اتفاق می افتد، زیرا ControllerContext اینجا بسته شده است. HandleErrorAttribute روی کلاس کنترلر یا اکشن متد درون آن در MVC برای مدیریت خطاهای ناشناخته توسط اکشن متدها اعمال میشود. RegisterGlobalFilters در فایل Global.asax به صورت زیر فراخوانی میشود:

FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

 

این تمام exception های اتفاق افتاده در تمام اکشن متدها در تمام کنترلرهای موجود در MVC رامدیریت مینماید.

حال با همدیگه پیاده سازی Exception filter برای اپلیکیشنمان را ببینیم:

مرحله 1: فایل Web.Config در اپلیکیشن را باز نمایید و تگ زیر را درون تگ برای مدیریت خطاهای سفارشی اضافه نمایید:

<customErrors mode="On"></customErrors>

 

مرحله 2: متد OnException از کلاس EmployeeInfoController را حذف (و یا کامنت) نمایید.

مرحله 3: در کلاس EmployeeInfoController، ویژگی زیر را اضافه نمایید:

[HandleError(ExceptionType = typeof(DbUpdateException), View = "Error")]
    public class EmployeeInfoController : Controller
    {

 

یک فیلتر خطا با Exception  از نوع DbUpdateException  و View با نام Error بر روی کلاس controller اعمال شده است.

برنامه را اجرا نمایید و سعی کنید یک رکورد تکراری در جدول EmployeeInfo  با کلید تکراری در فیلد EmpNo ثبت نمایید. این کد خطا را درون بلاک catch  در اکشن متد Create گیر می اندازد. f5 را بزنید، صفحه Error  به صورت زیر نمایش داده خواهد شد:

خطای دیتابیس mvc

ما میتوانیم چند ویژگی HandleError را بر روی کنترلر (یا اکشن متدهای درون آن) برای مدیریت خطاها اضافه نماییم. این رویکرد خطاها را در سطح Controller مدیریت مینماید.

نتیجه گیری: ما راه های زیادی را برای handle کردن exception ها در ASP.NET MVC در اختیار داریم. این وظیفه ماست که به عنوان یک توسعه دهنده، مناسب ترین شیوه را بر جسب نیازمان انتخاب نماییم.

برای دانلود سورس برنامه اینجا کلیک نمایید.

نویسنده

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