I have a ASP WebAPI project. I'm trying to setup a global exception handler on my basecontroller. So I created an ExceptionFilterAttribute like so.
using System.Web.Http.Filters;
public class MyExceptionFilterAttribute : ExceptionFilterAttribute
{
protected static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
var exception = actionExecutedContext.Exception;
log.Fatal(exception);
base.OnException(actionExecutedContext);
}
}
Then I also registered it in /App_Start/WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// ...
// Setup Filters
config.Filters.Add(new MyExceptionFilterAttribute());
}
}
When I add the attributes to my controller (or base-controller), nothing gets logged. What am I doing wrong?
Edit: My controller throwing the exception:
[HttpGet]
public string Hello(string name)
{
if (name.Equals("error", StringComparison.OrdinalIgnoreCase))
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
else
{
return name;
}
}
Actually when you add that filter to your HttpConfiguration it means that it will be executed for any action. That is, you don't need to add the whole attribute to your API controllers.
What can be skipping your filter? Other filter. The first filter to set the response wins and it can happen that the action itself gets never executed.
Anyway, maybe you need to switch to implement an IExceptionHandler and configure it as follows:
config.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler());
This approach is better because it's a true last-chance exception handler and it will be always called independently of the behavior of filters.
Like #ShekharPankaj had pointed out, not all exceptions are handled by the attribute (or the approach #MatÃas provided). My code was fine. I simple changed the exception to a ArgumentException and it gets handled.
See also this SO-thread: catch all unhandled exceptions in ASP.NET Web Api
To answer my own question, this isn't possible!
Handling all exceptions that cause internal server errors seems like a
basic capability Web API should have, so I have put in a request with
Microsoft for a Global error handler for Web API:
https://aspnetwebstack.codeplex.com/workitem/1001
If you agree, go to that link and vote for it!
In the meantime, the excellent article ASP.NET Web API Exception
Handling shows a few different ways to catch a few different
categories of error. It's more complicated than it should be, and it
doesn't catch all interal server errors, but it's the best approach
available today.
Update: Global error handling is now implemented and available in the
nightly builds! It will be released in ASP.NET MVC v5.1. Here's how it
will work:
https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling
Related
I have a custom exception FilterAttribute such as the following:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public sealed class ExceptionLoggingFilterAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException(nameof(filterContext));
}
if (filterContext.ExceptionHandled)
{
return;
}
// some exception logging code (not shown)
filterContext.ExceptionHandled = true;
}
I have this registered globally in my FilterConfig.cs
public static class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters?.Add(new ExceptionLoggingFilterAttribute());
}
}
I also have an Application_Error method declared in my global.asax.cs
protected void Application_Error(object sender, EventArgs e)
{
var exception = Server.GetLastError();
// some exception logging code (not shown)
}
When will the exception filter code be hit and when will it go straight to the global error handler in the Application_Error method? (I understand the ExceptionHandled concept and realise that by marking that as handled in my filter, it won't then cascade up to the global error handler).
An exception that I thought would hit the filter - an HttpException for 404, does not hit the filter but does get caught in the application error handler.
I have seen some code samples where people use the HttpContext.Current in the global.asax.cs to do a Server.TransferRequest to a specific error view. Is this best practice? Would it be better to use the CustomErrors section in the system.web section of the web.config?
An exception filter will be hit only for errors that occur during the execution of the ASP.NET MVC pipeline, e.g. during the execution of an Action method:
Exception filters. These implement IExceptionFilter and execute if
there is an unhandled exception thrown during the execution of the
ASP.NET MVC pipeline. Exception filters can be used for tasks such as
logging or displaying an error page. The HandleErrorAttribute class is
one example of an exception filter.
(from: https://msdn.microsoft.com/en-us/library/gg416513(VS.98).aspx)
In the case of a 404 error, an Action method could not be determined, so that the error is not handled in the filter.
All other errors will be handled in the Application_Error method.
As for the second part of your question, I'd recommend the following blog post that contains a good overview on how to set up custom error pages in a reliable way:
http://benfoster.io/blog/aspnet-mvc-custom-error-pages
I have an MVC Razor application that uses entity framework 6.0. However, if the DB is down or something, my code starts throwing exceptions at all kinds of random places like when I start evaluating my IEnumerable<T>s and IQueryable<T>s.
The model constructor is generated code that will be overwritten if I modify it which wouldn't help anyway because the constructor doesn't throw exceptions. Instead, the exceptions come in places like this
using (var dataContext = new ArchiveVMADDatabase.ArchiveDatabaseModel())
{
IQueryable<HDeploy> deploys = Helpers.GetProdDeploysFromArchive(dataContext);
var query = getBranchSelectListQuery(deploys);
listItems.AddRange(query);// EXCEPTION IF DB IS DOWN
}
Is there a good way for me to handle this in one place and avoid wrapping nearly 100% of my code in giant try catch blocks? I would really like to have it just return empty sets if it can't talk to the DB.
I'm not sure whether you are working with MVC or Web API, but in Web API one would use Exception Filters to centralize exception handling.
An exception filter is basically a derivative of the ExceptionFilterAttribute that can create a specific response depending on the caught exception:
public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is NotImplementedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
}
}
}
You don't need to add try {} catch {} blocks anywhere -- Web API triggers the configured exception filters automatically as soon as an exception reaches the outer most layer of your system (i.e. the controller level).
You can activate an exception filter only for specific ApiControllers or globally for every controller.
Similar question was asked here but that issue was specifically concerning a 404 error.
I have added Elmah as a global handler as described here. If I raise an error in code Elmah catches it just fine, but if I have a SQL error from EF via a controller for example, it is not caught - the exception message is returned in the json as a 500 error.
According to this article there are a few cases where an exception can't be caught but my case doesn't seem to be one of them.
Can anyone please explain why a global error handler in WebAPI won't catch a SQL exception?
Here's my handler:
public class UnhandledExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
Elmah.ErrorLog.GetDefault(HttpContext.Current).Log(new Elmah.Error(context.Exception));
}
}
and here's where it's registered in WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
----
config.Filters.Add(new UnhandledExceptionFilter());
}
Good morning all,
I noticed strange behavior of Ninject (at least I think that it is the problem) in my project. The situation is the following. I use
Ninject.3.0.2-unstable-9028
Ninject.Extensions.Factory.3.0.1.0
Ninject.Web.Common.3.0.2-unstable-9012
Ninject.Web.WebApi-RC.3.0.0.22
I changed the NinjectWebCommon.cs and added code with my bindings. I have a WebApiConfig.cs which configures global exceptions handler
public static void Register(HttpConfiguration config)
{
// Authentication token handler.
config.MessageHandlers.Add(new TokenAuthenticationHandler());
// Exceptions handler.
config.Filters.Add(new ExceptionsHandler());
...
}
and I have an exceptions handler class
public class ExceptionsHandler : ExceptionFilterAttribute
{
...
public override void OnException(HttpActionExecutedContext context)
{
...
context.Response = context.Request.CreateErrorResponse(httpStatusCode, response);
}
}
Now, when the request comes to the API it first gets to TokenAuthenticationHandler() object which does its magic and calls
return base.SendAsync(request, cancellationToken);
Then WebAPI controller kicks in and does its magic. In case of exception, it gets thrown and OnException of ExceptionsHandler gets called. The problem is that when OnException finishes its job, It gets called again with same context and I can't find the reason why.
I don't configure it twice. I don't have extra records in web.config. It started happening when I started using Ninject.Web.WebAPI-RC library. Before that I was using custom made Resolvers and Contexts classes that worked well, but, didn't dispose Ninject created objects. So, I decided to use what works. And everything do work except that for some reason OnException gets called twice.
If I remove
config.Filters.Add(new ExceptionsHandler());
from WebApiConfig.cs then it doesn't get called at all.
I think that Ninject has something to do with it, but I can't understand why it happens.
Thank you in advance.
try this
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public class ExceptionsHandler : ExceptionFilterAttribute
{
...
public override void OnException(HttpActionExecutedContext context)
{
...
context.Response = context.Request.CreateErrorResponse(httpStatusCode, response);
}
}
This worked for me.. =)
When an exception is thrown by your own code that's called from an action in a controller how should that be handled? I see a lot of examples of best practices where there are no try-catch statements at all. For example, accessing data from a repository:
public ViewResult Index()
{
IList<CustomModel> customModels = _customModelRepository.GetAll();
return View(customModels);
}
Clearly this code could throw an exception if the call is to a database that it can't access and we are using an ORM like Entity Framework for example.
However all that I can see will happen is that the exception will bubble up and show a nasty error message to the user.
I'm aware of the HandleError attribute but I understand it's mostly used to redirect you to an error page if an exception that's unhandled occurs.
Of course, this code could be wrapped in a try-catch but doesn't separate nicely, especially if you have more logic:
public ViewResult Index()
{
if (ValidationCheck())
{
IList<CustomModel> customModels = new List<CustomModel>();
try
{
customModels = _customModelRepository.GetAll();
}
catch (SqlException ex)
{
// Handle exception
}
if (CustomModelsAreValid(customModels))
// Do something
else
// Do something else
}
return View();
}
Previously I have extracted out all code that could throw exceptions like database calls into a DataProvider class which handles errors and returns messages back for showing messages to the user.
I was wondering what the best way of handling this is? I don't always want to return to an error page because some exceptions shouldn't do that. Instead, an error message to the user should be displayed with a normal view. Was my previous method correct or is there a better solution?
I do three things to display more user-friendly messages:
Take advantage of the global exception handler. In the case of MVC: Application_Error in Global.asax. Learn how to use it here: http://msdn.microsoft.com/en-us/library/24395wz3(v=vs.100).aspx
I subclass Exception into a UserFriendlyException. I do my very best in all of my underlying service classes to throw this UserFriendlyException instead of a plain old Exception. I always try to put user-meaningful messages in these custom exceptions. The main purpose of which is to be able to do a type check on the exception in the Application_Error method. For the UserFriendlyExceptions, I just use the user-friendly message that I've set deep down in my services, like "Hey! 91 degrees is not a valid latitude value!". If it's a regular exception, then it's some case I haven't handled, so I display a more generic error message, like "Oops, something went wrong! We'll do our best to get that fixed!".
I also create an ErrorController that is responsible for rendering user-friendly views or JSON. This is the controller whose methods will be called from the Application_Error method.
EDIT:
I thought I'd give a mention to ASP.NET Web API since it's closely related. Because the consumer of Web API endpoints won't necessarily be a browser, I like to deal with errors a little differently. I still use the "FriendlyException" (#2 above), but instead of redirecting to an ErrorController, I just let all my endpoints return some kind of base type that contains an Error property. So, if an exception bubbles all the way up to the Web API controllers, I make sure to stick that error in the Error property of API response. This error message will either be the friendly message that has bubbled up from the classes the API controller relies on, or it will be a generic message if the exception type is not a FriendlyException. That way, the consuming client can simply check whether or not the Error property of the API response is empty. Display a message if the error is present, proceed as usual if not. The nice thing is that, because of the friendly message concept, the message may be much more meaningful to the user than a generic "Error!" message. I use this strategy when writing mobile apps with Xamarin, where I can share my C# types between my web services and my iOS/Android app.
With Asp.Net MVC you can also override the OnException method for you controller.
protected override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled)
{
return;
}
filterContext.Result = new ViewResult
{
ViewName = ...
};
filterContext.ExceptionHandled = true;
}
This allow you to redirect to a custom error page with a message that refer to the exception if you want to.
I used an OnException override because I have several projects referenes to one that have a Controller that handle errors:
Security/HandleErrorsController.cs
protected override void OnException(ExceptionContext filterContext)
{
MyLogger.Error(filterContext.Exception); //method for log in EventViewer
if (filterContext.ExceptionHandled)
return;
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
filterContext.Result = new JsonResult
{
Data = new
{
Success = false,
Error = "Please report to admin.",
ErrorText = filterContext.Exception.Message,
Stack = filterContext.Exception.StackTrace
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
filterContext.ExceptionHandled = true;
}
All questions like this are not very constructive, because the answer is always "it depends", because there are so many ways of dealing with error handling.
Many people like to use the HandleError method, because any exception is basically non-recoverable. I mean, what are you going to do if you can't return the objects? You're going to show them an error anyways, right?
The question becomes, how you want to show them the error. If showing them an error page is acceptable, than HandleError works fine, and provides an easy place to log the error. If you're using Ajax or want something fancier, then you need to develop a way to do that.
You talk about a DataProvider class. That's basically what your Repository is. Why not build that into your repository?