MVC GlobalFilters not firing - asp.net-mvc

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.

Related

How do I handle OperationCanceledException in ASP.NET Web APIs?

I have this simple controller, whose Get method is called with ajax to look up zipcodes via an Entity Framework repository.
[Authorize]
public class ZipCodesApiController : AppApiController
{
public ZipCode Get(string zipCode)
{
return unitOfWork.ZipCodeRepository
.Get(x => x.Zip == zipCode)
.FirstOrDefault();
}
}
In production, my logs show that System.OperationCanceledException: The operation was canceled. is thrown quite often. I think what's going on is that users are viewing an address detail page, but navigating away or closing their browser before the ajax zipcode lookup returns. I guess IIS is telling my controller that they are no longer connected, and the .NET framework throws an exception?
This seems harmless, but it also seems like a bad idea to wrap the call to ZipCodeRepository in a try and have an empty OperationCanceledException catch clause.
I've googled the error and it seems to come up quite a bit in parallel programming, which is not something I am particularly familiar with.
What is an appropriate way to handle this exception? I think it's safe to ignore, but am I wrong about that, and the Entity Framework should be alerted so that it can clean something up (my AppApiController does have a dispose method at least)?
I get the same exceptions in my web API application, however i can catch them with the Application_Error method in Global.asax.cs
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Response.Clear();
OperationCanceledException httpException = exception as OperationCanceledException;
if (httpException != null)
{
var token = httpException.CancellationToken;
if (token.IsCancellationRequested)
{
// clear error on server
Server.ClearError();
Request.Abort();
}
}
}
I don't know if that is right.

Handling UnauthorizedAccessException with custom errors in MVC 4 Application

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.

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.

Filters of GlobalFilterCollection run before Filters of ControllerInstanceFilterProvider

I came across a weird behavior but I am not sure if I am on the right track here.
I have a controller which overrides OnException method of the Controller base class.
public class ControllerFiltersController : Controller {
public ActionResult Index() {
throw new NotImplementedException();
}
protected override void OnException(ExceptionContext filterContext) {
Trace.TraceInformation(
"ControllerFiltersController Exception: " + DateTime.Now.ToString("hh:mm:ss.fff")
);
}
}
I also have an custom ExceptionFilter as follows:
public class HandleErrorCustom : IExceptionFilter {
public void OnException(ExceptionContext filterContext) {
Trace.TraceInformation(
"HandleErrorCustom Exception Message: " + DateTime.Now.ToString("hh:mm:ss.fff")
);
}
}
Then, I registered it as a global filter:
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
filters.Add(new HandleErrorCustom());
}
What I expected is here for the controller instance filter to run before global filter since the order of filters which are provided by ControllerInstanceFilterProvider is Int32.MinValue and the scope of them is FilterScope.First.
As also explained here: ASP.NET MVC 3 Service Location, Part 4: Filters
But the result is different:
iisexpress.exe Information: 0 : HandleErrorCustom Exception Message:
06:56:49.972
iisexpress.exe Information: 0 : ControllerFiltersController Exception:
06:56:49.974
This is an ASP.NET MVC 4 application and I am not aware of any changes which effects the filter ordering behavior of ASP.NET MVC 3. What am I missing here?
This is expected behavior.
Filter ordering depends on the direction the information is flowing. If the information is flowing into the action, then the order is as you expect it; if the information is flowing back out of the action, then the order is reversed.
For example, assume you have three filters in this order: F1, F2, F3. Assume that these are action filters (meaning, they're listening to ActionExecuting and ActionExecuted). The order the system will run them is as follows:
F1.ActionExecuting()
F2.ActionExecuting()
F3.ActionExecuting()
Action()
F3.ActionExecuted()
F2.ActionExecuted()
F1.ActionExecuted()
Error handlers are, by definition, filters that run on the return-side of actions, so their order is reversed.

ELMAH and API controller in MVC4 not logging errors

Using an API controller in MVC4, when the controller action throws an exception, ELMAH does not log the error.
I think the problem is that MVC4 sets the HTTP status code to 500, and it returns the exception details in a JSON object, but it does not throw an unhandled exception so ELMAH never sees it.
How can I get ELMAH to capture all responses where the status code is not 200?
The anwser described before doesn't work. I've tried on my test server and received an error ("The given filter instance must implement one or more of the following filter interfaces: IAuthorizationFilter, IActionFilter, IResultFilter, IExceptionFilter.")
Then I realized what happened .... you are trying to add the custom filter to the MVC Global Filter (filters.Add(new ElmahHandledErrorLoggerFilter());)
To fix that, I've split the filter registration in GlobalFilter and HttpFilter
FilterConfig.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterHttpFilters(HttpFilterCollection filters)
{
filters.Add(new ElmahHandledErrorLoggerFilter());
}
Global.asax
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
FilterConfig.RegisterHttpFilters(GlobalConfiguration.Configuration.Filters);
;-)
UPDATE: This answer does not work in latest versions. Use julianox's answer.
Answer found from information here: http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
Where the exception filter logs to elmah:
public class ElmahHandledErrorLoggerFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
base.OnException(actionExecutedContext);
ErrorSignal.FromCurrentContext().Raise(actionExecutedContext.Exception);
}
}
but you also have to add the error filter to the GlobalConfiguration filters:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
GlobalConfiguration.Configuration.Filters.Add(new ElmahHandledErrorLoggerFilter());
filters.Add(new ElmahHandledErrorLoggerFilter());
filters.Add(new HandleErrorAttribute());
}

Resources