rafet
8/8/2014 - 1:32 PM

Inject Castle Windsor Dependencies into ASP.NET Web API Filter C#

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