How to re-route exception without marking Exception as handled - asp.net

Basically when my app has an unhandled exception I want it to be recorded as an unhandled exception by IIS (so that it can be seen on Event Viewer, for example) but I also want to route the user to an error controller.
I've overridden the OnException method of my controller in order to route users to a custom error page. The crux of the problem is this code:
protected override void OnException(ExceptionContext filterContext)
{
filterContext.Result = RedirectToAction("GeneralError", "Error", new{ routeValueA = "some value", routeValueB = "some other value"});
filterContext.ExceptionHandled = false;
}
My Problem is this: If I set filterContext.ExceptionHandled = false then I get a yellow screen of death instead of being rerouted to my error handling controller. If i set filterContext.ExceptionHandled = true then I get rerouted, but the exception is not recorded as an unhandled exception.
I know I can set a static error page using the web.config, but I don't want to do this, because then I can't use route values to dynamically send data to my Error Controller.
Can I succesfully set a result to my filterContext.Result without marking filterContext.ExceptionHandled= true?

try following from this source
protected override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled)
{
return;
}
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Shared/Error.aspx"
};
filterContext.ExceptionHandled = true;
}
Or You can even try this too
set custom errors in web.config as below:
<customErrors mode="On" defaultRedirect="~/Error">
<error redirect="~/Error/NotFound" statusCode="404" />
<error redirect="~/Error/UnauthorizedAccess" statusCode="403"/>
</customErrors>
Your ErrorController:
public class ErrorController : Controller
{
public ViewResult Index()
{
return View("Error");
}
public ViewResult NotFound()
{
Response.StatusCode = 404; //you may want to set this to 200
return View("NotFound");
}
public ViewResult UnauthorizedAccess()
{
Response.StatusCode = 404; //you may want to set this to 200
return View("UnauthorizedAccess");
}
}
Register HandleErrorAttribute as a global action filter in the FilterConfig class as follows:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomHandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}
UPDATE
I suggest you to read this answer as it gives the complete detail to the question you have asked and I hope you will find a good solution there!!

I believe what you are looking for is Custom Error Pages that you can set in your web.config file and tell IIS to redirect to a custom error page instead of the default (as you called it yellow page of death :) ) for any unhandled exceptions.
This might help :
How to make custom error pages work in ASP.NET MVC 4

Related

MVC Exception Filter equivalent in Asp.net web forms

In MVC Web Forms, we have the error Filter by impementing IExceptionFilter. This ensures that any exception in the application will be routed here.
Is there an equivalent for this in ASP.Net? Is the Application_Error method invoked for all exceptions?
First, you should check through the official documentation.
You're asking about Web Forms, and then referring to ASP.NET, so I'm not really sure if you're asking about MVC or something else, but I am going to assume MVC. 1 & 3 apply regardless.
I found a nice Stackify article that is more condensed, which I'll paraphrase here. I'm going to ignore error handling that is not a global solution (HandleErrorAttribute, etc).
Depending on your needs, there are a few options for dealing with unhandled exceptions:
1) Generic Erorr pages configured in Web.Config section
<system.web>
<customErrors mode="On" defaultRedirect="~/ErrorHandler/Index">
<error statusCode="404" redirect="~/ErrorHandler/NotFound"/>
</customErrors>
<system.web/>
If you set up something similar to the code above (replacing the example paths with paths to your error handler or other web pages), you can set up a default error page, as well as error pages for each type of status code. If you set mode="RemoteOnly", you'll only see these custom error pages when not on localhost, which can be very useful for debugging.
For example, I have the following in my project:
<customErrors mode="RemoteOnly" defaultRedirect="/ErrorHandler">
<error statusCode="400" redirect="~/ErrorHandler/?errorCode=400" />
<error statusCode="401" redirect="~/ErrorHandler/?errorCode=401" />
<error statusCode="403" redirect="~/ErrorHandler/?errorCode=403" />
<error statusCode="404" redirect="~/ErrorHandler/?errorCode=404" />
<error statusCode="500" redirect="~/ErrorHandler/?errorCode=500" />
</customErrors>
This means that any generic exception with those status codes is automatically handles for me. ErrorHandler is a simple controller I created for this. However, this does not allow you to execute code on an unhandled exception, just display a generic error page.
2) Override OnException function on Controller
This requires you to set up an interface class for your controllers and to develop an override method for the controller's OnException method.
From the Stackify article, they develop a class named UserMvcController that inherits from Controller. I personally have a class named IBaseController that has this method, along with other methods, overridden.
An example of a simple interface:
public class UserMvcController : Controller
{
protected override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
//Log the error!!
_Logger.Error(filterContext.Exception);
//Redirect or return a view, but not both.
filterContext.Result = RedirectToAction("Index", "ErrorHandler");
// OR
filterContext.Result = new ViewResult
{
ViewName = "~/Views/ErrorHandler/Index.cshtml"
};
}
}
An example from one of my projects with a few other functions. All of these functions are available on your controllers if your controllers inherit from your base class:
public class IBaseController : Controller
{
// Function to set up modal pup with Error styling
public void Error(string errorHeader, string errorMessage)
{
ViewBag.ErrorHeader = errorHeader;
ViewBag.ErrorMessage = errorMessage;
ViewBag.JavaScriptHandler = "ShowErrorModal();";
}
// Function to set up modal pup with Informational styling
public void Info(string infoHeader, string infoMessage)
{
ViewBag.InfoHeader = infoHeader;
ViewBag.InfoMessage = infoMessage;
ViewBag.JavaScriptHandler = "ShowInfoModal();";
}
// Function to override OnException method to pass exception info the ErrorController
protected override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
//Redirect or return a view, but not both.
filterContext.Result = RedirectToAction("Message", "ErrorHandler", new { area = "", errorException = filterContext.Exception.Message });
}
}
As you can see, my OnException override simply passes some of the exception information to another controller to handle. I do not use it to log or trace anything. If you're using a controller, this method will be called on all unhandled exceptions.
3) Use Application_Error
In your Global.asax file, you can also set up the Application_Error() function to handle errors.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
protected void Application_Error()
{
var ex = Server.GetLastError();
//log the error!
_Logger.Error(ex);
}
}
This will allow you to do simple logging and generic types of error handling. I suppose you can implement something very robust here, but I prefer to do most of my logical handling within the controller.
Closing Remark
Personally, I prefer overriding the OnException method of the controller and handling my errors through there. I've found that by using that, and a well developed controller to handle errors, you can do some powerful things. There are even more options than these, but I think this will get you started.
Best.

