0

I've been working on some internal applications for a while, and I can't quite get the error-handling to work correctly. When a user hits a controller that exists it all works normally. If the user tries to go to a URL of a controller that doesn't exist the error handling all works as normal, but when the view is served it displays as HTML in the browser. What am I doing wrong?

I grab all Application Errors in the Global.asax and then forward them to an Error controller.

private void Application_Error(object sender, EventArgs e)
{
    var exception = Server.GetLastError();
#if !DEBUG
    exception.AddExceptionDataValue("User", User?.Identity?.Name);
    exception.HandleException();
#endif
    var httpException = exception as HttpException;
    if (httpException != null)
    {
        Response.StatusCode = httpException.GetHttpCode();
    }
    Server.ClearError();
    Response.TrySkipIisCustomErrors = true;
    IController controller = new ErrorController();
    var routeData = new RouteData();
    routeData.Values.Add("controller", "Error");
    routeData.Values.Add("action", "Index");
    routeData.Values.Add("message", exception == null ? "Details not available." : exception.Message);
    var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
    controller.Execute(rc);
}

This results in passing the error message to my Error controller on EVERY error. The only difference is what is displayed to the user.

EDIT: I've found this ONLY happens with IIS Express. When this runs on a real server with IIS all error pages are rendered like in the first example. IIS Express just displays raw HTML to the user for some reason. I can only get around this by using the web.config <httpErrors> section to handle each status code and forward to a controller action, but that seems unnecessary since my Global.asax will handle it in production without an issue.

Example of an error generated inside an existing controller (even if the action doesn't exist):

Application Error

Example of an error generated by trying to access a non-existent controller (it's the entire document including the error message):

Application Error as markup

DunningKrugerEffect
  • 603
  • 1
  • 5
  • 18
  • I'm not sure what the problem is? you don't want html? – Fran Oct 25 '16 at 16:51
  • The bottom is what the browser is displaying. I want that to be rendered, not displayed as text. – DunningKrugerEffect Oct 25 '16 at 17:36
  • Do you need to send it to another controller? Could you write it as custom error atttibute? – Fran Oct 25 '16 at 17:53
  • I'm sending the request to another controller in the Global.asax code I posted. But for some reason it displays the page correctly only in some circumstances. – DunningKrugerEffect Oct 25 '16 at 19:23
  • 1
    I can't see the error message in the "text" output. Just to make sure, is `Application_Error` executed when trying to reach controller 'xxx'? How are custom errors configured in web.config? Depending on that, it may be IIS trying to display a 404 with your custom mechanism. This is how I have it configured (it looks like our requirements are similar and I had the same issue; with this set-up it works): http://stackoverflow.com/questions/40167833/passing-a-view-model-with-server-transferrequest#40235546 – Andrei Olariu Oct 26 '16 at 12:43

1 Answers1

0

Do you need to send these errors to another controller?

you could just create a custom implementation off of the HandleErrorAttribute

public class CustomErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        base.OnException(filterContext);

        var controllerName = (string) filterContext.RouteData.Values["controller"];
        var actionName = (string) filterContext.RouteData.Values["action"];
        var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);

        filterContext.Result = new ViewResult
        {
            ViewName = "Error",
            ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
            TempData = filterContext.Controller.TempData
        };
        filterContext.ExceptionHandled = true;
        filterContext.HttpContext.Response.Clear();
        filterContext.HttpContext.Response.StatusCode = 500;
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
    }
}

and add it to the FilterConfig

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CustomErrorAttribute());
    }
Fran
  • 6,440
  • 1
  • 23
  • 35