Johny Johny - 3 months ago 18
Javascript Question

Error page always loads inside partial view

I've been struggling to get around this problem for quite a while now but I cannot seem to find a solution that works for me.

I handle all errors by overriding OnException method in my BaseController class, which all others controllers inherit.

protected override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
var rd = new RouteData
{
Values = { ["controller"] = "Error", ["action"] = "Index" },
DataTokens = { }
};
var error = $@"Error: {filterContext.Exception.Message} in {filterContext.HttpContext.Request.FilePath}
Details:
{filterContext.Exception.StackTrace}";

_logger = LogManager.GetLogger(GetType());
_logger.Error(error + Environment.NewLine + "Temp Id: " + AppSession.TempId);

IController c = new ErrorController();
c.Execute(new RequestContext(new HttpContextWrapper(System.Web.HttpContext.Current), rd));
}


My error controller is pretty simple:

public ActionResult Index()
{
ViewBag.Error = "Oops.. Something went wrong";
return View("Error");
}


It works, Error page shows up, but it always loads up inside the partial view container, the partial view that raised the error. Instead, I want to do a proper redirect to just error page alone.

I've tried using and handle errors that way but it behaves in exact same manner. I've also tried handling errors in Global.asax Application_Error method, which I knew wouldn't make any difference but I wanted
to try it anyways..

My guess is because the partial view is loaded via $.get call it somehow wraps the response in the same div/container the partial view was supposed to load.

Any help would be greatly appreciated. Should you need more information, please let me know.

I've also tried looking up on SO for similar scenarios but no post, that i've found, has a good solution...

Thanks in advance.

Answer

What you should be doing is, If the error happens in an ajax call, you should be sending a json response with a property which indicates which url to redirect to. If it is not an ajax request, you can send the normal redirectresult.

Something like this

protected override void OnException(ExceptionContext filterContext)
{
    //your existing code to log errors here

    filterContext.ExceptionHandled = true;
    if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
    {
        var targetUrl = UrlHelper.GenerateUrl("Default", "Index", "Error", 
              new RouteValueDictionary(), RouteTable.Routes, Request.RequestContext, false);

        filterContext.Result = new JsonResult
        {
            JsonRequestBehavior = JsonRequestBehavior.AllowGet,
            Data = new { Error = true, Message = filterContext.Exception.Message,
                                                                   RedirectUrl= targetUrl }
        };
        filterContext.HttpContext.Response.StatusCode = 500;

        filterContext.ExceptionHandled = true;
    }
    else
    {
        filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary 
                                          {{"controller", "Error"}, {"action", "Index"}});
    }
}

Now you can listen the .ajaxError() event which will be fired anytime an ajax request completes with an error. Get the RedirectUrl value and redirect as needed. You may also consider showing a meaningful message to user (Even in a partial view from the modal dialog) so user won't be confused by the blind redirect !

$(function () {

        $(document).ajaxError(function (event, request, settings) {           
            console.log('ajax request', request.responseText);
            var d = JSON.parse(request.responseText);
            alert("Ajax error:"+d.Message);
            window.location.href = d.RedirectUrl;
        });
});