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.
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
{
...
}
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:
ILoggingService
. This also means you can add other functionality into the logging service (emailing support, for example).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").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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With