Need a complete sample to handle unhandled exceptions using "ExceptionHandler" in ASP.NET Web Api?
You don't need to implement IExceptionHandler low-level mechanism yourself.
Instead, you can simply inherit from ExceptionHandler and override the Handle method.
public class MyExceptionHandler: ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
//TODO: Do what you need to do
base.Handle(context);
}
}
ExceptionHandler implements IExceptionHandler and manage basic core mechanisms (like async and that exception should be handled or not).
Use your exception handler like that:
config.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler());
Source
This page explains how to implements IExceptionHandler, but there are some typos and the code does not reflect the latest version of WebApi.
There is no documentation about the System.Web.Http.ExceptionHandling
namespace (a little bit on NuDoq).
So.. using a .NET assembly decompiler having a look at the source code on GitHub, I saw the ExceptionHandler
class which implements IExceptionHandler
and have some virtual methods.
ExceptionHandler looks like that:
namespace System.Web.Http.ExceptionHandling
{
/// <summary>Represents an unhandled exception handler.</summary>
public abstract class ExceptionHandler: IExceptionHandler
{
/// <returns>Returns <see cref="T:System.Threading.Tasks.Task" />.</returns>
Task IExceptionHandler.HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
ExceptionContext arg_14_0 = context.ExceptionContext;
if (!this.ShouldHandle(context))
{
return TaskHelpers.Completed();
}
return this.HandleAsync(context, cancellationToken);
}
/// <summary>When overridden in a derived class, handles the exception asynchronously.</summary>
/// <returns>A task representing the asynchronous exception handling operation.</returns>
/// <param name="context">The exception handler context.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
public virtual Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
this.Handle(context);
return TaskHelpers.Completed();
}
/// <summary>When overridden in a derived class, handles the exception synchronously.</summary>
/// <param name="context">The exception handler context.</param>
public virtual void Handle(ExceptionHandlerContext context)
{
}
/// <summary>Determines whether the exception should be handled.</summary>
/// <returns>true if the exception should be handled; otherwise, false.</returns>
/// <param name="context">The exception handler context.</param>
public virtual bool ShouldHandle(ExceptionHandlerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
ExceptionContext exceptionContext = context.ExceptionContext;
ExceptionContextCatchBlock catchBlock = exceptionContext.CatchBlock;
return catchBlock.IsTopLevel;
}
}
}
You can clearly see that ShouldHandle
is implemented using ExceptionContextCatchBlock.IsTopLevel
and that HandleAsync
calls Handle
:)
I hope this will helps until complete documentation shows up.
In your WebApi config your need to add the line:
config.Services.Replace(typeof (IExceptionHandler), new OopsExceptionHandler());
Also make sure you have created the base ExceptionHandler class that implements IExceptionHandler:
public class ExceptionHandler : IExceptionHandler
{
public virtual Task HandleAsync(ExceptionHandlerContext context,
CancellationToken cancellationToken)
{
if (!ShouldHandle(context))
{
return Task.FromResult(0);
}
return HandleAsyncCore(context, cancellationToken);
}
public virtual Task HandleAsyncCore(ExceptionHandlerContext context,
CancellationToken cancellationToken)
{
HandleCore(context);
return Task.FromResult(0);
}
public virtual void HandleCore(ExceptionHandlerContext context)
{
}
public virtual bool ShouldHandle(ExceptionHandlerContext context)
{
return context.CatchBlock.IsTopLevel;
}
}
Note that this will only deal with exceptions that are not handled elsewhere (e.g. by Exception filters).
As per Jon Susiak's answer you need to use:
config.Services.Replace(typeof (IExceptionHandler), new OopsExceptionHandler());
Note the Replace
call, not an Add
.
The reason for this is as per the article at this link:
Global Error Handling in ASP.NET Web API 2
Solution Overview
We provide two new user-replaceable services, IExceptionLogger and IExceptionHandler, to log and handle unhandled exceptions.
The services are very similar, with two main differences:
We support registering multiple exception loggers but only a single exception handler.
And since there is already a handler registered by default you can't add another.
public DefaultServices(HttpConfiguration configuration)
{
if (configuration == null)
throw System.Web.Http.Error.ArgumentNull("configuration");
this._configuration = configuration;
this.SetSingle<IActionValueBinder>((IActionValueBinder) new DefaultActionValueBinder());
this.SetSingle<IApiExplorer>((IApiExplorer) new ApiExplorer(configuration));
this.SetSingle<IAssembliesResolver>((IAssembliesResolver) new DefaultAssembliesResolver());
this.SetSingle<IBodyModelValidator>((IBodyModelValidator) new DefaultBodyModelValidator());
this.SetSingle<IContentNegotiator>((IContentNegotiator) new DefaultContentNegotiator());
this.SetSingle<IDocumentationProvider>((IDocumentationProvider) null);
this.SetMultiple<IFilterProvider>((IFilterProvider) new ConfigurationFilterProvider(), (IFilterProvider) new ActionDescriptorFilterProvider());
this.SetSingle<IHostBufferPolicySelector>((IHostBufferPolicySelector) null);
this.SetSingle<IHttpActionInvoker>((IHttpActionInvoker) new ApiControllerActionInvoker());
this.SetSingle<IHttpActionSelector>((IHttpActionSelector) new ApiControllerActionSelector());
this.SetSingle<IHttpControllerActivator>((IHttpControllerActivator) new DefaultHttpControllerActivator());
this.SetSingle<IHttpControllerSelector>((IHttpControllerSelector) new DefaultHttpControllerSelector(configuration));
this.SetSingle<IHttpControllerTypeResolver>((IHttpControllerTypeResolver) new DefaultHttpControllerTypeResolver());
this.SetSingle<ITraceManager>((ITraceManager) new TraceManager());
this.SetSingle<ITraceWriter>((ITraceWriter) null);
this.SetMultiple<ModelBinderProvider>((ModelBinderProvider) new TypeConverterModelBinderProvider(), (ModelBinderProvider) new TypeMatchModelBinderProvider(), (ModelBinderProvider) new KeyValuePairModelBinderProvider(), (ModelBinderProvider) new ComplexModelDtoModelBinderProvider(), (ModelBinderProvider) new ArrayModelBinderProvider(), (ModelBinderProvider) new DictionaryModelBinderProvider(), (ModelBinderProvider) new CollectionModelBinderProvider(), (ModelBinderProvider) new MutableObjectModelBinderProvider());
this.SetSingle<ModelMetadataProvider>((ModelMetadataProvider) new DataAnnotationsModelMetadataProvider());
this.SetMultiple<ModelValidatorProvider>((ModelValidatorProvider) new DataAnnotationsModelValidatorProvider(), (ModelValidatorProvider) new DataMemberModelValidatorProvider());
this.SetMultiple<ValueProviderFactory>((ValueProviderFactory) new QueryStringValueProviderFactory(), (ValueProviderFactory) new RouteDataValueProviderFactory());
this.SetSingle<IModelValidatorCache>((IModelValidatorCache) new ModelValidatorCache(new Lazy<IEnumerable<ModelValidatorProvider>>((Func<IEnumerable<ModelValidatorProvider>>) (() => ServicesExtensions.GetModelValidatorProviders((ServicesContainer) this)))));
this.SetSingle<IExceptionHandler>((IExceptionHandler) new DefaultExceptionHandler());
this.SetMultiple<IExceptionLogger>();
this._serviceTypesSingle = new HashSet<Type>((IEnumerable<Type>) this._defaultServicesSingle.Keys);
this._serviceTypesMulti = new HashSet<Type>((IEnumerable<Type>) this._defaultServicesMulti.Keys);
this.ResetCache();
}