Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling errors and exceptions in asp.net mvc

What is the accepted way to handling errors and trapping exception in asp.net mvc structure? The general consensus is to have exceptions bubble up. So what layer (View or Controller) would you handle the exceptions (catch them/ display user friendly text, etc). My hunch is that it is done in the controller? EDITED: I would like to avoid repeating the same error handling code in every controller action..hence I am looking for a concise example of how to implement error handling without repeating the same code.

like image 874
sarsnake Avatar asked Oct 27 '11 22:10

sarsnake


3 Answers

Handling exceptions in the controller could lead to a lot of repetitive code. A better approach is to handle them in an action filter that extends HandleErrorAttribute. There you could log exceptions and then redirect to a page that displays a nice message indicating the user that something went wrong.

However, there are some cases where you need to handle exceptions in your controller methods, for example, when you can recover from an exception and show the user an appropriate message, for example an exception that was throw by your business layer indicating that the values you provided are not valid. In that case, you should catch the specific exception and show the user the same view along with an appropriate message.

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
{
...
}
like image 165
uvita Avatar answered Oct 08 '22 23:10

uvita


Your hunch is correct. Definetely needs to be the controller. Here's an example:

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

Notes:

  • Check ModelState first. If not valid, return. Get's it out of the way.
  • Don't keep newing up a logging service. Use a singleton instance, and use DI to inject it into your controllers so your working off an interface, e.g ILoggingService. This also means you can add other functionality into the logging service (emailing support, for example).
  • Your lower layers (services, repository, etc) may throw errors (custom, or built-in), so it's important the controller catches them, as it's the "aggregator" and what is responsible for the flow between the client and the server.
  • Use ModelState.AddModelError to add errors so the View can show them. You can also use custom exceptions which may be user friendly and you show them to the user. For lower-level errors (SQL, etc), you can simply add a generic error to the ModelState ("Sorry, an error occured. Please try again later").
like image 20
RPM1984 Avatar answered Oct 08 '22 23:10

RPM1984


It's not so easy as it could seem.

If you need centralized exception handling, fastest way is to override the OnException method in the 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);
        }

As you can see in this method I've catched all unhandled PEDException exceptions, if you have custom exception raised from your bl, I think having a base controller with an OnException method can be a good solution, however there are cases where this can potentially be dangerous. Generally I think that it's better to define a custom Attribute (Extending ErrorAttributeFilter) to avoid a lot of other problems (with caching for example, your actions simply won't be executed at all, while the attribute will always be executed).

Please see here for further informations

like image 40
BigMike Avatar answered Oct 08 '22 22:10

BigMike