Обробка помилок і винятків у asp.net mvc

Що таке прийнятний спосіб обробки помилок та вилучення у структурі asp.net mvc? Загальний консенсус полягає в тому, щоб винятки лягли в бік. Так який шар (View або Controller) ви обробили винятками (ловите їх/показуєте зручний текст тощо). Мені здається, що це робиться в контролері? РЕДАКТИ: Я хотів би уникати повторення того самого коду обробки помилок у кожній дії контролера . Тому я шукаю стислий приклад того, як реалізувати обробку помилок, не повторюючи той самий код.

9
Ось що я закінчив робити JS стороні всередині jQuery ajax "> stackoverflow.com/questions/8249479/ …
додано Автор sarsnake, джерело
Використання методу перехоплення дійсно може допомогти тут. Багато підтримувальних контейнерів залежно від цього; Я знаю Spring.NET і Castle Windsor, які обидва мають інтеграцію з asp.net mvc. Ви будете перехоплювати дзвінки до своїх контролерів і обробляти винятки у вашому перехоплювачі.
додано Автор Marijn, джерело
Це питання може стати гарною відправною точкою: stackoverflow.com/questions/5442231/…
додано Автор Marijn, джерело

7 Відповіді

Обробка винятків у контролері може призвести до повторюваного коду. Кращий підхід - обробляти їх у фільтрі дій, який поширюється на HandleErrorAttribute . Тут ви можете зареєструвати винятки, а потім переспрямовувати на сторінку, яка відображає приємний повідомлення, що вказує користувачеві, що щось пошкоджено.

Тим не менш, є деякі випадки, коли вам потрібно обробляти винятки в методах контролера, наприклад, коли ви можете відновитись після винятку та показати користувачеві відповідне повідомлення, наприклад, винятком, яке було викинуто вашим бізнес-рівнем, що вказує на те, що значення ви вказали недійсні. У такому випадку ви повинні спіймати конкретне виняток і показати користувачеві той самий вигляд разом з відповідним повідомленням.

РЕДАГУВАТИ:

public class CustomErrorHandlerAttribute : HandleErrorAttribute
{
     public override void OnException(ExceptionContext filterContext)
     {
         var logger = log4net.LogManager.GetLogger("SomeLoggerHere");

         logger.Error("An unhandled error occurred", filterContext.Exception);

         if (filterContext.HttpContext.Request.IsAjaxRequest())
         {
             filterContext.HttpContext.Response.Clear();
             filterContext.HttpContext.Response.Status = "500 Internal Server Error";
             filterContext.Result = new JsonResult { Data = new { ErrorMessage = filterContext.Exception.Message } };
             filterContext.ExceptionHandled = true;                
         }
         else
         {
             base.OnException(filterContext);
         }

    }

}

EDIT 2: Then you use the attribute like this:

[CustomErrorHandler]
public class AnyController : Controller
{
...
}
15
додано
Не могли б ви надати чітко написаний приклад того, як здійснювати обробку помилок за допомогою HandleErrorAttribute? спасибі
додано Автор sarsnake, джерело
Я хотів би продовжити це рішення, але я незрозуміла, де виходить код вище? Мій контролер?
додано Автор sarsnake, джерело
дякую, так само сам CustomErrorHandlerAttribute йде де? той же файл? це важливо?
додано Автор sarsnake, джерело
дякую, мені доведеться спробувати це. Я розширюю щедрості, тому що я, швидше за все, не маю часу спробувати це сьогодні
додано Автор sarsnake, джерело
Я дам вам нагороду, я закінчив робити щось подібне, але розширював його для роботи з JsonResult, так що я можу повернути користувальницьке повідомлення про помилку в мой JS код
додано Автор sarsnake, джерело
Ви можете коментувати свої контролери за допомогою цього атрибута. Я оновив відповідь.
додано Автор uvita, джерело
Ви можете створити інший клас в іншому каталозі, якщо хочете (наприклад, фільтри).
додано Автор uvita, джерело

Ваша думка правильна. Потрібно бути контролером. Ось приклад:

[HttpPost]
public ActionResult Create(OrderViewModel model)
{
   if (!ModelState.IsValid)
     return View(model);

   try
   {
      repository.Save(model);
      unitOfWork.Commit();
      return RedirectToAction("Index");
   }
   catch (Exception exc)
   {
      _loggingService.Error(exc);
      ModelState.AddModelError("KeyUsedInView", exc.Message);//or, show a generic error.
   }

   return View(model);
}

