Handling UnauthorizedAccessException with custom errors in MVC 4 Application - asp.net-mvc

I have enabled the global error handling for an application by applying the HandleError attribute within the filterConfig registration.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
I am then using the custom errors (web.config) to hopefully display a friendly error message for each server error.
<customErrors mode="On" ></customErrors>
This seemed to be working fine for most exceptions and I was getting the expected behaviour in that the custom error page View (Error.cshtml in the shared view folder) was being displayed.
However I have recently noticed that this is not the behaviour I see if the error thrown is an UnauthorizedAccessException.
I am a bit stumped with this, as looking in fiddler I see that this UnauthorizedAccessException exception returns a plain 500 internal server error as a standard exception does.
So how come the standard exception abides by my customError setup but the UnauthorizedAccessException does not?
ANd how can I get them to behave the same, as they are both essentially an error which I want to prevent the end user from seeing.

This blog post provided me with the overview of exception handling to enable me to decide how to handle the unauthorizedAccessException, which essentially means handling them within the Application_OnStart.
http://prideparrot.com/blog/archive/2012/5/exception_handling_in_asp_net_mvc
For my purposes there doesn't seem much point in handling the errors with the HandleErrorAttribute and in the global Application_OnStart so for my purposes I decided it was best to handle everything in the Application_OnSTart,

If you just want to force 'unhandled' exceptions like UnauthorizedAccessException to go through the normal custom-error page then you can override the controller's OnException method similar to the following:
protected override void OnException(ExceptionContext filterContext)
{
base.OnException(filterContext);
if (!filterContext.ExceptionHandled && filterContext.RequestContext.HttpContext.IsCustomErrorEnabled)
{
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
filterContext.Result = View("Error",
new HandleErrorInfo(filterContext.Exception, filterContext.GetCurrentControllerName(), filterContext.GetCurrentActionName()));
}
}
The article that you referenced is an excellent resource for a more thorough explanation of error-handling techniques, though, and should be considered as well.

Related

MVC GlobalFilters not firing

I have this
public class ExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
Exception exception = context.Exception;
if (!(exception is HttpException))
{
Trace.TraceError("ExceptionFilter: " + ExceptionUtilities.GetFullExceptionMessage(exception));
Trace.Flush();
}
}
}
and this in global.asax
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new Filters.ExceptionFilter());
filters.Add(new HandleErrorAttribute());
}
but when I trigger an error by creating a dangerous request like this
example.com/dsfgds:dfgd
I get an exception:
A potentially dangerous Request.Path value was detected from the
client (:)
and the filter doesn't fire and the breakpoint inside doesn't get hit.
That's because you're registering an IExceptionFilter with MVC, and as such it will only capture unhandled exceptions that were raised within an MVC Action (and I think maybe other MVC filters? Don't quote me on that). But the error about a potentially dangerous request is an ASP.NET error, the request never made it to MVC, so the MVC error filter never gets called. Likewise, any IIS level errors would also not be handled by this. For non-MVC errors you still need to monitor the Application_OnError event. Or in the case of exception handlers like Elmah, let it monitor the event for you.

FluentNhibernate configuration exception handling in application_start

I am initializing FluentNHibernate from Application_Start event like so:
Fluently.Configure()
.Database(OracleDataClientConfiguration.Oracle10
.Driver<NHibernate.Driver.OracleDataClientDriver>()
.ConnectionString("MyConnectionString")
.DefaultSchema("MySchema")
)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<SomeClass>())
.BuildConfiguration()
.BuildSessionFactory();
If the connection string is bad, or connection to the DB fails for some other reason, I get a TNS No listener exception. I would like to display/log this exception but Application_Start (and Applicaiton_Error) doesn't have an HttpContext or Response object in IIS7 Integrated mode. The user gets a yellow screen of death telling them to turn custom errors On. Elmah doesn't log the message either. I would like to solve the problem in one of two possible ways:
Disable nhibernate configuration from connecting to the database on configuration.
Provide custom user feedback based on the error and get Elmah working (somehow). This would be my ideal choice.
I was able to move NHibernate configuration to run on Session_Start, as described here, which gets exception handling working for this error, but then I get other exceptions that can be misleading to the root cause of the problem. Does anyone have a good solution for this scenario?
Thank you.
This is what I do:
void Application_Start() {
try {
// setup your app / nhibernate
} catch(Exception ex) {
Application["StartupError"] = ex
}
}
void Application_BeginRequest() {
var startupError = Application["StartupError"] as Exception;
if (startupError != null)
throw new Exception("Error starting application", startupError);
}
In your BeginRequest method you have access to the Request and can do what you want to show the error (or show a nice page)

