Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I provide a HandleErrorInfo model with an object?

I'm writting an ASP.NET MVC app and I encountered following problem. I want to make custom error pages with detailed information about the errors (such as error message or what controller caused the error), but I can't provide the model on the view with an HandleErrorInfo object.

At first I configured the Web.config file to handle custom errors:

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

Then I created the Error Controller to manage the error views:

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

    public ViewResult NotFound()
    {
        Response.StatusCode = 404;
        return View("NotFound");
    }
}

And on the View I added the model type:

@model System.Web.Mvc.HandleErrorInfo

But the model is null everytime and I don't know how to pass the right object with error details to the view and where this object should be created. Can you help me? Is it even possible to do it this way?

like image 317
Roman Suska Avatar asked Jul 24 '14 11:07

Roman Suska


3 Answers

So you're dealing with two different 'layers' here. <customErrors> is apart of the main ASP.NET framework. Then HandleError is apart of the MVC framework. When you use <customErrors> ASP.NET is unaware of how to pass the exception information into a view, it'll just load/redirect you to the error page.

To make the view work, you need to keep everything inside the MVC ecosystem. So you'll need to use HandleErrorAttribute, or a varient . You can see in the source code of the attribute it attempts to detect when an unhandled exception has happened, and if custom errors are enabled, then spins up a new ViewResult with an instance of HandleErrorInfo that has the exception information inside of it.

To be noted, in a default MVC project, HandleErrorAttribute is applied as a global filter inside of the App_Start/FilterConfig.cs file.

like image 69
Steven V Avatar answered Oct 12 '22 15:10

Steven V


I found another way. Sending error model to view from controller.

1. Global.asax

protected void Application_Error(object sender, EventArgs e)
{
    var httpContext = ((MvcApplication)sender).Context;
    var currentController = " ";
    var currentAction = " ";
    var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));

    if (currentRouteData != null)
    {
        if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
        {
            currentController = currentRouteData.Values["controller"].ToString();
        }

        if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
        {
            currentAction = currentRouteData.Values["action"].ToString();
        }
    }

    var ex = Server.GetLastError();
    //var controller = new ErrorController();
    var routeData = new RouteData();
    var action = "GenericError";

    if (ex is HttpException)
    {
        var httpEx = ex as HttpException;

        switch (httpEx.GetHttpCode())
        {
            case 404:
                action = "NotFound";
                break;

            // others if any
        }
    }

    httpContext.ClearError();
    httpContext.Response.Clear();
    httpContext.Response.StatusCode = ex is HttpException ? ((HttpException)ex).GetHttpCode() : 500;
    httpContext.Response.TrySkipIisCustomErrors = true;

    routeData.Values["controller"] = "Error";
    routeData.Values["action"] = action;
    routeData.Values["exception"] = new HandleErrorInfo(ex, currentController, currentAction);

    IController errormanagerController = new ErrorController();
    HttpContextWrapper wrapper = new HttpContextWrapper(httpContext);
    var rc = new RequestContext(wrapper, routeData);
    errormanagerController.Execute(rc);
}

2. Error Controller:

public class ErrorController : Controller
{
    //
    // GET: /Error/

    public ActionResult Index()
    {
        return RedirectToAction("GenericError", new HandleErrorInfo(new HttpException(403, "Dont allow access the error pages"), "ErrorController", "Index"));
    }

    public ViewResult GenericError(HandleErrorInfo exception)
    {            
        return View("Error", exception);
    }

    public ViewResult NotFound(HandleErrorInfo exception)
    {         
        ViewBag.Title = "Page Not Found";
        return View("Error", exception);
    }        
}

3. View/Shared/Error.cshtml:

@model System.Web.Mvc.HandleErrorInfo
@{   
    ViewBag.Title = ViewBag.Title ?? "Internal Server Error";
}
<div class="list-header clearfix">
    <span>Error : @ViewBag.Title</span>
</div>
<div class="list-sfs-holder">
    <div class="alert alert-error">
        An unexpected error has occurred. Please contact the system administrator.
    </div>
    @if (Model != null && HttpContext.Current.IsDebuggingEnabled)
    {
        <div>
            <p>
                <b>Exception:</b> @Model.Exception.Message<br />
                <b>Controller:</b> @Model.ControllerName<br />
                <b>Action:</b> @Model.ActionName
            </p>
            <div style="overflow: scroll">
                <pre>
                    @Model.Exception.StackTrace
                </pre>
            </div>
        </div>
    }
</div>

4. web.config:

<system.web>
    <customErrors mode="Off" />
</system.web>

Credits: https://thatsimpleidea.wordpress.com/2013/10/28/mvc-error-page-implementation/#comment-166

like image 28
equiman Avatar answered Oct 12 '22 14:10

equiman


This didn't work for me but inspired me to find that my Global.asax.Application_Start() didn't register the FilterConfig class like so:

FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

This, coupled with Steven V's answer resolved my null Model issue.

like image 1
illya Avatar answered Oct 12 '22 15:10

illya