How to keep the Server.GetLastError after Response redirecting in MVC
Asked Answered
M

2

5

In my Global.asax I have defined the Application_error method :

protected void Application_Error(object sender, EventArgs e)
{
    // Code that runs when an unhandled error occurs

    // Get the exception object.
    var exc                         = Server.GetLastError();

    //logics

    Response.Redirect(String.Format("~/ControllerName/MethodName?errorType={0}", errorAsInteger));
}

And the exc variable does keep the last error but After being redirected from the response in the responding method(MethodName) the Server.GetLastError() is null. How can I keep it or pass it to the Response.Redirect(String.Format("~/ControllerName/MethodName?errorType={0}" so I can have the exception in my method body as an object ?

Monsoon answered 14/1, 2014 at 12:33 Comment(0)
G
17

I would like to advise you to not redirect when there is an error so that the URL is preserved and the correct HTTP status code is set.

Instead, execute your controller inside Application_Error

protected void Application_Error(object sender, EventArgs e)
{
    var exception = Server.GetLastError();

    var httpContext = ((HttpApplication)sender).Context;
    httpContext.Response.Clear();
    httpContext.ClearError();
    ExecuteErrorController(httpContext, exception);
}

private void ExecuteErrorController(HttpContext httpContext, Exception exception)
{
    var routeData = new RouteData();
    routeData.Values["controller"] = "Error";
    routeData.Values["action"] = "Index";
    routeData.Values["errorType"] = 10; //this is your error code. Can this be retrieved from your error controller instead?
    routeData.Values["exception"] = exception;

    using (Controller controller = new ErrorController())
    {
        ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
    }
}

Then ErrorController is

public class ErrorController : Controller
{
    public ActionResult Index(Exception exception, int errorType)
    {
        Response.TrySkipIisCustomErrors = true;
        Response.StatusCode = GetStatusCode(exception);

        return View();
    }

    private int GetStatusCode(Exception exception)
    {
        var httpException = exception as HttpException;
        return httpException != null ? httpException.GetHttpCode() : (int)HttpStatusCode.InternalServerError;
    }
}
Gelman answered 14/1, 2014 at 13:49 Comment(2)
excellent answer for MVC 5 use! I would like to add that for anyone who has an issue where the request output is being sent over and shows as plain-text use httpContext.Response.ContentType = "text/html"; under routeData.Values["exception"] = exception; and that should fix it.Geniality
Can someone explain the line "routeData.Values["errorType"] = 10; //this is your error code. Can this be retrieved from your error controller instead?" I don't understand what does the errorType = 10 represent or whats the meaning of the comment behind it.Handwoven
K
4

The value of TempData persists until it is read or until the session times out. Persisting TempData in this way enables scenarios such as redirection, because the values in TempData are available beyond a single request.

Dictionary<string, object> tempDataDictionary = HttpContext.Current.Session["__ControllerTempData"] as Dictionary<string, object>;
            if (tempDataDictionary == null)
            {
                tempDataDictionary = new Dictionary<string, object>();
                HttpContext.Current.Session["__ControllerTempData"] = tempDataDictionary;
            }
            tempDataDictionary.Add("LastError", Server.GetLastError());

Then in your action you can use

var error = TempData["LastError"];

But here is other solution which you can do without redirect

protected void Application_Error(object sender, EventArgs e)
        {
            Exception exception = Server.GetLastError();

            Response.Clear();
            var httpException = exception as HttpException;
            var routeData = new RouteData();
            routeData.Values.Add("controller", "Error");

            if (httpException == null)
            {
                routeData.Values.Add("action", "HttpError500");
            }
            else
            {
                switch (httpException.GetHttpCode())
                {
                    case 404:
                        routeData.Values.Add("action", "HttpError404");
                        break;
                    default:
                        routeData.Values.Add("action", "HttpError500");
                        break;
                }
            }

            routeData.Values.Add("error", exception);
            Server.ClearError();
            IController errorController = DependencyResolver.Current.GetService<ErrorController>();
            errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
        }

then in Controller you can add action

public ActionResult HttpError500(Exception error)
        {
            return View();
        }
Katlynkatmai answered 14/1, 2014 at 13:40 Comment(4)
but tempDataDictionary is a local variable in here. How am I supposed to use it in the method where I get redirected by the the Response.RidirectoToMonsoon
Well dictionary is reference type. so you are not assiging to local variable but pointer to memory, also i have updated document about second possible solutionKatlynkatmai
In that case the url doesn't change. I've tried it. Plus, this way I can do the Server.GetLastError() in the controller method and it returns the correct value.Monsoon
@Monsoon then first option when you save to temp data and after redirect you can use TempData from you action in mvcKatlynkatmai

© 2022 - 2024 — McMap. All rights reserved.