Is it proper way of exception handling in ASP.NET MVC

I have read articles on exception handling in ASP.NET MVC. I want to make sure I am doing right by presenting it briefly. Could anyone please comment.
Catch the exceptions in controller actions, if necessary.
[HttpPost]
public ActionResult Insert()
{
try
{
}
catch
{
//ModelState.Error -> display error msg to the user.
}
}
Override the "OnException" method of controller in basecontroller and "log" the exceptions raised in step 1 and other MVC exceptions
Logged the global exceptions in application_onerror.
I would definitely recommend ELMaH instead of writing this code yourself, and also over Log4Net for your MVC apps. I personally avoid any exception handling, unless I have a specific functional response to it. In this way, I don't "eat" any of the errors that an application-wide tool such as ELMaH will handle gracefully for me.
ELMaH also has nice built-in web reporting, and there are third-party tools specifically for ELMaH that can give you statistics, e.g. the most frequent errors.
You might start with a custom error redirect...
<customErrors defaultRedirect="~/site/error" mode="RemoteOnly">
<error statusCode="404" redirect="~/site/notfound" />
</customErrors>
...to a controller that is aware you are using ELMaH...
public virtual ActionResult Error() {
System.Collections.IList errorList = new System.Collections.ArrayList();
ErrorLog.GetDefault(System.Web.HttpContext.Current).GetErrors(0, 1, errorList);
ErrorLogEntry entry = null;
if (errorList.Count > 0) {
entry = errorList[0] as Elmah.ErrorLogEntry;
}
return View(entry);
}
...backed by a view that helps the visitor get the specific error ID to you:
#model Elmah.ErrorLogEntry
#if (Context.User.Identity.IsAuthenticated) {
<p>Since you are signed in, we've noted your contact information,
and may follow up regarding this to help improve our product.</p>
} else {
<p>Since you aren't signed in, we won't contact you regarding this.</p>
}
<p>Error ID: #Model.Id</p>
I also notice this is an HttpPost in this example. If you are doing AJAX, then you'll want to handle errors for those in a unique way. Pick a standard response you can send to browsers that all of your AJAX code handles gracefully. Perhaps by displaying the ELMaH error ID in a javascript alert (as a simple example).
I also handle a few special types of AJAX errors via Global.asax:
protected void Application_EndRequest()
{
if (Context.Response.StatusCode == 302 &&
Context.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
HandleErrorAttribute is a nice feature, but it is well-known that there is extra work to use it in conjunction with ELMaH. How to get ELMAH to work with ASP.NET MVC [HandleError] attribute?
If you want handle exceptions in your Actions you can override "OnException" in your Controller like so:
protected override void OnException(ExceptionContext filterContext)
{
logging or user notification code here
}
You can put it in your BaseController class to prevent duplication
try and catch are for expected exceptions ie your user has entered a file name and it might not exist so you want to catch the FileNotFoundException.
For unexpected exceptions use either the Error event in the MvcApplication object e.g.
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
this.Error += MvcApplication_Error;
// Other code
}
private void MvcApplication_Error(object sender, EventArgs e)
{
Exception exception = this.Server.GetLastError();
// Do logging here.
}
}
or as Dima suggested you have controller level execption handling using
protected override void OnException(ExceptionContext filterContext)
{
// Do logging here.
}
Keep the trys and catches on code where you want to catch something expected and can handle.
"Generic" error handling just obfuscates the underlying problem, which you will have to dig for later.

How to get rid of that HTML error report in ASP.NET MVC?

