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());
}
Related
I'm using EmbedIO with the Web API module.
I'd like to have an exception handler that will catch all the unhandled exceptions and return a suited HTTP error code according to the exception type. However, it's not clear if this can be achieved.
The class WebModuleBase exposes a property called OnUnhandledException that can be set to an ExceptionHandlerCallback, but when the callback is invoked, the response's status code has already been set to HttpStatusCode.InternalServerError, as stated in the code comments.
This is not convenient since I want to set the response code myself.
WebModuleBase exposes a different property called OnHttpException that can be set to a HttpExceptionHandlerCallback. This can be set to
HttpExceptionHandler.DataResponse(ResponseSerializer.Json) which partially solves the issue.
The main concern now is that the application exceptions must be converted to HttpException in the controllers.
I'd like to throw custom exceptions from the domain code, get them in an exception handler and just return a HTTPException in there, according to the initial exception.
Basically something similar to Exception Filters in ASP.NET Web API.
Here's the code to setup the web server:
var webApiModule = new WebApiModule("/api", ResponseSerializer.Json)
.WithController<MyController>();
webApiModule.OnUnhandledException = ExceptionHandler.DataResponseForException();
webApiModule.OnHttpException = ExceptionHandler.DataResponseForHttpException();
WebServerEmbedded = new EmbedIO.WebServer(
opt => opt
.WithUrlPrefix(url)
.WithMode(HttpListenerMode.EmbedIO))
.WithModule(null, webApiModule);
These are the delegates used for exception handlers:
internal static class ExceptionHandler
{
public static ExceptionHandlerCallback DataResponseForException()
{
return (context, exception) => ResponseSerializer.Json(context, exception.Message);
}
public static HttpExceptionHandlerCallback DataResponseForHttpException()
{
return (context, httpException) => ResponseSerializer.Json(context, httpException.Message);
}
}
Thanks.
Exceptions, as well as HTTP exceptions, are handled by EmbedIO at both module and server level (each nested module group introduces a further level, but that's beyond the point).
The catch clause for HTTP exceptions always comes before the "general-purpose" catch clause, for the obvious reason that HTTP exceptions are exceptions themselves and need to be sorted out. Therefore, if an exception handler throws a HTTP exception, the latter must be handled at an outer level.
In opther words, you can write a module-level exception handler that throws a HTTP exception, then use a server-level HTTP exception handler to generate the appropriate response.
var webApiModule = new WebApiModule("/api", ResponseSerializer.Json)
.WithController<MyController>()
.HandleUnhandledException(ExceptionHandler.DataResponseForException));
WebServerEmbedded = new EmbedIO.WebServer(
opt => opt
.WithUrlPrefix(url)
.WithMode(HttpListenerMode.EmbedIO))
.WithModule(webApiModule)
.HandleHttpException(ExceptionHandler.DataResponseForHttpException);
internal static class ExceptionHandler
{
public static Task DataResponseForException(IHttpContext context, Exception exception)
{
// Replace ANY_VALID_STATUS CODE with, well, any valid status code.
// Of course you can use different status codes according to, for example,
// the type of exception.
throw new HttpException(ANY_VALID_STATUS_CODE, exception.Message);
}
public static Task DataResponseForHttpException(IHttpContext context, IHttpException httpException)
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
return ResponseSerializer.Json(context, httpException.Message);
}
}
EDIT: There's an even simpler way, if you need it for custom exceptions: just have your exceptions implement IHttpException.
Here you can see how IHttpException methods are used by the HTTP exception handling code.
Here is an example of probably the most obscure method, PrepareResponse.
EDIT: I added setting the status code in DataResponseForHttpException.
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 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
I want to throw a exception in the ActionFilterAttribute like this:
public override void OnActionExecuting(ActionExecutingContext filterContext) {
base.OnActionExecuting(filterContext);
if (!_controller.HasRoleIndex()) {
throw new Exception(Resources.BackOffice.lblErrorPermission);
}
}
I want it like this, because I want to execute this code in some ajax request, and when the user dosen't have permissions, send a exception and then showing a message.
I have the code of showing the message in the exception implemented, and it works for exeptions in other places of the aplication, but when an exception is thrown in the OnActionExecuting of the ActionFilterAttribute, a unhandled exception in the application, and what I get in the view is a Server Error.
How can I handle the exception, without redirecting to another page? I only want to throw the exception, no show a 404 error page.
Edit:
To put things in context: I have a web page with some ajax calls, I wanted the ajax calls to have a behavior in case that an error ocurred while the call was made so I have something like this in all my ajaxs:
$.ajax({
...
error: function (e, o, errorText) { AlertMessages.SetErrorMessageAndShow(errorText); },
...
});
This code shows a modal window with a friendly user message with the cause of the error:
Session expired. Bad email adress, etc.
I want to use the same code for validation. And with validation I was thinking of use ActionFilterAttribute.
But I can't get how to send and exception from the OnActionExecuting.
Is this possible? or i have to return a specific Json and then in the javascript act acording the json returned?
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.. =)