Asp.net mvc'deki hataları ve özel durumları işleme

ASP.NET mvc yapısında hataları ele alma ve istisna yakalama için kabul edilen yol nedir? Genel fikir birliği istisna olmaktır. Yani hangi katmanları (View veya Controller) istisnaları halledersiniz (yakalayın/kullanıcı dostu metni görüntüleyin, vb.). Benim önsezim kontrolörde yapıldığı mı? DÜZENLENMİŞ: Her bir denetleyici eyleminde aynı hata işleme kodunu tekrarlamaktan kaçınmak istiyorum . Bu nedenle, aynı kodu tekrar etmeden hata işlemeyi nasıl gerçekleştireceğinize dair özlü bir örnek arıyorum.

9
katma yazar sarsnake, kaynak
Bir müdahale tekniğini kullanmak burada gerçekten yardımcı olabilir. Çok bağımlılık enjeksiyon kapları bunu destekler; Her ikisi de asp.net mvc ile entegrasyonu olan Spring.NET ve Castle Windsor'u biliyorum. Denetleyicilerinize yapılan çağrıları engeller ve önleyicideki istisnaları halledersiniz.
katma yazar Marijn, kaynak
Bu soru iyi bir başlangıç ​​noktası olabilir: stackoverflow.com/questions/5442231/…
katma yazar Marijn, kaynak

7 cevap

Kontrol ünitesindeki istisnaları tutmak, tekrar eden birçok koda yol açabilir. Daha iyi bir yaklaşım, HandleErrorAttribute öğesini genişleten bir eylem filtresinde bunları işlemektir. Burada istisnalar kaydedebilir ve daha sonra kullanıcıya bir şeyler ters gittiğini belirten güzel bir mesaj gösteren bir sayfaya yönlendirebilirsiniz.

Ancak, denetleyici yöntemlerinizdeki istisnaları ele almanız gereken bazı durumlar vardır, örneğin, bir istisnadan kurtarıp kullanıcıya uygun bir mesaj gösterdiğinizde, örneğin iş katmanınızın değerlerin olduğunu belirten bir istisna Sağladınız geçerli değil. Bu durumda, belirli bir özel durumu yakalamanız ve kullanıcıya uygun bir mesajla birlikte aynı görünümü göstermeniz gerekir.

DÜZENLEME:

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
katma
HandleErrorAttribute kullanarak nasıl hata işleme uygulandığınıza dair açık bir örnek verebilir misiniz? teşekkür ederim
katma yazar sarsnake, kaynak
Bu çözümü takip etmek isterim, fakat yukarıdaki kodun nereye gittiğini belirsiz miyim? Kontrol cihazım?
katma yazar sarsnake, kaynak
teşekkürler, bu yüzden CustomErrorHandlerAttribute kendisi nereye gider? aynı dosya? önemli mi?
katma yazar sarsnake, kaynak
teşekkürler, denemek zorundayım. Büyük olasılıkla bugün denemek için zamanım olmayacağından ben de ödülü uzatacağım
katma yazar sarsnake, kaynak
Size ödül vereceğim, benzer bir şey yapmamı bitirdim ama JsonResult'u işlemek için uzattım, böylece özel hata mesajım JS koduma dönebilsin
katma yazar sarsnake, kaynak
Denetleyici sınıflarınıza bu özellik ile not ekleyebilirsiniz. Cevabı güncelledim.
katma yazar uvita, kaynak
İsterseniz farklı bir dizinde farklı bir sınıf oluşturabilirsiniz (örn. Filtreler)
katma yazar uvita, kaynak

Önsezilerin doğru. Kesinlikle kontrolör olması gerekiyor. İşte bir örnek:

[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);
}

Notlar:

  • Önce ModelState'i kontrol edin. Geçerli değilse, geri dönün. Yoldan çekil.
  • Kayıt servisini yenilemeye devam etmeyin. Tekil bir örnek kullanın ve denetleyicilerinize enjekte etmek için DI'yi kullanın, böylece bir arabirim üzerinde çalışın, ör. ILoggingService . Bu ayrıca, kayıt servisine başka işlevler ekleyebilirsiniz (örneğin, e-postayla gönderme).
  • Alt katmanlarınız (hizmetler, depolar, vb.) hataları (özel veya yerleşik) atabilir, bu nedenle "toplayıcı" olduğu ve sorumlu olanın denetleyici 'nin yakalanması önemlidir. İstemci ve sunucu arasındaki akış için.
  • Görünümün gösterebileceği hataları eklemek için ModelState.AddModelError 'ı kullanın. Kullanıcı dostu olabilecek ve bunları kullanıcıya gösterebileceğiniz özel istisnaları da kullanabilirsiniz. Alt düzey hatalar için (SQL, vb.), ModelState'e genel bir hata ekleyebilirsiniz ("Üzgünüz, bir hata oluştu. Lütfen daha sonra tekrar deneyin").