All I need would be just the error message in plain text. But ASP.NET is doing some HTML report output from every error.
I have a jquery ajax call and when an error is thrown I'm getting all that crap over to the client side.
I've created a filter attribute but didn't helped.
public class ClientErrorHandler : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
var responce = filterContext.RequestContext.HttpContext.Response;
responce.Write(filterContext.Exception.Message);
responce.ContentType = MediaTypeNames.Text.Plain;
filterContext.ExceptionHandled = true;
}
}
EDIT
I'm seeing this
and I'd like to see just what is in here filterContext.Exception.Message
It looks to me like the reason why you cannot correctly handle the exception is because it happens outside of the MVC pipeline. If you look at the stack trace in the code you posted there is no reference to System.Web.Mvc code (the firing of exception filters when an exception occurs is called from ControllerActionInvoker.InvokeAction).
The stack trace indicates that the exception happens late in the ASP.NET pipeline (OnEndRequest) and that it's coming through the Autofac component.
To capture this error you would have to subscribe to the HttpApplication's Error event. See the following article on creating a global error handler: http://msdn.microsoft.com/en-us/library/994a1482.aspx . In this event you can handle the error and redirect to a custom error page.
you need to return a ContentResult
ContentResult result = new ContentResult();
result.Content = filterContext.Exception.Message;
result.ContentType = MediaTypeNames.Text.Plain;
filterContext.Result = result;
filterContext.ExceptionHandled = true;
Since you're using JQuery and WCF (by the details of your error), you might want to take a look at this article on how to handle service faults elegantly between jQuery and WCF - you might have to rework your service if you are able to do so.

How to get ELMAH to work with ASP.NET MVC [HandleError] attribute?