Override Elmah logged error message

Is there any way of overriding the error message logged by Elmah without duplicating it?
I have a custom exception class:
public class BusinessException : Exception
{
// detailed error message used for logging / debugging
public string InternalErrorMessage { get; set; }
public BusinessException(string message, string internalMessage)
:base(message)
{
InternalErrorMessage = internalMessage;
}
}
From the code, i throw an exception like this:
string detailedErrorMessage = string.Format("User {0} does not have permissions to access CreateProduct resource", User.Identity.Name);
throw new BusinessException("Permission denied", detailedErrorMessage);
When Elmah logs the error, it only logs Permission denied message. However, i need to log the InternalErrorMessage property of the exception instead.
I've tried to create a custom HandleErrorAttribute to do this, but this duplicates the exceptions logged:
public class ErrorHandleAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled == true)
return;
Exception exception = filterContext.Exception;
BusinessException businessException = exception as BusinessException;
if (businessException != null)
{
ErrorSignal.FromCurrentContext().Raise(new Exception(businessException.InternalErrorMessage, exception));
}
}
}
I think your issue might be here:
if (businessException != null) {
ErrorSignal.FromCurrentContext().Raise(
new Exception(businessException.InternalErrorMessage, exception));
}
When you create a new exception rather than something like this:
if (businessException != null) {
ErrorSignal.FromCurrentContext().Raise(businessException));
}
I have this done the code for one of my sites and can see if I can recreate your issue later today (assuming this does not work). I think this is the SO post that helped me implement: How to get ELMAH to work with ASP.NET MVC [HandleError] attribute?
Edit:
Re-reading your question and the other answer I realize I was trying to solve your attempted correction and not the actual problem. Have you tried something like this solution which is only a slight deviation from your current attempt:
public override void OnException(ExceptionContext filterContext) {
if (filterContext.ExceptionHandled == true) {
return;
}
Exception exception = filterContext.Exception;
BusinessException businessException = exception as BusinessException;
if (businessException != null) {
var customEx = new Exception(
businessException.InternalErrorMessage, new BusinessException());
ErrorSignal.FromCurrentContext().Raise(customEx);
return;
}
}
Check that the InternalErrormessage is returning what you expect, and I expect that forcing a return here will prevent the exception from being logged twice. Otherwise it is essentially what you had done.
I suspect you'll need to create your own errorlog implementation to log anything other than the standard properties. This shouldn't be too difficult.
Using the SqlErrorLog as an example, you only need to override the log method & put in your own logic to modify what the Error class contains before calling the base implementation.
Using what #Matthew has also said will stop you logging the exception twice.
All the source code is here

How to get exception details on shared error.cshtml

