0

I’m trying to implement a global exception handling in my MVC 5 application. To achieve that I’ve added a handler to the Application_Error event in Global.asax.cs.

In my handler, I read the error details from the Server GetLastError. I store the error details in a cookie and do a redirect to an error controller which reads the cookie and displays the error details to the user.

The cookie gets set correctly, but when I try to read it in my error controller, the cookie does not exist.

This is my code:

protected void Application_Error( Object sender, EventArgs e )
{
    const String route = "Default";
    const String controller = "Errors";
    const String area = "";
    var action = "InternalServerError";
    var errorCode = 500;

    try
    {
        // Get the previous exception.
        var exception = Server.GetLastError() ?? new HttpException( (Int32) HttpStatusCode.InternalServerError, "Unknown internal server error occurred." );
        // Clear the exception
        Server.ClearError();
        // Bypass IIS custom errors
        Response.TrySkipIisCustomErrors = true;

        // Check for HTTP code
        var httpException = exception as HttpException;
        if ( httpException != null )
            errorCode = httpException.GetHttpCode();

        // ReSharper disable once SwitchStatementMissingSomeCases
        switch ( errorCode )
        {
            case 401:
            case 403:
                action = "Forbidden";
                break;
            case 404:
                action = "NotFound";
                break;
        }

        // Try to collect some error details
        try
        {
            var details = new WebErrorDetails
            {
                Exception = exception,
                ErrorSource = HttpContext.Current.Request.Url.ToString()
            };

            HttpContext.Current.Response.Cookies.Set(new HttpCookie(CommonConstants.ErrorDetails, JsonConvert.SerializeObject(details))
            {
                Expires = DateTime.Now.Add(2.ToMinutes()),
                HttpOnly = true
            });
        }
        catch
        {
            // ignore
        }

        Response.RedirectToRoute( route, new RouteValueDictionary( new { area, controller, action } ) );
    }
    catch
    {
        Response.RedirectToRoute( route, new RouteValueDictionary( new { area, controller, action = "InternalServerError" } ) );
    }
}


public class ErrorsController : ControllerBase
{
    #region Ctor

    /// <summary>
    ///     Initialize a new instance of the <see cref="ErrorsController" /> class.
    /// </summary>
    /// <param name="loggerFactory">A <see cref="ILoggerFactory" />.</param>
    public ErrorsController( ILoggerFactory loggerFactory )
        : base( loggerFactory.CreateLogger( typeof(ErrorsController) ) )
    {
        Logger.Trace( "Enter." );
    }

    #endregion

    #region Private Members

    [NotNull]
    private WebErrorDetails PopErrorDetails()
    {
        try
        {
            // GetRequestCookie looks like this => HttpContext.Current.Request.Cookies[cookieName];
            var cookie = HttpContextService.GetRequestCookie( CommonConstants.ErrorDetails );
            if ( cookie != null )
            {
                var errorDetails = JsonConvert.DeserializeObject<WebErrorDetails>( cookie.Value );
                if ( errorDetails != null )
                    return errorDetails;
            }
        }
        catch ( Exception ex )
        {
            Logger.Warn( ex, "Failed to pop error details." );
        }

        // Fall-back value
        return new WebErrorDetails
        {
            Exception = new Exception( "Exception details missing." ),
            ErrorSource = "-"
        };
    }

    private void StoreErrorDetails( WebErrorDetails errorDetails )
    {
        try
        {
            HttpContextService.AddCookieToResponse( new HttpCookie( CommonConstants.ErrorDetails, JsonConvert.SerializeObject( errorDetails ) )
                                                    {
                                                        Expires = DateTime.Now.Add( 2.ToMinutes() ),
                                                        HttpOnly = true
                                                    } );
        }
        catch ( Exception ex )
        {
            Logger.Warn( ex, "Failed to store error details." );
        }
    }

    #endregion

    #region Action Methods

    /// <summary>
    ///     Returns a error view for 500 internal server errors.
    /// </summary>
    /// <returns>Returns a error view for 500 internal server errors.</returns>
    public async Task<ActionResult> InternalServerError()
    {
        Logger.Info( "Enter error action method." );
        WebErrorDetails errorDetails = null;
        try
        {
            errorDetails = PopErrorDetails();

            // Get the layout view model
            var layoutVm = await PrepareLayoutViewModel();

            // Build the view model
            var vm = new LayoutApplicationErrorViewModel
            {
                Exception = errorDetails.Exception,
                ErrorSource = errorDetails.ErrorSource,
                ViewTitle = CommonResources.Common_Static_InternalServerError
            };
            HttpContextService.StatusCode = (Int32) HttpStatusCode.InternalServerError;

            // Set the layout view model
            SetLayoutData( layoutVm, vm );

            return View( "Error", vm );
        }
        catch ( Exception ex )
        {
            try
            {
                Logger.Error( ex, "Unexpected exception occurred." );
                if ( errorDetails != null )
                    StoreErrorDetails( errorDetails );
                else
                    StoreErrorDetails( new WebErrorDetails
                                       {
                                           ErrorSource = HttpContextService.RequestUrl.ToString(),
                                           Exception = ex
                                       } );
            }
            catch
            {
                // ignore
            }
            return RedirectToAction( "GeneralError", "Errors" );
        }
    }

    /// <summary>
    ///     Returns a general error view without any layout.
    /// </summary>
    /// <returns>Returns a general error view without any layout.</returns>
    public ActionResult GeneralError()
    {
        Logger.Info( "Enter general error action method." );

        try
        {
            // Build the view model
            var errorDetails = PopErrorDetails();
            var vm = new LayoutApplicationErrorViewModel
            {
                Exception = errorDetails.Exception,
                ErrorSource = errorDetails.ErrorSource,
                ViewTitle = "Error"
            };
            HttpContextService.StatusCode = (Int32) HttpStatusCode.InternalServerError;

            return View( vm );
        }
        catch ( Exception ex )
        {
            Logger.Fatal( ex, "Could not display basic error view." );
        }
    }

    #endregion
}

Note: Setting and Reading cookies works everywhere else. I assume the problem is related to the redirect?

1 Answer 1

1

//Clear the response.

Response.Clear();
Sign up to request clarification or add additional context in comments.

1 Comment

After clearing the response, it works perfectly. Thank you for the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.