I am trying to use ELMAH to log errors in my ASP.NET MVC application, however when I use the [HandleError] attribute on my controllers ELMAH doesn't log any errors when they occur.
As I am guessing its because ELMAH only logs unhandled errors and the [HandleError] attribute is handling the error so thus no need to log it.
How do I modify or how would I go about modifying the attribute so ELMAH can know that there was an error and log it..
Edit: Let me make sure everyone understands, I know I can modify the attribute thats not the question I'm asking... ELMAH gets bypassed when using the handleerror attribute meaning it won't see that there was an error because it was handled already by the attribute... What I am asking is there a way to make ELMAH see the error and log it even though the attribute handled it...I searched around and don't see any methods to call to force it to log the error....
You can subclass HandleErrorAttribute and override its OnException member (no need to copy) so that it logs the exception with ELMAH and only if the base implementation handles it. The minimal amount of code you need is as follows:
using System.Web.Mvc;
using Elmah;
public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
base.OnException(context);
if (!context.ExceptionHandled)
return;
var httpContext = context.HttpContext.ApplicationInstance.Context;
var signal = ErrorSignal.FromContext(httpContext);
signal.Raise(context.Exception, httpContext);
}
}
The base implementation is invoked first, giving it a chance to mark the exception as being handled. Only then is the exception signaled. The above code is simple and may cause issues if used in an environment where the HttpContext may not be available, such as testing. As a result, you will want code that is that is more defensive (at the cost of being slightly longer):
using System.Web;
using System.Web.Mvc;
using Elmah;
public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
base.OnException(context);
if (!context.ExceptionHandled // if unhandled, will be logged anyhow
|| TryRaiseErrorSignal(context) // prefer signaling, if possible
|| IsFiltered(context)) // filtered?
return;
LogException(context);
}
private static bool TryRaiseErrorSignal(ExceptionContext context)
{
var httpContext = GetHttpContextImpl(context.HttpContext);
if (httpContext == null)
return false;
var signal = ErrorSignal.FromContext(httpContext);
if (signal == null)
return false;
signal.Raise(context.Exception, httpContext);
return true;
}
private static bool IsFiltered(ExceptionContext context)
{
var config = context.HttpContext.GetSection("elmah/errorFilter")
as ErrorFilterConfiguration;
if (config == null)
return false;
var testContext = new ErrorFilterModule.AssertionHelperContext(
context.Exception,
GetHttpContextImpl(context.HttpContext));
return config.Assertion.Test(testContext);
}
private static void LogException(ExceptionContext context)
{
var httpContext = GetHttpContextImpl(context.HttpContext);
var error = new Error(context.Exception, httpContext);
ErrorLog.GetDefault(httpContext).Log(error);
}
private static HttpContext GetHttpContextImpl(HttpContextBase context)
{
return context.ApplicationInstance.Context;
}
}
This second version will try to use error signaling from ELMAH first, which involves the fully configured pipeline like logging, mailing, filtering and what have you. Failing that, it attempts to see whether the error should be filtered. If not, the error is simply logged. This implementation does not handle mail notifications. If the exception can be signaled then a mail will be sent if configured to do so.
You may also have to take care that if multiple HandleErrorAttribute instances are in effect then duplicate logging does not occur, but the above two examples should get your started.
Sorry, but I think the accepted answer is an overkill. All you need to do is this:
public class ElmahHandledErrorLoggerFilter : IExceptionFilter
{
public void OnException (ExceptionContext context)
{
// Log only handled exceptions, because all other will be caught by ELMAH anyway.
if (context.ExceptionHandled)
ErrorSignal.FromCurrentContext().Raise(context.Exception);
}
}
and then register it (order is important) in Global.asax.cs:
public static void RegisterGlobalFilters (GlobalFilterCollection filters)
{
filters.Add(new ElmahHandledErrorLoggerFilter());
filters.Add(new HandleErrorAttribute());
}
There is now an ELMAH.MVC package in NuGet that includes an improved solution by Atif and also a controller that handles the elmah interface within MVC routing (no need to use that axd anymore)
The problem with that solution (and with all the ones here) is that one way or another the elmah error handler is actually handling the error, ignoring what you might want to set up as a customError tag or through ErrorHandler or your own error handler
The best solution IMHO is to create a filter that will act at the end of all the other filters and log the events that have been handled already. The elmah module should take care of loging the other errors that are unhandled by the application. This will also allow you to use the health monitor and all the other modules that can be added to asp.net to look at error events
I wrote this looking with reflector at the ErrorHandler inside elmah.mvc
public class ElmahMVCErrorFilter : IExceptionFilter
{
private static ErrorFilterConfiguration _config;
public void OnException(ExceptionContext context)
{
if (context.ExceptionHandled) //The unhandled ones will be picked by the elmah module
{
var e = context.Exception;
var context2 = context.HttpContext.ApplicationInstance.Context;
//TODO: Add additional variables to context.HttpContext.Request.ServerVariables for both handled and unhandled exceptions
if ((context2 == null) || (!_RaiseErrorSignal(e, context2) && !_IsFiltered(e, context2)))
{
_LogException(e, context2);
}
}
}
private static bool _IsFiltered(System.Exception e, System.Web.HttpContext context)
{
if (_config == null)
{
_config = (context.GetSection("elmah/errorFilter") as ErrorFilterConfiguration) ?? new ErrorFilterConfiguration();
}
var context2 = new ErrorFilterModule.AssertionHelperContext((System.Exception)e, context);
return _config.Assertion.Test(context2);
}
private static void _LogException(System.Exception e, System.Web.HttpContext context)
{
ErrorLog.GetDefault((System.Web.HttpContext)context).Log(new Elmah.Error((System.Exception)e, (System.Web.HttpContext)context));
}
private static bool _RaiseErrorSignal(System.Exception e, System.Web.HttpContext context)
{
var signal = ErrorSignal.FromContext((System.Web.HttpContext)context);
if (signal == null)
{
return false;
}
signal.Raise((System.Exception)e, (System.Web.HttpContext)context);
return true;
}
}
Now, in your filter config you want to do something like this:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//These filters should go at the end of the pipeline, add all error handlers before
filters.Add(new ElmahMVCErrorFilter());
}
Notice that I left a comment there to remind people that if they want to add a global filter that will actually handle the exception it should go BEFORE this last filter, otherwise you run into the case where the unhandled exception will be ignored by the ElmahMVCErrorFilter because it hasn't been handled and it should be loged by the Elmah module but then the next filter marks the exception as handled and the module ignores it, resulting on the exception never making it into elmah.
Now, make sure the appsettings for elmah in your webconfig look something like this:
<add key="elmah.mvc.disableHandler" value="false" /> <!-- This handles elmah controller pages, if disabled elmah pages will not work -->
<add key="elmah.mvc.disableHandleErrorFilter" value="true" /> <!-- This uses the default filter for elmah, set to disabled to use our own -->
<add key="elmah.mvc.requiresAuthentication" value="false" /> <!-- Manages authentication for elmah pages -->
<add key="elmah.mvc.allowedRoles" value="*" /> <!-- Manages authentication for elmah pages -->
<add key="elmah.mvc.route" value="errortracking" /> <!-- Base route for elmah pages -->
The important one here is "elmah.mvc.disableHandleErrorFilter", if this is false it will use the handler inside elmah.mvc that will actually handle the exception by using the default HandleErrorHandler that will ignore your customError settings
This setup allows you to set your own ErrorHandler tags in classes and views, while still loging those errors through the ElmahMVCErrorFilter, adding a customError configuration to your web.config through the elmah module, even writing your own Error Handlers. The only thing you need to do is remember to not add any filters that will actually handle the error before the elmah filter we've written. And I forgot to mention: no duplicates in elmah.
You can take the code above and go one step further by introducing a custom controller factory that injects the HandleErrorWithElmah attribute into every controller.
For more infomation check out my blog series on logging in MVC. The first article covers getting Elmah set up and running for MVC.
There is a link to downloadable code at the end of the article. Hope that helps.
http://dotnetdarren.wordpress.com/
I'm new in ASP.NET MVC. I faced the same problem, the following is my workable in my Erorr.vbhtml (it work if you only need to log the error using Elmah log)
#ModelType System.Web.Mvc.HandleErrorInfo
#Code
ViewData("Title") = "Error"
Dim item As HandleErrorInfo = CType(Model, HandleErrorInfo)
//To log error with Elmah
Elmah.ErrorLog.GetDefault(HttpContext.Current).Log(New Elmah.Error(Model.Exception, HttpContext.Current))
End Code
<h2>
Sorry, an error occurred while processing your request.<br />
#item.ActionName<br />
#item.ControllerName<br />
#item.Exception.Message
</h2>
It is simply!
A completely alternative solution is to not use the MVC HandleErrorAttribute, and instead rely on ASP.Net error handling, which Elmah is designed to work with.
You need to remove the default global HandleErrorAttribute from App_Start\FilterConfig (or Global.asax), and then set up an error page in your Web.config:
<customErrors mode="RemoteOnly" defaultRedirect="~/error/" />
Note, this can be an MVC routed URL, so the above would redirect to the ErrorController.Index action when an error occurs.
For me it was very important to get email logging working. After some time I discover that this need only 2 lines of code more in Atif example.
public class HandleErrorWithElmahAttribute : HandleErrorAttribute
{
static ElmahMVCMailModule error_mail_log = new ElmahMVCMailModule();
public override void OnException(ExceptionContext context)
{
error_mail_log.Init(HttpContext.Current.ApplicationInstance);
[...]
}
[...]
}
I hope this will help someone :)
This is exactly what I needed for my MVC site configuration!
I added a little modification to the OnException method to handle multiple HandleErrorAttribute instances, as suggested by Atif Aziz:
bear in mind that you may have to take care that if multiple HandleErrorAttribute instances are in effect then duplicate logging does not occur.
I simply check context.ExceptionHandled before invoking the base class, just to know if someone else handled the exception before current handler.
It works for me and I post the code in case someone else needs it and to ask if anyone knows if I overlooked anything.
Hope it is useful:
public override void OnException(ExceptionContext context)
{
bool exceptionHandledByPreviousHandler = context.ExceptionHandled;
base.OnException(context);
Exception e = context.Exception;
if (exceptionHandledByPreviousHandler
|| !context.ExceptionHandled // if unhandled, will be logged anyhow
|| RaiseErrorSignal(e) // prefer signaling, if possible
|| IsFiltered(context)) // filtered?
return;
LogException(e);
}

Resources