0

I am using a combination of HandleErrorAttribute and a Custom Error Controller in the Custom Errors section for error handling in a MVC3 application. The logic is to handle any Ajax request errors via the OnException handler in the HandleErrorAttribute and rest of the errors via the ErrorController. Below is the code -

// Handle any ajax error via HandleErrorAttribute 
public class HandleAjaxErrorAttribute : System.Web.Mvc.HandleErrorAttribute
 {
    public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
    {



        filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
        var exception = filterContext.Exception;

        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            //some logic
            filterContext.ExceptionHandled = true;
        }

    }
}

//Handle remaining errors in the Error Controller

public class ErrorController : Controller
{

    protected override void HandleUnknownAction(string actionName)
    {
        var exception = Server.GetLastError(); //Can't get the exception object here.
        //some logic
    }



}

The Web.config settings:

<customErrors mode="On" defaultRedirect="~/Error">
</customErrors>

When any non-ajax exception occurs the control flows from the OnException block to the HandleUnknownAction in the Error Controller. However I am unable to get the Exception object. How can I get the Exception object in the Error Controller ?

Also, do you think this two step approach is a proper way to handle errors in MVC3 ? I thought of handling error in a centralized location using the Application_Error event handler but as per my research this is not the recommed approach for MVC applications.

devanalyst
  • 1,348
  • 4
  • 28
  • 55
  • Why not simply direct the other errors the same way you do with Ajax? What I mean is, if it's Ajax do something and if it is no, call the view and give it the exception object (that's what I do in my MVC project). You need to realize that the CustomErrors just redirects to a given url and sends it the Url that caused the error. It does not, however, store any information, it's simply a url redirection. – oamsel Jun 13 '12 at 12:24
  • HandleErrorAttribute cannot handle errors that are outside the MVC pipeline (404, 403 etc.), so I still need the CustomErrors and I need to call it via Action instead of calling the View directly. Also I don't want to hard code the view name in the OnException handler. Besides having all error handling at one place is better. – devanalyst Jun 13 '12 at 12:42
  • @OmerAmsel, it would be helpful if you could post some code. – devanalyst Jun 13 '12 at 12:49

3 Answers3

0

Regarding my comment above, I'm pasting my code here in order to clarify what I did.

This code goes inside a BaseController (highly recommended to make one, see this article: http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx)

and I've overridden the OnException method (My main goal here was to differentiate between Ajax calls and regular calls):

protected override void OnException(ExceptionContext filterContext)
    {
        // Set to true or else it'll still propagate
        filterContext.ExceptionHandled = true;

        string message;



        // Check if it's an Ajax request
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            // Here I check if the custom error is on, if it isn't I'll paste the
            // stacktrace and the error
            if (!filterContext.HttpContext.IsCustomErrorEnabled)
            {
                message = filterContext.Exception.Message + filterContext.Exception.StackTrace;
            }
            else
            {
                message = "Something went wrong";
            }

            // TODO: Decide what to do if ajax
            filterContext.HttpContext.Response.StatusCode = 401;
            var json = new JsonResult {Data = message};
            json.ExecuteResult(this.ControllerContext);
        }
        else
        {
            // This is basically the same conditional, but instead of sending a JSON
            // I'll redirect to a view and give it the exception
            if (!filterContext.HttpContext.IsCustomErrorEnabled)
            {
                ViewData.Model = filterContext.Exception;
                this.View("Error").ExecuteResult(this.ControllerContext);
            }
            else
            {
               this.View("../Error/ServerError").ExecuteResult(this.ControllerContext);
            }

        }
    }
}

Important to notice about my comment above is that I'm not saying not to use CustomError, but think of it only when errors that are, indeed, outside the pipeline occur (401 for example).

oamsel
  • 475
  • 3
  • 10
0

I might be wrong but I believe that you do not need to worry about error info in your controller because MVC automatically wires it up in the View. So if you make your (Razor) view like this, it should work:

@model System.Web.Mvc.HandleErrorInfo

Any logging can be done in your attribute but if you need to do it in the error controller, create a LogError action on your Error controller that takes a HandleErrorInfo argument and call it something like:

@Html.RenderAction("LogError", Model)

PS I haven't tested this but it should work.

Rob Kent
  • 5,183
  • 4
  • 33
  • 54
0

My Error message was not getting displayed and ErrorController was not kicking in till I did following setting

<customErrors mode="On" defaultRedirect="~/Error">
</customErrors>
Taryn
  • 242,637
  • 56
  • 362
  • 405