Question

I had checked this link http://www.asp.net/web-api/overview/web-api-routing-and-actions/web-api-global-error-handling. In this link they mentioned like this

class OopsExceptionHandler : ExceptionHandler
{
    public override void HandleCore(ExceptionHandlerContext context)
    {
        context.Result = new TextPlainErrorResult
        {
            Request = context.ExceptionContext.Request,
            Content = "Oops! Sorry! Something went wrong." +
                      "Please contact support@contoso.com so we can try to fix it."
        };
    }

    private class TextPlainErrorResult : IHttpActionResult
    {
        public HttpRequestMessage Request { get; set; }

        public string Content { get; set; }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            HttpResponseMessage response = 
                             new HttpResponseMessage(HttpStatusCode.InternalServerError);
            response.Content = new StringContent(Content);
            response.RequestMessage = Request;
            return Task.FromResult(response);
        }
    }
}

I don't know how to call this class in my Web API actions. So can any one give me the complete sample using this ExceptionHandler.

Was it helpful?

Solution 2

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).

OTHER TIPS

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.

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();
    }

You can take a look at the following sample from Web API team:

http://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/Elmah/ReadMe.txt

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top