Inject Castle Windsor Dependencies into ASP.NET Web API Filter C#
using System.Collections.Generic;
using System.Diagnostics;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using Castle.Windsor;
using Algorythmic.Web.API.Filters;
namespace Algorythmic.Web.API.Windsor.Filters
{
/// <summary>
/// Custom filter provider for actions that include injected dependencies.
/// </summary>
public class ConfigurableFilterProvider : IFilterProvider
{
private readonly IWindsorContainer _container;
/// <summary>
/// Initializes a new instance of the <see cref="ConfigurableFilterProvider"/> class.
/// </summary>
public ConfigurableFilterProvider()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ConfigurableFilterProvider"/> class.
/// </summary>
/// <param name="container">The container.</param>
internal ConfigurableFilterProvider(IWindsorContainer container)
{
_container = container;
}
/// <summary>
/// Returns an enumeration of filters.
/// </summary>
/// <param name="configuration">The HTTP configuration.</param>
/// <param name="actionDescriptor">The action descriptor.</param>
/// <returns>
/// An enumeration of filters.
/// </returns>
public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
List<FilterInfo> filters = new List<FilterInfo>(ConfigureGlobalFilters());
filters.AddRange(ConfigureLocalFilters(actionDescriptor.GetFilters(), FilterScope.Action));
filters.AddRange(ConfigureLocalFilters(actionDescriptor.ControllerDescriptor.GetFilters(), FilterScope.Controller));
return filters;
}
/// <summary>
/// Configures the local filters.
/// </summary>
/// <param name="filters">The filters.</param>
/// <param name="scope">The scope.</param>
/// <returns>A list of filters or an empty list if none were found.</returns>
private IEnumerable<FilterInfo> ConfigureLocalFilters(IEnumerable<IFilter> filters, FilterScope scope)
{
Debug.Assert(filters != null);
foreach (IFilter filter in filters)
{
if (filter is LogUncaughtExceptionFilterAttribute)
{
yield return new FilterInfo(
(LogUncaughtExceptionFilterAttribute)_container.Resolve(filter.GetType()), scope);
}
else
{
yield return new FilterInfo(filter, scope);
}
}
}
/// <summary>
/// Configures global filters, e.g. those that are registered using config.Filters.Add(..).
/// </summary>
/// <returns>A list of filters or an empty list if none were found.</returns>
private IEnumerable<FilterInfo> ConfigureGlobalFilters()
{
foreach (FilterInfo filter in GlobalConfiguration.Configuration.Filters)
{
if (filter.Instance is LogUncaughtExceptionFilterAttribute)
{
yield return new FilterInfo(
(LogUncaughtExceptionFilterAttribute)_container.Resolve(filter.Instance.GetType()), FilterScope.Global);
}
else
{
yield return filter;
}
}
}
}
}
using System;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;
using Castle.Core.Logging;
namespace Algorythmic.Web.API.Filters
{
/// <summary>
/// Filter for handling otherwise uncaught <see cref="Exception"/> occurences.
/// </summary>
public class LogUncaughtExceptionFilterAttribute : ExceptionFilterAttribute
{
private ILogger _logger = NullLogger.Instance;
/// <summary>
/// Gets or sets the logger.
/// </summary>
/// <value>
/// The logger.
/// </value>
[ExcludeFromCodeCoverage]
public ILogger Logger
{
get { return _logger; }
set { _logger = value; }
}
/// <summary>
/// Gets a value that indicates whether multiple filters are allowed.
/// </summary>
/// <returns>true if multiple filters are allowed; otherwise, false.</returns>
public override bool AllowMultiple
{
get { return false; }
}
/// <summary>
/// Raises the exception event.
/// </summary>
/// <param name="context">The context for the action.</param>
public override void OnException(HttpActionExecutedContext context)
{
Exception ex = context.Exception;
if (ex != null)
{
Logger.Fatal(ex.Message, ex);
context.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
}
}
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
using Algorythmic.Web.API.Filters;
namespace Algorythmic.Web.API.Windsor.Installers
{
/// <summary>
/// Installs <see cref="LogUncaughtExceptionFilterAttribute"/> dependencies.
/// </summary>
public class ExceptionFilterInstaller : IWindsorInstaller
{
/// <summary>
/// Performs the installation in the <see cref="T:Castle.Windsor.IWindsorContainer" />.
/// </summary>
/// <param name="container">The container.</param>
/// <param name="store">The configuration store.</param>
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<LogUncaughtExceptionFilterAttribute>().LifestyleTransient());
}
}
}
using System.Linq;
using System.Web.Http;
using System.Web.Http.Dispatcher;
using System.Web.Http.Filters;
using Castle.Facilities.Logging;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Castle.Windsor.Installer;
using Algorythmic.Web.API.Windsor.Filters;
namespace Algorythmic.Web.API.Windsor
{
/// <summary>
/// Contains startup functionality for use with the dependency container.
/// </summary>
internal static class ContainerContext
{
internal static readonly IWindsorContainer Container = new WindsorContainer();
/// <summary>
/// Configures the specified container.
/// </summary>
public static IWindsorContainer Configure()
{
Container.Install(FromAssembly.This());
Container.Register(Classes.FromThisAssembly().BasedOn<ApiController>().LifestyleTransient());
Container.AddFacility<LoggingFacility>(f => f.LogUsing(LoggerImplementation.Log4net).WithAppConfig());
// setup injection for API controllers
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new CompositionRoot(Container));
// setup injection for filters
ConfigureFilters();
return Container;
}
/// <summary>
/// Configures the filters by injecting any required dependencies.
/// </summary>
private static void ConfigureFilters()
{
// remove default providers; prevents filters from being executed twice per scope
IFilterProvider defaultActionProvider =
GlobalConfiguration.Configuration.Services.GetFilterProviders()
.First(i => i is ActionDescriptorFilterProvider);
IFilterProvider globalConfigurationProvider =
GlobalConfiguration.Configuration.Services.GetFilterProviders()
.First(i => i is ConfigurationFilterProvider);
GlobalConfiguration.Configuration.Services.Remove(typeof(IFilterProvider), defaultActionProvider);
GlobalConfiguration.Configuration.Services.Remove(typeof(IFilterProvider), globalConfigurationProvider);
// add custom provider
GlobalConfiguration.Configuration.Services.Add(typeof(IFilterProvider), new ConfigurableFilterProvider(Container));
}
}
}