Примітки:

  • Спочатку перевірте ModelState. Якщо недійсний, поверніться. Вийди це з ладу.
  • Не зберігайте нову службу реєстрації. Використовуйте екземпляр singleton і використовуйте DI для введення його у свої контролери, щоб ви працювали з інтерфейсом, наприклад, ILoggingService . Це також означає, що ви можете додати інші функції до служби ведення журналу (наприклад, електронна пошта).
  • Нижні шари (служби, репозиторій тощо) можуть викидати помилки (користувацькі чи вбудовані), тому важливо, щоб контролер ловив їх, оскільки це "агрегатор" і що відповідає для потоку між клієнтом і сервером.
  • Використовуйте ModelState.AddModelError , щоб додати помилки, щоб переглядати їх. Ви також можете використовувати спеціальні винятки, які можуть бути зручними для користувачів і відображати їх користувачеві. Для помилок нижчого рівня (SQL та ін.) Ви можете просто додати загальну помилку ModelState ("Вибачте, помилка сталася. Будь ласка, спробуйте пізніше").
3
додано
@ Amir978 - я не слідуй. Де він просить зробити ці речі в питанні?
додано Автор RPM1984, джерело
@ Amir978 - немає пробсів. Не знаєте, чому ви "зберегли ім'я користувача" - який сценарій? Реєстрація? Можливо, поставте ще одне питання. Що стосується звіту про помилку - саме для цього є Елма. І ви можете використовувати щось на зразок Sentinel для захоплення помилок журналу.
додано Автор RPM1984, джерело
Я вважаю за краще не повторювати той самий код у кожному Action.
додано Автор sarsnake, джерело
Як ви можете зберегти ім'я користувача в цьому випадку? І як створити звіт про всі помилки, що сталися?
додано Автор Amir978, джерело
Ви правильно. Я просто попросив знайти рішення для себе.
додано Автор Amir978, джерело

Це не так просто, як може здатися.

Якщо вам потрібна централізована обробка виключень, найшвидшим способом є переопределення методу OnException у Контролері

[NonAction]
        protected override void OnException(ExceptionContext filterContext)
        {

            this.Session["ErrorException"] = filterContext.Exception;

            if (filterContext.Exception.GetType() == typeof(PEDException))
            {
               //Mark exception as handled
                filterContext.ExceptionHandled = true;

               //... logging, etc

               //Redirect
                filterContext.Result = this.RedirectToAction( "ShowError", "Errors");
            }

            base.OnException(filterContext);
        }

Як ви можете бачити в цьому методі, я зібрав всі невикористані виключення PEDException, якщо у вас є спеціальне виключення з вашого bl, я думаю, що наявність базового контролера з методом OnException може бути гарним рішенням, однак є випадки, коли це може потенційно бути небезпечним. Як правило, я думаю, що краще визначити спеціальний атрибут (Extending ErrorAttributeFilter), щоб уникнути багатьох інших проблем (наприклад, кешування ваших дій просто не виконуватимуться, а атрибут буде завжди виконуватися).

Будь ласка, перегляньте тут для отримання додаткової інформації.

2
додано

Ви можете створити користувальницький базовий контролер і наслідувати клас базового контролера. Потім замініть OnException у своєму користувацькому контролері. Потім кожен з ваших контролерів успадкує від вашого нового власного контролера бази.

Крім того, ви можете замінити подія Application_Error у global.asax

1
додано

Це дійсно залежить від того, що ви прагнете досягти.

Для простого сценарію відображення користувацького повідомлення з будь-якими помилками навколо, ви можете використовувати старі добрі конфігурації помилок у веб-інтерфейсі web.config.

Зауважте, що це буде використовуватися для помилок, які навіть не досягають точки досяжності контролерів. Подібно до того, коли у URL-адресах виникають проблеми з неправильно закодованими спеціальними значеннями.

Атрибут HandleError або ваш власний призначений атрибут дають вам змогу отримати більш точний контроль у інших сценаріях.

Зауважте, що якщо ви хочете застосувати власний атрибут помилки для обробки до всіх контролерів, це можна зробити, застосувавши його як глобальний фільтр дій. Таким чином, вам не потрібно явно застосовувати його до кожного контролера.