3
katma
@ Amir978 - takip etmiyorum. Sorudaki bu şeyleri nerede yapmak istiyor?
katma yazar RPM1984, kaynak
@ Amir978 - yok probs. Neden "kullanıcı adını" kaydedeceğinizden emin değil - senaryo nedir? Kaydol? Belki başka bir soru yollayın. Hata raporuna gelince - Elmah bunun için. Ve kayıt hatalarını yakalamak için Sentinel gibi bir şey kullanabilirsiniz.
katma yazar RPM1984, kaynak
Her bir eylemde aynı kodu tekrar etmemeyi tercih ederim.
katma yazar sarsnake, kaynak
Bu durumda kullanıcı adını nasıl kaydedebilirsiniz? Ve meydana gelen tüm hataların bir raporu nasıl oluşturulur?
katma yazar Amir978, kaynak
Haklısın, sadece kendim için bir çözüm bulmayı istedim.
katma yazar Amir978, kaynak

Göründüğü kadar kolay değil.

Eğer merkezi istisna işlemine ihtiyacınız varsa, en hızlı yol Denetleyicideki OnException yöntemini geçersiz kılmaktır.

[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);
        }

Bu yöntemde gördüğünüz gibi, tüm işlenmemiş PEDException istisnalarını yakaladım, eğer bl özelliğinden özel bir istisnaınız varsa, bir OnException yöntemiyle bir temel denetleyiciye sahip olmanın iyi bir çözüm olabileceğini düşünüyorum, ancak bunun potansiyel olarak olabileceği durumlar var tehlikeli ol. Genel olarak, diğer birçok problemden kaçınmak için özel bir Öznitelik (Genişletme HatasıAttributeFilter) tanımlamanın daha iyi olduğunu düşünüyorum (örneğin, önbelleğe alma işlemiyle, öznitelik her zaman yürütülürken, eylemleriniz hiç yürütülmeyecektir).

Daha fazla bilgi için lütfen burayı inceleyin.

2
katma

Özel bir temel denetleyici oluşturabilir ve Temel Denetleyici sınıfından devralabilirsiniz. Ardından, kişisel denetleyicinizdeki OnException'ı geçersiz kılın. Ardından, kontrol cihazınızın her birinin yeni özel ana kontrol cihazınızdan miras kalmasını sağlayın.

Alternatif olarak, global.asax'daki Application_Error olayını geçersiz kılabilirsiniz.

1
katma

Gerçekten neyi başarmayı hedeflediğinize bağlı.

Çevrenizdeki herhangi bir hata üzerinde özel bir mesaj göstermenin basit senaryosu için web.config'inizdeki eski eski özel hata yapılandırmalarını kullanabilirsiniz.

Bunun, kontrolörlere ulaşma noktasına bile ulaşmayan hatalarda kullanılacağını unutmayın. URL'lerde düzgün şekilde kodlanmamış özel değerler ile ilgili sorunlar olduğunda olduğu gibi.

HandleError özniteliği veya kendi özel özniteliğiniz, istediğiniz diğer senaryolarda istediğiniz daha iyi bir denetim sağlar.

Özel tanıtıcı hata özniteliğinizi Tüm denetleyicilere uygulamak isterseniz, bunu genel bir eylem filtresi olarak uygulayarak yapabilirsiniz. Bu şekilde, her denetleyiciye açıkça uygulamanız gerekmez.

0
katma

Genelde Application_Error'ı Global.asax dosyasında geçersiz kılar ve kullanıcıyı her özel durum için genel bir özel durum sayfasına yönlendirir ve sonra bazı ayrıntılar içeren bir e-posta gönderir. Yeterince basit. İşte genellikle kullandığım şey:

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
katma

Tutarlılık işlemleri için BASE denetleyici seviyesinde istisna kayıt işlemlerinin merkezi olarak yapılmasını kabul etmeme rağmen ... Ayrıca, kullanıcı gereksinimindeki hatalara ek olarak kullanıcı arayüzünde kullanıcı dostu hata mesajlarının görüntülenmesinin standart bir yol olduğuna da inanıyorum. Kontrolör seviyesinde yakalanan ve kaydedilen istisnalar daha sonra bir "Dostu İstisna" ya sarılabilir ve merkezi bir istisna işlemcisine aktarılabilir. Paylaşılan görünüm klasöründeki error.cshtml dosyası, gerçekleşen ve Denetleyici düzeyinde kaydedilen ve error.cshtml'ye iletilen "Gerçek İstisnayı" saran "Kolaylık Özel Durumu" nı işlemek ve görüntülemek için iyi bir yerdir.

Özel bir temel denetim kullanıyorum ve Global.asax Application_Start olayından çağrılan FilterConfig sınıfında RegisterGolbalFilters yöntemini kullanarak HandleErrorAttribute ayarını kullanıyorum

Base_Controller Kodu

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);
    }
}

FilterConfig.cs Kodu

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

Global.asax Application_Start Kodu

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);

}

Bu bölüm oldukça standarttır ... Yapacağınız şey, gerçekten kullanıcı için bir fark yaratır. İstisnaları merkezi olarak işlemek için bir hata sınıfı sınıfını kullanıyorum ve istisnaya neden olan Denetleyici ve Eylem'i temel alan kullanıcıya "Kolay" içerik tabanlı iletileri gösteriyorum. Aşağıdaki örnek kodda, catch bloğunu basit tutmak için Views/Shared klasöründeki error.cshtml sayfasına taşındım. Aşağıdaki kod, pratik hata mesajının uygulama içeriğine bağlı olarak değişeceğinden örnek koddur ve istisna işlemeyi bakım amacıyla sınıfa taşımak isteyebilirsiniz.

//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
katma