I am using ASPNet MVC4 with HandleErrorAttribute filter configured on Global.asax
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
On my web.config I have configured customError mode to RemoteOnly:
<customErrors mode="RemoteOnly" />
So, the user are redirected to ~/Shared/Error.cshtml when a exception are raised on any View, that`s ok.
Now, I can catch this exception for log:
~/Shared/Error.cshtml code:
#{
log4net.LogManager
.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType)
.Fatal("Not captured exception", ex);
}
Actually this code are working fine (without EX parameter), but I need to improve my log including exception details.
How I can get exception details on this page?
Just make your Error.cshtml view strongly typed to HandleErrorInfo which is the model being passed to this view by the HandleErrorAttribute:
#model System.Web.Mvc.HandleErrorInfo
#{
log4net
.LogManager
.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType)
.Fatal("Not captured exception", Model.Exception);
}
And by the way, logging inside a view doesn't seem like the cleanest thing you could do. I'd rather write my custom error handler attribute and log the exception there.
I'd recommend writing a custom handler and log within, e.g.
[AttributeUsage(AttributeTargets.All, Inherited = true)]
public class HandleErrorAttributeCustom : HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
base.OnException(context);
log4net.LogManager
.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType)
.Fatal("Not captured exception", context.Exception);
}
}

How to use another error page instead of Error.aspx in ASP.NET MVC?

I have AdminError.aspx and Error.aspx pages and I want to display AdminError.aspx when there is an exception. I need both files.
Why this doesn't work?
<customErrors mode="On" redirectMode="ResponseRedirect" defaultRedirect="AdminError.aspx" />
Instead Error.aspx is always displayed.
What can I do?
Asp.net mvc provide [HandleError] attribute to handle this kind of requirement, you can specify different error page (view) you want to redirect to based on specific error type. It's very flexible and recommended to do that way.
For example
[HandleError(ExceptionType = typeof(NullReferenceException),
View = "NullError")]
[HandleError(ExceptionType = typeof(SecurityException),
View = "SecurityError")]
public class HomeController : Controller
{
public ActionResult Index()
{
throw new NullReferenceException();
}
public ActionResult About()
{
return View();
}
}
Check out this similar question to find out more information.
Thanks,
I think I found a solution.
I'm using HandleErrorWithELMAHAttribute (How to get ELMAH to work with ASP.NET MVC [HandleError] attribute?)
and in OnException method I've set my view:
public override void OnException(ExceptionContext context)
{
View = "AdminError"; // this is my view
base.OnException(context);
var e = context.Exception;
if (!context.ExceptionHandled // if unhandled, will be logged anyhow
|| RaiseErrorSignal(e) // prefer signaling, if possible
|| IsFiltered(context)) // filtered?
return;
LogException(e);
}
I've noticed that it works with and without redirectMode and defaultRedirect attributes from customErrors tag.

How do i use Application_Error in ASP.NET MVC?

I want to use Application_Error with my MVC project, but i can't get it to work. I add the following to my Global.asax file:
protected void Application_Error(object sender, EventArgs e)
{
Exception objErr = Server.GetLastError().GetBaseException();
Session["Test"] = "Message:" + objErr.Message.ToString();
}
(The Session is only for tests. Im gonna use a database to log error, if i get this to work.)
Then i try to throw an exception from my HomeController and my Home/Index View, but it only triggers Debug.
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
throw (new Exception());
return View();
}
In my Webconfig file i set a defaulterror page but it doesn't redirect to the view:
<customErrors defaultRedirect="Home/Error">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
So firstly remember that global error handling should be a last resort, and controller classes have a specific error method for errors;
protected virtual bool OnError(string actionName,
System.Reflection.MethodInfo methodInfo, Exception exception)
Within this you can redirect to the standard shared error view;
protected override bool OnError(string actionName,
System.Reflection.MethodInfo methodInfo, Exception exception)
{
RenderView("Error", exception);
return false;
}
The problem you have in the global application error is that it has no concept of views or controllers, so if you want to redirect in there then you must use a known URL
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
System.Diagnostics.Debug.WriteLine(exception);
Response.Redirect("/Home/Error");
}
but you don't need to do this. If you set the default error page in web.config then you don't need that redirect
<customErrors defaultRedirect="Home/Error" />
However, unless you've added an error view to your Home controller that doesn't exist, so add the following to the home controller
public ActionResult Error()
{
return View();
}
Then (if you're sensible) you'd put the error handling code in the Error() method, as that's where all unhandled errors will end up.
public ActionResult Error()
{
Exception exception = Server.GetLastError();
System.Diagnostics.Debug.WriteLine(exception);
return View();
}
And finally remember that by default you don't see custom errors if you are connecting to localhost! So you need to change that behaviour
<customErrors mode="On" defaultRedirect="/Home/Error" />
Do you have a Session set up in the first place? If the error is triggered from an IHttpHandler not marked with IRequiresSessionState, then accessing Session will fail.
What are you doing with Session["Test"]? Are you sure your code is actually not working? You could try a File.Open and simply output some text (say, the current time) to C:\my-log.txt, which is slightly more likely to succeed than using Session.
GetBaseException isn't useful in this case (nor generally for logging) as far as I can tell.
Message is of type string - calling .ToString() isn't necessary. In general, I'd strongly recommend avoiding ToString() where possible - if you're using it because you're unsure of the type of the object, that should be a red flag; doing an end-run around the type system can hide subtle bugs (for instance, DBNull.Value.ToString() == ""). For GUI's the builtin types provide a .ToString(IFormatProvider) overload which is culture-sensitive and avoid portability issues. Since that overload is also not present on object it's also a safeguard to avoid the very weakly typed .ToString calls.

Resources