0
додано

Зазвичай я замінюю Application_Error у файлі Global.asax і перенаправляю користувача на загальну сторінку виключень для кожного винятку, а потім надіслати електронний лист з деякими подробицями. Досить простий. Ось що я звичайно користуюся:

protected void Application_Error(object sender, EventArgs e)
{
    if (Request.Url.ToString().StartsWith("http://localhost:"))
        return;
    string msg;
    Exception ex = Server.GetLastError().GetBaseException();
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("Exception Found");
    sb.AppendLine("Timestamp: " + System.DateTime.Now.ToString());
    sb.AppendLine("Error in: " + Request.Url.ToString());
    sb.AppendLine("Browser Version: " + Request.UserAgent.ToString());
    sb.AppendLine("User IP: " + Request.UserHostAddress.ToString());
    sb.AppendLine("Error Message: " + ex.Message);
    sb.AppendLine("Stack Trace: " + ex.StackTrace);
    msg = sb.ToString();
    Server.ClearError();
    YourMailHelper.SendException("Your Site Exception", msg);
    Response.Redirect("~/Error.html");
}
0
додано

Хоча я погоджуюсь, що операції журналювання виключень повинні бути централізовані на рівні контролера BASE для забезпечення послідовності ... Я також вважаю, що стандартний спосіб відображення дружніх до користувача повідомлень про помилки в інтерфейсі користувача слід розглянути на додаток до вимог реєстрації. Винятки, захоплені та зареєстровані на рівні контролера, можуть бути загорнутими в "Дружній виняток" і передані до центрального процесора виключень. Файл error.cshtml у спільній папці перегляду є хорошим місцем для обробки та відображення "Дружнього винятку", який містить "фактичне виключення", яке сталося, і було зареєстровано на рівні контролера і передано до error.cshtml.

Я використовую користувальницький елемент керування базою та встановлюю HandleErrorAttribute за допомогою методу RegisterGolbalFilters у класі FilterConfig, викликаному з події Global.asax Application_Start

Код Base_Controller

public class Base_Controller : Controller
{
    protected override void OnException(ExceptionContext filterContext)
    {
        Exception e = filterContext.Exception;
        //Custom Exception Logging Here
        //Log Exception e
        //Elmah.Mvc.ElmahController ec = new Elmah.Mvc.ElmahController();
        base.OnException(filterContext);
    }
}

Code FilterConfig.cs

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

Global.asax Application_Start Code

void Application_Start(object sender, EventArgs e)
{
   //Code that runs on application startup
    AreaRegistration.RegisterAllAreas();
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

}

Ця частина є досить стандартною ... Їй те, що ви робите далі, дійсно змінює користувача. Я використовую клас errorhandling для обробки винятків централізовано та показує користувачам "Friendly" контекстні повідомлення на основі Controller та Action, які викликали виняток. У наведеному нижче зразку коду я перемістив блок лову на сторінку error.cshtml в папці «Перегляди/Спільна», щоб тримати його простим. Нижче наведений код - це лише приклад коду, оскільки дружня повідомлення про помилку буде змінюватися залежно від контексту програми, і ви можете хотіти перемістити обробку виключень у клас заради технічного обслуговування.

//Check for Transport Exception with "Actual Exception" stored
//in the inner exception property
if (Model.Exception.InnerException != null)
{
    errFriendly = Model.Exception.Message;
    modelEx = Model.Exception.InnerException;
}
else
{
    modelEx = Model.Exception;
}
try
{           
    throw modelEx; 
}
catch (System.Data.SqlClient.SqlException ex)
{
    //Display Landing page friendly error for exception caused by home controller
    //Display generic data access error for all other controllers/actions
    if (Model.ActionName == "Index" && Model.ControllerName == "Home")
    {errFriendly = "Landing page cannot display product data...";}
    else
    {errFriendly = "Problem Accessing Data...";}
    errType = ex.GetType().ToString();
    errActual = ex.Message;
}

To download the entire code sample see the blog post at: http://www.prodataman.com/Blog/Post/119/MVC-Custom-Exception-Handling

0
додано
var chat = new Chat();
var chat = new Chat();
642 учасників

Обсуждение вопросов по C# / .NET / .NET Core / .NET Standard / Azure Сообщества-организаторы: — @itkpi — @dncuug