ASP.Net MVC 5 – Cookie not stored - c#

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?

//Clear the response.
Response.Clear();

Related

ASP.NET redirect to a page when a specific error occurs on a page

Sorry if this is a duplicate question. However, I've tried looking for the answer and can't seem to find it.
Is there a way in ASP.NET to redirect to a page when a specific error occurs (in my case, when the request is too large). This needs to be just when the error occurs on a specific page, and not just on any page.
Thanks in advance!
As ADyson says in the comments, perhaps a try - catch block could be used for this situation.
try
{
// put the code that you want to try here
}
catch(Exception specificException)
{
return RedirectToAction(actionName, controllerName, routeValues);
}
Let me know if this helps.
Yes! there is as follows:
In the Global.asax file:
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
HttpException httpException = exception as HttpException;
if (httpException != null)
{
if (httpException.GetHttpCode() == 404)
{
Server.ClearError();
Response.Redirect("~/Home/PageNotFound");
return;
}
}
//Ignore from here if don't want to store the error in database
HttpContextBase context = new HttpContextWrapper(HttpContext.Current);
RouteData routeData = RouteTable.Routes.GetRouteData(context);
string controllerName = null;
string actionName = null;
if (routeData != null)
{
controllerName = routeData.GetRequiredString("controller");
actionName = routeData.GetRequiredString("action");
}
ExceptionModel exceptionModel = new ExceptionModel()
{
ControllerName = controllerName ?? "Not in controller",
ActionOrMethodName = actionName ?? "Not in Action",
ExceptionMessage = exception.Message,
InnerExceptionMessage = exception.InnerException != null ? exception.InnerException.Message : "No Inner exception",
ExceptionTime = DateTime.Now
};
using (YourDbContext dbContext = new YourDbContext())
{
dbContext.Exceptions.Add(exceptionModel);
dbContext.SaveChanges();
}
// Ignore till here if you don't want to store the error on database
// clear error on server
Server.ClearError();
Response.Redirect("~/Home/Error");
}
Then in the controller:
public class HomeController : Controller
{
[AllowAnonymous]
public ActionResult Error()
{
return View();
}
[AllowAnonymous]
public ActionResult PageNotFound()
{
return View();
}
}
Here is everything you need to handle error in ASP.NET MVC Application.You can also customize according to your personal preference.

How to use customexceptionhandlers in ASP.net web-api

