Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HandleErrorInfo using MVC2 - Model is null?

I have an MVC 2 web application, which is nearing release. Until now I have had custom errors turned off but I would like them working when I go production ready.

I have set up my web.config with the following:

<customErrors mode="On" defaultRedirect="/Error/">
  <error statusCode="404" redirect="/Error/NotFound "/>
</customErrors>

The 404 one works perfectly, and NotFound is an action which maps directly to a View that just shows a pretty standard 404 page using my own Site.Master file.

For anything other than a 404, I want a user to be able to view the exception details. (This is an internal application, and there is no security risk in doing so).

The Error default action Index is set to return a View() which I have defined. What I cannot figure out is how to pass the exception information into the View?

This looked promising:

http://devstuffs.wordpress.com/2010/12/12/how-to-use-customerrors-in-asp-net-mvc-2/

But when I use the View with:

<%@ Page Title="" Language="C#" 
    MasterPageFile="~/Views/Shared/Bootstrap.Master"
    Inherits="System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>" %>

The error page itself throws an error, as HandleErrorInfo is null. Obviously an error in the custom error causes MVC2 a whole host of problems, and the result is a yellow screen of death.

I guess either I missed something key in the blog, or it doesn't explain how to get the HandleErrorInfo to be anything other than null without setting the Error attribute for every single one of my Controllers/Actions.

Also, I am aware that MVC3 deals with this better, and I am aware Razor is very good. It has not been used for this project nor will this project be changed to use it. So please no "Use MVC3" answers.

like image 770
KingCronus Avatar asked Jul 18 '12 13:07

KingCronus


1 Answers

The HandleErrorInfo is null because you are performing a redirect in customErrors.

This is the idea I'm trying out in my latest project and I updated for MVC 2. I didn't use customErrors because i can't invoke a controller action without performing a redirect (i guess).

Application Error

protected void Application_Error(Object sender, EventArgs e)
{
    GlobalErrorHandler.HandleError(((HttpApplication)sender).Context, Server.GetLastError(), new ErrorController());
}

Global error handler

public class GlobalErrorHandler
{
    public static void HandleError(HttpContext context, Exception ex, Controller controller)
    {
        LogException(ex);

        context.Response.StatusCode = GetStatusCode(ex);
        context.ClearError();
        context.Response.Clear();
        context.Response.TrySkipIisCustomErrors = true;

        if (IsAjaxRequest(context.Request))
        {
            ReturnErrorJson(context, ex);
            return;
        }

        ReturnErrorView(context, ex, controller);
    }

    public static void LogException(Exception ex)
    {
        // log the exception
    }

    private static void ReturnErrorView(HttpContext context, Exception ex, Controller controller)
    {
        var routeData = new RouteData();
        routeData.Values["controller"] = "Error";
        routeData.Values["action"] = GetActionName(GetStatusCode(ex));

        controller.ViewData.Model = new HandleErrorInfo(ex, " ", " ");
        ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(context), routeData));
    }

    private static void ReturnErrorJson(HttpContext context, Exception ex)
    {
        var json = string.Format(@"success: false, error:""{0}""", ex.Message);
        context.Response.ContentType = "application/json";
        context.Response.Write("{" + json + "}");
    }

    private static int GetStatusCode(Exception ex)
    {
        return ex is HttpException ? ((HttpException)ex).GetHttpCode() : 500;
    }

    private static bool IsAjaxRequest(HttpRequest request)
    {
        return request.Headers["X-Requested-With"] != null && request.Headers["X-Requested-With"] == "XMLHttpRequest";
    }

    private static string GetActionName(int statusCode)
    {
        string actionName;

        switch (statusCode)
        {
            case 404:
                actionName = "NotFound";
                break;

            case 400:
                actionName = "InvalidRequest";
                break;

            case 401:
                actionName = "AccessDenied";
                break;

            default:
                actionName = "ServerError";
                break;
        }

        return actionName;
    }

    public static bool IsDebug
    {
        get
        {
            bool debug = false;

#if DEBUG
            debug = true;
#endif
            return debug;
        }
    }
}

Error controller

public class ErrorController : Controller
{
    public ActionResult AccessDenied()
    {
        return View("AccessDenied");
    }

    public ActionResult InvalidRequest()
    {
        return View("InvalidRequest");
    }

    public ActionResult NotFound()
    {
        return View("NotFound");
    }

    public ActionResult ServerError()
    {
        return View("ServerError");
    }
}

ServerError view

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    ServerError
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>ServerError</h2>

    <% if (Model.Exception != null ) { %>
        <p>
          Controller: <%= Model.ControllerName %>
        </p>
        <p>
          Action: <%= Model.ActionName %>
        </p>
        <p>
          Message: <%= Model.Exception.Message%>
        </p>
        <p>
          Stack Trace: <%= Model.Exception.StackTrace%>
        </p>
    <% } %>

</asp:Content>
like image 110
VJAI Avatar answered Oct 04 '22 07:10

VJAI