Efectuarea de erori și excepții în asp.net mvc

Care este modalitatea acceptată de a gestiona erorile și de a trage excepția în structura asp.net mvc? Consensul general este să existe excepții. Deci, ce strat (View sau Controller) ați gestiona excepțiile (prindeți-le/afișați text ușor de utilizat, etc.). Mă gândesc că se face în controlor? EDITATĂ: Aș dori să evite repetarea aceluiași cod de manipulare a erorilor în fiecare acțiune a controlorului . De aceea, caut un exemplu concis de implementare a procesării erorilor fără a repeta același cod.

9
Aceasta este ceea ce am ajuns să fac JS în interiorul jQuery ajax "> stackoverflow.com/questions/8249479/…
adăugat autor sarsnake, sursa
Folosirea unei tehnici de interceptare ar putea ajuta cu adevărat aici. O mulțime de recipiente de injecție de dependență susțin acest lucru; Știu despre Spring.NET și Castle Windsor, care au ambele integrare cu asp.net mvc. Ai intercepta apelurile către controlorii tăi și să rezolvi excepțiile în interceptorul tău.
adăugat autor Marijn, sursa
Această întrebare poate fi un bun punct de plecare: stackoverflow.com/questions/5442231/…
adăugat autor Marijn, sursa

7 răspunsuri

Manipularea excepțiilor în controler ar putea duce la o mulțime de cod repetitiv. O abordare mai bună este să le rezolvați într-un filtru de acțiune care extinde HandleErrorAttribute . Acolo puteți înregistra excepții și apoi să redirecționați către o pagină care afișează un mesaj frumos care indică utilizatorului că ceva nu a mers bine.

Cu toate acestea, există câteva cazuri în care trebuie să rezolvați excepțiile în metodele controlerului dvs., de exemplu, atunci când vă puteți recupera de la o excepție și puteți afișa utilizatorului un mesaj adecvat, de exemplu o excepție care a fost aruncată de stratul de afacere, indicând că valorile pe care le-ați furnizat, nu sunt valide. În acest caz, ar trebui să prindeți excepția specifică și să afișați utilizatorului aceeași vizualizare împreună cu un mesaj corespunzător.

EDIT:

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
adăugat
Puteți furniza un exemplu clar scris despre modul de aplicare a erorilor de manipulare folosind HandleErrorAttribute? mulțumesc
adăugat autor sarsnake, sursa
Aș dori să urmăresc această soluție, dar nu știu unde este codul de mai sus? Controlorul meu?
adăugat autor sarsnake, sursa
mulțumesc, astfel încât CustomErrorHandlerAttribute în sine merge unde? același fișier? conteaza?
adăugat autor sarsnake, sursa
mulțumesc, va trebui să încerc. Voi extinde recompensa, pentru că cel mai probabil nu am timp să încerc azi
adăugat autor sarsnake, sursa
Vă voi da recompensa, am ajuns să fac ceva similar, dar l-am extins și pe JsonResult, așa că pot să returnez mesajul de eroare personalizat în codul meu js
adăugat autor sarsnake, sursa
Puteți adnota clasele de controler cu acest atribut. Am actualizat răspunsul.
adăugat autor uvita, sursa
Puteți crea o clasă diferită într-un alt director dacă doriți (de exemplu, Filtre)
adăugat autor uvita, sursa

Bunătatea ta este corectă. Definitiv trebuie să fie controlerul. Iată un exemplu:

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

Note:

  • Verificați mai întâi ModelState. Dacă nu este validă, întoarceți-vă. Ia-ți drumul.
  • Nu continuați să actualizați un serviciu de înregistrare. Utilizați o instanță singleton și utilizați DI pentru ao injecta în controlerele dvs., astfel încât să lucrați cu o interfață, de exemplu ILoggingService . Acest lucru înseamnă, de asemenea, că puteți adăuga alte funcții în serviciul de înregistrare (de exemplu, suport pentru trimiterea prin e-mail).
  • Straturile dvs. inferioare (servicii, repository etc.) pot arunca erori (personalizate sau integrate), deci este important ca controlerul să le captureze, deoarece este "agregatorul" și ceea ce este responsabil pentru fluxul dintre client și server.
  • Utilizați ModelState.AddModelError pentru a adăuga erori, astfel încât vizualizarea să le poată fi afișată. De asemenea, puteți utiliza excepții personalizate, care pot fi ușor de utilizat și le afișați utilizatorilor. Pentru erorile de nivel inferior (SQL, etc), puteți adăuga pur și simplu o eroare generică la ModelState ("Ne pare rău, a apărut o eroare. Încercați din nou mai târziu").
