HiveBrain v1.2.0
Get Started
← Back to all entries
debugcsharpModerate

Error Handling - Controller's OnException and Application_Error

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
handlingerrorcontrollerandapplication_erroronexception

Problem

I'm pretty new to MVC. Below is the code that I have added to an existing MVC 5 application. The code is using log4net to log any run-time error/exception.

NOTE: I could override OnException() in MVC filter "HandleErrorAttribute" but I preferred to have this functionality in my custom Controller base class.

Can anyone please review and provide feedback?

BaseController.cs

public class BaseController: Controller
{
    private log4net.ILog logger;

    protected override void OnException(ExceptionContext filterContext)
    {
        //Log error
        logger = log4net.LogManager.GetLogger(filterContext.Controller.ToString());
        logger.Error(filterContext.Exception.Message, filterContext.Exception);

        //If the request is AJAX return JSON else redirect user to Error view.
        if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
        {
            //Return JSON
            filterContext.Result = new JsonResult
            {
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                    Data = new { error = true, message = "Sorry, an error occurred while processing your request." }
            };
        }
        else
        {
            //Redirect user to error page
            filterContext.ExceptionHandled = true;
            filterContext.Result = this.RedirectToAction("Index", "Error");
        }
        base.OnException(filterContext);
    }
}


HomeController.cs

public class HomeController : BaseController
{
    ...
}


Global.asax.cs

```
public class MvcApplication : System.Web.HttpApplication
{
private static readonly ILog log = LogManager.GetLogger(typeof(MvcApplication));

protected void Application_Error(object sender, EventArgs e)
{
//Log exception
Exception exception = Server.GetLastError();
log.Error(exception);

//Clear error from response stream
Response.Clear();
Server.ClearError();

Solution

The OnException() methods does 2 things.

  • It logs the exception



  • It creates an return value



So let us refactor these two points to two separate methods.

private void LogException(ExceptionContext exceptionContext)
{
    logger = log4net.LogManager.GetLogger(exceptionContext.Controller.ToString());
    logger.Error(exceptionContext.Exception.Message, exceptionContext.Exception);
}


private const String XMLHttpRequest = "XMLHttpRequest";
private const String XRequestedWithHeadername= "X-Requested-With";
private const String JSONErrorMessage = "Sorry, an error occurred while processing your request.";
private void CreateExceptionContextResult(ExceptionContext exceptionContext)
{
    if (exceptionContext.HttpContext.Request.Headers[XRequestedWithHeadername] == XMLHttpRequest )
    {
        //Return JSON
        exceptionContext.Result = new JsonResult
        {
                JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                Data = new { error = true, message = JSONErrorMessage }
        };
    }
    else
    {
        //Redirect user to error page
        exceptionContext.ExceptionHandled = true;
        exceptionContext.Result = this.RedirectToAction("Index", "Error");
    }
}


If we want to handle the different error types, we should create a new class which only keeps the values for actionName and controllerName.

public class ActionControllerName
{
     public String ActionName { get; private set; }
     public String ControllerName { get; private set; }
     public ActionControllerName(String actionName, String controllerName)
     {
         ActionName = actionName;
         ControllerName = controllerName;
     }
}


Now we add a Dictionary and a method to fill this dictionary, which should be called in the constructor, or injected by an property.

private const int DefaultEntryKey = -1;
private Dictionary redirectDictionary = new Dictionary();
private void FillRedirectDictionary()
{
    redirectDictionary.Add(DefaultEntryKey , new ActionControllerName("Index", "Error"));
    // more to add
}


Next we implement a method to get a RedirectToRouteResult passing the exception as parameter

private RedirectToRouteResult GetRedirect(Exception exception)
{
    int errorCode = DefaultEntryKey;
    HttpException httpException = exception as HttpException;
    if (httpException != null) 
    {
        errorCode = httpException.ErrorCode;
        if (!redirectDictionary.ContainsKey(errorCode))
        {
            errorCode = DefaultEntryKey;
        }
    }
    ActionControllerName acn = redirectDictionary[errorCode];
    return this.RedirectToAction(acn.ActionName , acn.ControllerName);
}


We go back to this else part

else
{
    //Redirect user to error page
    exceptionContext.ExceptionHandled = true;
    exceptionContext.Result = this.RedirectToAction("Index", "Error");
}


and change it to

else
{
    //Redirect user to error page
    exceptionContext.ExceptionHandled = true;
    exceptionContext.Result = GetRedirect(exceptionContext.Exception);
}

Code Snippets

private void LogException(ExceptionContext exceptionContext)
{
    logger = log4net.LogManager.GetLogger(exceptionContext.Controller.ToString());
    logger.Error(exceptionContext.Exception.Message, exceptionContext.Exception);
}
private const String XMLHttpRequest = "XMLHttpRequest";
private const String XRequestedWithHeadername= "X-Requested-With";
private const String JSONErrorMessage = "Sorry, an error occurred while processing your request.";
private void CreateExceptionContextResult(ExceptionContext exceptionContext)
{
    if (exceptionContext.HttpContext.Request.Headers[XRequestedWithHeadername] == XMLHttpRequest )
    {
        //Return JSON
        exceptionContext.Result = new JsonResult
        {
                JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                Data = new { error = true, message = JSONErrorMessage }
        };
    }
    else
    {
        //Redirect user to error page
        exceptionContext.ExceptionHandled = true;
        exceptionContext.Result = this.RedirectToAction("Index", "Error");
    }
}
public class ActionControllerName
{
     public String ActionName { get; private set; }
     public String ControllerName { get; private set; }
     public ActionControllerName(String actionName, String controllerName)
     {
         ActionName = actionName;
         ControllerName = controllerName;
     }
}
private const int DefaultEntryKey = -1;
private Dictionary<int,ActionControllerName> redirectDictionary = new Dictionary<int,ActionControllerName>();
private void FillRedirectDictionary()
{
    redirectDictionary.Add(DefaultEntryKey , new ActionControllerName("Index", "Error"));
    // more to add
}
private RedirectToRouteResult GetRedirect(Exception exception)
{
    int errorCode = DefaultEntryKey;
    HttpException httpException = exception as HttpException;
    if (httpException != null) 
    {
        errorCode = httpException.ErrorCode;
        if (!redirectDictionary.ContainsKey(errorCode))
        {
            errorCode = DefaultEntryKey;
        }
    }
    ActionControllerName acn = redirectDictionary[errorCode];
    return this.RedirectToAction(acn.ActionName , acn.ControllerName);
}

Context

StackExchange Code Review Q#60759, answer score: 12

Revisions (0)

No revisions yet.