I am trying to understand custom exceptionhandlers but am not getting the hang of it. I tried implementing a custom exception handler like explained in the following pages:
https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/
https://learn.microsoft.com/en-us/aspnet/web-api/overview/error-handling/web-api-global-error-handling
Now my code is:
public class CustomRestErrorHandlerException : ExceptionHandler
{
public void CustomError(String error)
{
var message = new HttpResponseMessage(HttpStatusCode.NoContent)
{
Content = new StringContent("An unknown error occurred saying" + error)
};
throw new HttpResponseException(message);
}
public override void Handle(ExceptionHandlerContext context)
{
if (context.Exception is ArgumentNullException)
{
var result = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "ArgumentNullException"
};
context.Result = new ErrorMessageResult(context.Request, result);
}
else if (context.Exception is ArgumentException)
{
var result = new HttpResponseMessage(HttpStatusCode.NoContent)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Argument is not found"
};
context.Result = new ErrorMessageResult(context.Request, result);
}
else if (context.Exception is ArgumentOutOfRangeException)
{
var result = new HttpResponseMessage(HttpStatusCode.NotImplemented)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Argument is out of range"
};
context.Result = new ErrorMessageResult(context.Request, result);
}
else
{
CustomError(context.Exception.Message);
}
}
public class ErrorMessageResult : IHttpActionResult
{
private HttpRequestMessage _request;
private HttpResponseMessage _httpResponseMessage;
public ErrorMessageResult(HttpRequestMessage request, HttpResponseMessage httpResponseMessage)
{
_request = request;
_httpResponseMessage = httpResponseMessage;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(_httpResponseMessage);
}
}
}
Then I try to call the exceptionhandler which I obviously do wrong : (this I probably do not understand <<
[Route("api/***/***")]
[HttpGet]
public IHttpActionResult Clear****()
{
try
{
// try clearing
}
catch(Exception e)
{
throw new CustomRestErrorHandlerException(); << error occurs here
}
return this.Ok();
}
As you can see the error occurs because the exception is not an exceptionhandler but I have no idea how to then throw an exception through an custom exception handler since it's explained nowhere.
Can anyone explain this to me with a small example perhaps?
ExceptionHandler's have to be registered in the Web API configuration. This can be done in the WebApiConfig.cs file as shown below, where config is of type System.Web.Http.HttpConfiguration.
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
Once they are registered they are automatically called during unhandled exceptions. To test it out you might want to throw an exception in the action method such as:
[Route("api/***/***")]
[HttpGet]
public IHttpActionResult Clear****()
{
try
{
// try clearing
}
catch(Exception e)
{
throw new ArgumentNullException(); << error occurs here
}
return this.Ok();
}
You can now put a breakpoint in your exception handler and see how the unhandled exception is caught by the global ExceptionHandler.
Quote from: https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/:
"The handler, like the logger, must be registered in the Web API configuration. Note that we can only have one Exception Handler per application."
config.Services.Replace(typeof(IExceptionHandler), new CustomRestErrorHandlerException ());
So add the line above to the WebApiConfig.cs file and then simply throw an exception from the controller:
[Route("api/***/***")]
[HttpGet]
public IHttpActionResult Clear****()
{
// do not use try catch here
//try
//{
// try clearing
//}
//catch(Exception e)
//{
//throw new CustomRestErrorHandlerException(); << error occurs here
//}
throw new Exception();
}

Asp.Net Mvc Display exception in View from tempdata

I am handling error in Base controller. I need to display the error stored in tempdata, Exception type in a razor view. How can I do that?
Base Controller code
protected override void OnException(ExceptionContext filterContext)
{
// if (filterContext.ExceptionHandled)
// return;
//Let the request know what went wrong
filterContext.Controller.TempData["Exception"] = filterContext.Exception.Message;
//redirect to error handler
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(
new { controller = "Error", action = "Index" }));
// Stop any other exception handlers from running
filterContext.ExceptionHandled = true;
// CLear out anything already in the response
filterContext.HttpContext.Response.Clear();
}
Razor View Code
<div>
This is the error Description
#Html.Raw(Html.Encode(TempData["Exception"]))
</div>
Try to make common exception attribute handling and register it as global filters. Like,
Common Exception Handling attribute :
/// <summary>
/// This action filter will handle the errors which has http response code 500.
/// As Ajax is not handling this error.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class HandleErrorAttribute : FilterAttribute, IExceptionFilter
{
private Type exceptionType = typeof(Exception);
private const string DefaultView = "Error";
private const string DefaultAjaxView = "_Error";
public Type ExceptionType
{
get
{
return this.exceptionType;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
this.exceptionType = value;
}
}
public string View { get; set; }
public string Master { get; set; }
public void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (!filterContext.IsChildAction && (!filterContext.ExceptionHandled && filterContext.HttpContext.IsCustomErrorEnabled))
{
Exception innerException = filterContext.Exception;
// adding the internal server error (500 status http code)
if ((new HttpException(null, innerException).GetHttpCode() == 500) && this.ExceptionType.IsInstanceOfType(innerException))
{
var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
// checking for Ajax request
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var result = new PartialViewResult
{
ViewName = string.IsNullOrEmpty(this.View) ? DefaultAjaxView : this.View,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
};
filterContext.Result = result;
}
else
{
var result = this.CreateActionResult(filterContext, model);
filterContext.Result = result;
}
filterContext.ExceptionHandled = true;
}
}
}
private ActionResult CreateActionResult(ExceptionContext filterContext, HandleErrorInfo model)
{
var result = new ViewResult
{
ViewName = string.IsNullOrEmpty(this.View) ? DefaultView : this.View,
MasterName = this.Master,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData,
};
result.TempData["Exception"] = filterContext.Exception;
return result;
}
}
And Error/_Error view
#model HandleErrorInfo
<div>
This is the error Description
#TempData["Exception"]
</div>
I agree that you should never expose an exception to your view but if you really need to, try using a custom attribute.
public class CustomExceptionAttribute : System.Web.Mvc.HandleErrorAttribute
{
public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
if (!filterContext.ExceptionHandled)
{
filterContext.Controller.TempData.Add("Exception", filterContext.Exception);
filterContext.ExceptionHandled = true;
}
}
}
public class MyController : System.Web.Mvc.Controller
{
[CustomException]
public ActionResult Test()
{
throw new InvalidOperationException();
}
}
If you override the OnException method in the base controller, then every action will get an Exception object placed in temp data. This maybe the desired behavior but with an attribute you can selectively enable this feature.
I would strongly suggest not to show any detailed exception information in any public facing application as this could end up as a security issue. However, if this is an intranet application with controlled access or if you REALLY want to show the exception details, create a DisplayTemplate and use it as follows:
<div>
Exception Details
#Html.Display(TempData["Exception"])
</div>

Passing the current url with routedata to custom error controller

I'm trying to pass the accessed url to my error controller called ErrorController so that I can log what page was being accessed at the time.
In my Global.asax.cs I have a method Application_Error looking like this:
protected void Application_Error(object sender, EventArgs e)
{
var httpContext = ((MvcApplication)sender).Context;
var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
var currentController = " ";
var currentAction = " ";
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 = "Index";
if (ex is HttpException)
{
var httpEx = ex as HttpException;
switch (httpEx.GetHttpCode())
{
case 404:
action = "NotFound";
// Pass along some data about accessed page here
break;
// others if any
default:
action = "Index";
break;
}
}
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;
controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);
((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
}
And my ErrorController looks like this:
public class ErrorController : BaseController
{
private readonly ILog _logger;
public ErrorController()
{
_logger = LogManager.GetLogger("CustomHandleErrorAttribute.class");
}
//
// GET: /Error/
public ActionResult Index()
{
return View();
}
public ActionResult NotFound(string error)
{
_logger.Error(error);
return View();
}
}
How should I go about populating the error parameter so I can log this to my file?
I think you're over complicating it a bit. Just create a shared function that logs the exception and page in your error controller class. This way you can forget about routing all together. You can use the UrlReferrer property on the request to get the page that the error occurred on.
Global.asax (vb.net):
Sub Application_Error()
Dim ex As Exception = Server.GetLastError()
Dim page As String = If(Not IsNothing(Request), Request.UrlReferrer.AbsoluteUri, Nothing)
ErrorController.LogError(ex, page)
Server.ClearError()
End Sub
Ended up doing the following in my ErrorController.cs:
if (Request.Url != null)
{
var path = Request.Url.AbsoluteUri;
_logger.Error("404: " + path);
}

Custom error handling in mvc not working

I am handling errors in my controller and I have [CustomErrorHandleAttribute] which I have written what to do when there is an exception in my actions. Even there is no error in my code it is redirecting to customerrorhandle and throwing error. I am not able to find the error why it is doing this.
Here is my code:
namespace ExceptionHandlingInMVC.Controllers
{
[CustomHandleError]
public class HomeController : Controller
{
//
// GET: /Home/
public object Index()
{
try
{
ViewData["Title"] = "Home Page";
ViewData["Message"] = "Current time is:" + DateTime.Now.ToLongTimeString();
var x = 10;
var y = 10;
var result = x / y;
ViewData["Result"] = result;
return View();
}
catch (Exception e)
{
throw e;
}
}
[CustomHandleError]
public object About()
{
ViewData["Title"] = "About Page";
return View();
}
}
public class ErrorPresentation
{
public String ErrorMessage { get; set; }
public Exception TheException { get; set; }
public Boolean ShowMessage { get; set; }
public Boolean ShowLink { get; set; }
}
}
CustomHandleErrorAttribute that i've wrote:
namespace ExceptionHandlingInMVC
{
/// <summary>
/// This attribute (AOP) filter is used to override the Error handling and make sure that all erros are recorded in the event logs, so that they can in turn be picked up by
/// our SIEM tool so that we a) stop customers seing a bad error message and b) we are capturing all the events that happen and c) improives security for
/// by preventing a hacker from seing s=details of how our application is put together
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class CustomHandleErrorAttribute : ActionFilterAttribute
{
/// <summary>
/// This event is called when the action is called i.e. an error has just occured
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
try
{
// Bail if we can't do anything; app will crash.
if (filterContext == null)
return;
// since we're handling this, log to ELMAH(Error logging modules and handler)
var ex = filterContext.Exception ?? new Exception("No further information exists.");
WriteToEventLog(ex);
filterContext.ExceptionHandled = true;
var data = new ErrorPresentation
{
ErrorMessage = HttpUtility.HtmlEncode(ex.Message),
TheException = ex,
ShowMessage = filterContext.Exception != null,
ShowLink = false
};
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Home/ErrorPage.aspx"
};
}
catch (Exception exception)
{
throw;
}
}
/// <summary>
/// This method writes the exception to the event log we have specified in the web.config or the app.config
/// </summary>
/// <param name="exception"></param>
public void WriteToEventLog(Exception exception)
{
// pick up which machine we are on, this will already be set for all websites
var machineName = ConfigurationManager.AppSettings["MachineName"];
// PIck up the eventlog we are going to write to
var eventLogName = ConfigurationManager.AppSettings["EventLogName"];
EventLog.WriteEntry("abc", exception.Message, EventLogEntryType.Error);
}
}
}
You should really be performing your error handling by overriding Application_Error in global.asax. That way you can be sure that your code will only execute when an error occurs. Using OnActionExecuted means that your code will execute regardless or whether or not an error is thrown.
Here's the function:
void Application_Error(object sender, EventArgs e)
{
//do your stuff here
}
Try this:
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
try
{
// Bail if we can't do anything; app will crash.
if (filterContext == null)
return;
// since we're handling this, log to ELMAH(Error logging modules and handler)
if (filterContext.Exception == null || filterContext.ExceptionHandled)
{
var ex = filterContext.Exception ?? new Exception("No further information exists.");
this.WriteToEventLog(ex);
return;
};
filterContext.ExceptionHandled = true;
var data = new ErrorPresentation
{
ErrorMessage = HttpUtility.HtmlEncode(ex.Message),
TheException = ex,
ShowMessage = filterContext.Exception != null,
ShowLink = false
};
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Home/ErrorPage.aspx"
};
}
catch (Exception exception)
{
throw;
}
}
if there is no exeption you need to return, because this attribute fires every time, not only when you have an error.
update:
I suggest to you write code below in global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomErrorHandle());
}
to fire this attibute to all actions. So you don't need to write attribute to any action.
Event should be fired when there is error only and I am writing to eventlog:
In my global.asax I added the following code:
/// <summary>
/// Managing errors from a single location
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Application_Error(object sender, EventArgs e)
{
// 1. Get the last error raised
var error = Server.GetLastError();
//2. Get the error code to respond with
var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;
//3.. Log the error ( I am ignoring 404 error)
if (code != 404)
{
// Write error details to eventlog
WriteToEventLog(error);
}
//4. Clear the response stream
Response.Clear();
//5. Clear the server error
Server.ClearError();
//6. Render the Error handling controller without a redirect
string path = Request.Path;
Context.RewritePath(string.Format("~/Home/Error",code),false);
IHttpHandler httpHandler = new MvcHttpHandler();
httpHandler.ProcessRequest(Context);
Context.RewritePath(path,false);
}
/// <summary>
/// This method writes the exception to the event log we have specified in the web.config or the app.config
/// </summary>
/// <param name="exception"></param>
public void WriteToEventLog(Exception exception)
{
EventLog.WriteEntry("abc", exception.Message, EventLogEntryType.Error);
}

Categories