3
adăugat
@ Amir978 - Nu urmez. Unde cere el să facă aceste lucruri în această întrebare?
adăugat autor RPM1984, sursa
@ Amir978 - fără probleme. Nu sunteți sigur de ce ați "salva numele de utilizator" - care este scenariul? Inscrie-te? Poate postați o altă întrebare. În ceea ce privește raportul de eroare - pentru care este Elmah. Și puteți folosi ceva de genul Sentinel pentru a capta erorile de înregistrare.
adăugat autor RPM1984, sursa
Prefer să nu repet același cod în fiecare acțiune.
adăugat autor sarsnake, sursa
Cum puteți salva numele de utilizator în acest caz? Și cum să creați un raport despre toate erorile care au avut loc?
adăugat autor Amir978, sursa
Ai dreptate. Tocmai am cerut să găsesc o soluție pentru mine.
adăugat autor Amir978, sursa

Nu este așa de ușor cum ar părea.

Dacă aveți nevoie de o manipulare excepțională centralizată, cea mai rapidă modalitate este de a suprascrie metoda OnException din Controller

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

După cum puteți vedea în această metodă, am capturat toate excepțiile PEDException nefolosite, dacă aveți o excepție personalizată ridicată de la bl, cred că având un controler de bază cu o metodă OnException poate fi o soluție bună, dar există cazuri în care acest lucru poate potențial fi periculos. În general, cred că este mai bine să definiți un Atribut personalizat (Extinderea ErrorAttributeFilter) pentru a evita o mulțime de alte probleme (cu caching, de exemplu, acțiunile dvs. pur și simplu nu vor fi executate deloc, în timp ce atributul va fi întotdeauna executat).

Consultați aici pentru mai multe informații

2
adăugat

Ați putea crea un controler de bază personalizat și moșteniți din clasa Base Controller. Apoi, înlocuiți opțiunea OnException în contul dvs. personalizat. Apoi fiecare moștenire a controlorului dvs. va moșteni de la noul dvs. controlor de bază personalizat.

Alternativ, puteți suprascrie evenimentul Application_Error în global.asax

1
adăugat

Depinde într-adevăr de ceea ce urmărești să atingi.

Pentru scenariul simplu de afișare a unui mesaj personalizat cu privire la eventualele erori, puteți utiliza configurația de erori personalizate vechi vechi în site-ul web.config.

Rețineți că acest lucru va fi folosit pentru erorile care nu ajung până la punctul de a ajunge la controlori. Ca în cazul în care există probleme cu valori speciale care nu sunt corect codate în adresele URL.

Atributul HandleError sau propriul atribut personalizat vă permit un control mai bun pe care doriți să îl doriți în alte scenarii.

Rețineți că dacă doriți să aplicați atributul de eroare personalizat al mânerului tuturor controlorilor, puteți face acest lucru aplicând-l ca filtru global de acțiune. În acest fel, nu este necesar să o aplicați în mod explicit pentru fiecare controlor.

0
adăugat

De obicei, suprascriu Application_Error în fișierul Global.asax și redirecționez utilizatorul la o pagină de excepție generică pentru fiecare excepție, apoi trimite un e-mail cu câteva detalii. Destul de simplu. Iată ce folosesc de obicei:

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
adăugat

Deși sunt de acord că operațiile de logare a excepțiilor ar trebui să fie centralizate la nivelul controlerului BASE din motive de consecvență ... De asemenea, consider că ar trebui să se ia în considerare și o modalitate standard de a afișa mesaje de eroare prietenoase utilizatorilor în interfața de utilizare în plus față de cerințele de înregistrare. Excepțiile capturate și înregistrate la nivelul Controlor pot fi apoi înfășurate într-o "Excepție prietenoasă" și transmise unui procesor excepțional central. Fișierul error.cshtml din dosarul de vizualizare partajat este un loc bun pentru a procesa și a afișa "Excepția prietenoasă" care împachetează "Excepția reală" care a apărut și a fost înregistrată la nivelul controlerului și a fost transmisă la error.cshtml.

Folosesc un control personalizat al bazei și setarea HandleErrorAttribute utilizând metoda RegisterGolbalFilters din clasa FilterConfig chemată din evenimentul Global.asax Application_Start

Codul bazei de cod

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 Cod

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

}

Această parte este destul de standard ... Ceea ce faci în continuare, într-adevăr, face o diferență pentru utilizator. Folosesc o clasă de erori care să proceseze excepțiile centralizate și să afișeze mesajul bazat pe contextual "prietenos" pe utilizator pe baza controlerului și a acțiunii care a cauzat excepția. În exemplul de mai jos, am mutat blocul de captură pe pagina error.cshtml din dosarul Vizualizări/partajate pentru a-l menține simplu. Codul de mai jos este doar un exemplu de cod, deoarece mesajul de eroare prietenos se va schimba în funcție de contextul aplicației și este posibil să doriți să mutați procesul de excepție în clasă pentru a vă întreține.

//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
adăugat