Skip to content

Instantly share code, notes, and snippets.

@akhileshnirapure
Last active September 19, 2016 20:51
Show Gist options
  • Save akhileshnirapure/22ce4c4dcab3ded98da9a692665991ed to your computer and use it in GitHub Desktop.
Save akhileshnirapure/22ce4c4dcab3ded98da9a692665991ed to your computer and use it in GitHub Desktop.
// UnhandledExceptionFilterAttribute.cs
// http://stackoverflow.com/questions/12519561/throw-httpresponseexception-or-return-request-createerrorresponse
// http://pastebin.com/Uu65fRwT
// https://blog.iamandycohen.com/2013/02/09/webapi-and-the-behavior-of-exceptions-and-an-alternative-configurable-way-to-deal/
// https://github.com/iamandycohen/Omegaluz.GlobalApiExceptions/blob/master/ExampleApi/App_Start/WebApiExceptionConfig.cs
namespace Northwind.WebApi2Services.Filters
{
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http.Filters;
/// <summary>
/// Represents the an attribute that provides a filter for unhandled exceptions.
/// </summary>
public class UnhandledExceptionFilterAttribute : ExceptionFilterAttribute
{
#region DefaultHandler
/// <summary>
/// Gets a delegate method that returns an <see cref="HttpResponseMessage" />
/// that describes the supplied exception.
/// </summary>
/// <value>
/// A <see cref="Func{T1,T2,TResult}" /> delegate method that returns
/// an <see cref="HttpResponseMessage" /> that describes the supplied exception.
/// </value>
private static readonly Func<Exception, HttpRequestMessage, HttpResponseMessage> DefaultHandler =
(exception, request) =>
{
if (exception == null)
return null;
HttpResponseMessage response = request.CreateResponse(
HttpStatusCode.InternalServerError, GetContentOf(exception)
);
response.ReasonPhrase = exception.Message.Replace(Environment.NewLine, String.Empty);
return response;
};
#endregion
#region GetContentOf
/// <summary>
/// Gets a delegate method that extracts information from the specified exception.
/// </summary>
/// <value>
/// A <see cref="Func{Exception, String}" /> delegate method that extracts information
/// from the specified exception.
/// </value>
private static readonly Func<Exception, string> GetContentOf = exception =>
{
if (exception == null)
return String.Empty;
var result = new StringBuilder();
result.AppendLine(exception.Message);
result.AppendLine();
Exception innerException = exception.InnerException;
while (innerException != null)
{
result.AppendLine(innerException.Message);
result.AppendLine();
innerException = innerException.InnerException;
}
#if DEBUG
result.AppendLine(exception.StackTrace);
#endif
return result.ToString();
};
#endregion
#region Handlers
private readonly
ConcurrentDictionary<Type, Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>>
_filterHandlers =
new ConcurrentDictionary
<Type, Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>>();
/// <summary>
/// Gets the exception handlers registered with this filter.
/// </summary>
/// <value>
/// A <see cref="ConcurrentDictionary{TKey,TValue}" /> collection that contains
/// the exception handlers registered with this filter.
/// </value>
protected
ConcurrentDictionary<Type, Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>>
Handlers
{
get { return _filterHandlers; }
}
#endregion
#region OnException(HttpActionExecutedContext actionExecutedContext)
/// <summary>
/// Raises the exception event.
/// </summary>
/// <param name="actionExecutedContext">The context for the action.</param>
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext == null || actionExecutedContext.Exception == null)
return;
Type type = actionExecutedContext.Exception.GetType();
Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> registration;
if (Handlers.TryGetValue(type, out registration))
{
HttpStatusCode? statusCode = registration.Item1;
Func<Exception, HttpRequestMessage, HttpResponseMessage> handler = registration.Item2;
HttpResponseMessage response = handler(
actionExecutedContext.Exception.GetBaseException(),
actionExecutedContext.Request
);
// Use registered status code if available
if (statusCode.HasValue)
response.StatusCode = statusCode.Value;
actionExecutedContext.Response = response;
}
else
{
// If no exception handler registered for the exception type, fallback to default handler
actionExecutedContext.Response = DefaultHandler(
actionExecutedContext.Exception.GetBaseException(), actionExecutedContext.Request
);
}
}
#endregion
#region Register<TException>(HttpStatusCode statusCode)
/// <summary>
/// Registers an exception handler that returns the specified status code for exceptions of type
/// <typeparamref name="TException" />.
/// </summary>
/// <typeparam name="TException">The type of exception to register a handler for.</typeparam>
/// <param name="statusCode">The HTTP status code to return for exceptions of type <typeparamref name="TException" />.</param>
/// <returns>
/// This <see cref="UnhandledExceptionFilterAttribute" /> after the exception handler has been added.
/// </returns>
public UnhandledExceptionFilterAttribute Register<TException>(HttpStatusCode statusCode)
where TException : Exception
{
Type type = typeof (TException);
var item = new Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>(
statusCode, DefaultHandler
);
if (!Handlers.TryAdd(type, item))
{
Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> oldItem;
if (Handlers.TryRemove(type, out oldItem))
Handlers.TryAdd(type, item);
}
return this;
}
#endregion
#region Register<TException>(Func<Exception, HttpRequestMessage, HttpResponseMessage> handler)
/// <summary>
/// Registers the specified exception <paramref name="handler" /> for exceptions of type
/// <typeparamref name="TException" />.
/// </summary>
/// <typeparam name="TException">The type of exception to register the <paramref name="handler" /> for.</typeparam>
/// <param name="handler">The exception handler responsible for exceptions of type <typeparamref name="TException" />.</param>
/// <returns>
/// This <see cref="UnhandledExceptionFilterAttribute" /> after the exception <paramref name="handler" />
/// has been added.
/// </returns>
/// <exception cref="ArgumentNullException">The <paramref name="handler" /> is <see langword="null" />.</exception>
public UnhandledExceptionFilterAttribute Register<TException>(
Func<Exception, HttpRequestMessage, HttpResponseMessage> handler)
where TException : Exception
{
if (handler == null)
throw new ArgumentNullException("handler");
Type type = typeof (TException);
var item = new Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>(
null, handler
);
if (!Handlers.TryAdd(type, item))
{
Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> oldItem;
if (Handlers.TryRemove(type, out oldItem))
Handlers.TryAdd(type, item);
}
return this;
}
#endregion
#region Unregister<TException>()
/// <summary>
/// Unregisters the exception handler for exceptions of type <typeparamref name="TException" />.
/// </summary>
/// <typeparam name="TException">The type of exception to unregister handlers for.</typeparam>
/// <returns>
/// This <see cref="UnhandledExceptionFilterAttribute" /> after the exception handler
/// for exceptions of type <typeparamref name="TException" /> has been removed.
/// </returns>
public UnhandledExceptionFilterAttribute Unregister<TException>()
where TException : Exception
{
Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> item;
Handlers.TryRemove(typeof (TException), out item);
return this;
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment