Search code examples
c#wcfasp.net-web-apiaspectsunity-interception

Can I get WebApi to work with IoC Aspects/Interceptor


I'm from a WCF background where I successfully used IoC with Aspects/Interceptors to abstract functions such as Authentication and Logging. I would simply just add the required interfaces to the aspects constructor the same way as you would with any typical IoC setup.

I'm now trying to apply the same sort of process to webapi, but as the controllers inherit from a ApiController and do not implement a interface. I'm assuming there is a different way of applying aspects maybe?

public class MyController: ApiController
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly ILoginService _loginService;
    private readonly ILog _log;

    public LoginController(ILoginService loginService, IUnitOfWork unitOfWork, ILog log)
    {
        this._loginService = loginService;
        this._unitOfWork = unitOfWork;
        this._log = log;
    }

    // I WANT TO INTERCEPT THIS METHOD USING UserTokenAuthenticationInterceptor 
    public HttpResponseMessage Get(Guid id)
    {
        _log.Log(log something);
        // some code thats gets some data using this._loginService
        _log.Log(log the save);
         _unitOfWork.Save();
    }
}

The Aspect

public class UserTokenAuthenticationInterceptor : IInterceptionBehavior 
{
    private readonly ILoginService _loginService;
    private readonly ILog _log;

    public UserTokenAuthenticationInterceptor(ILog log, ILoginService loginService)
    {
        this._log = log;
        this._loginService = loginService;
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        _log.Log(log entering authentication aspect);
        // do some authentication here using this._loginService
        _log.Log(log exiting authentication aspect);
    }

    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    public bool WillExecute { get { return true; }}
}

Container registration:

container.RegisterType<IUnitOfWork, UnitOfWork.UnitOfWork>(new HierarchicalLifetimeManager());
container.RegisterType<ILoginService , LoginService>();
container.RegisterType<ILog, LogService>();

I'm using unity in this example. Can anyone point me in the right direction?


Solution

  • Thanks for the help everyone, I eventually figured it out.

    I got most of my answer from this article https://unity.codeplex.com/discussions/446780

    I used the the following nuget packages.

    • Unity (I added this 1 first)
    • Unity.WebApi (has unity version problem if unity isnt added first)

    First I needed a new IFilterProvider implementation. Its job it to register all actionfilters with the container.

    public class UnityActionFilterProvider : ActionDescriptorFilterProvider, IFilterProvider
    {
        private readonly IUnityContainer container;
    
        public UnityActionFilterProvider(IUnityContainer container)
        {
            this.container = container;
        }
    
        public new IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
        {
            var filters = base.GetFilters(configuration, actionDescriptor);
    
            foreach (var filter in filters)
            {
                container.BuildUp(filter.Instance.GetType(), filter.Instance);
            }
    
            return filters;
        }
    }
    

    Then a registration method was required to register the new actionfilterprovider and remove the original webapi implementation. This needs to be executed in the RegisterComponents() method which is in the UnityConfig.cs file the Unity.WebApi nuget package creates.

    public static void RegisterFilterProviders(IUnityContainer container)
    {
        var providers = GlobalConfiguration.Configuration.Services.GetFilterProviders().ToList();
    
        GlobalConfiguration.Configuration.Services.Add(typeof(System.Web.Http.Filters.IFilterProvider),
                                                        new UnityActionFilterProvider(container));
    
        var defaultprovider = providers.First(p => p is ActionDescriptorFilterProvider);
    
        GlobalConfiguration.Configuration.Services.Remove(typeof(System.Web.Http.Filters.IFilterProvider), defaultprovider);
    
    }
    

    In the same RegisterComponents() method I registered my Types

    container.RegisterType<IUnitOfWork, UnitOfWork.UnitOfWork>(new HierarchicalLifetimeManager());
    container.RegisterType<ILoginService , LoginService>();
    container.RegisterType<ILog, LogService>();    
    

    Next, I needed to create a class based on AuthorizeAttribute.

    public class UserTokenAuthenticationAttribute : AuthorizeAttribute
    {
        private ILoginService _loginService;
    
        // This is the magic part - Unity reads this attribute and sets injects the related property. This means no parameters are required in the constructor.
        [Microsoft.Practices.Unity.Dependency]
        public ILoginService LoginService
        { 
            get
            {
                return this._loginService;
            } 
            set
            {
                this._loginService = value;
            }  
        }
    
        protected override bool IsAuthorized(HttpActionContext actionContext)
        {
            // Authorise code goes here using injected this._loginService
        }
    }      
    

    A log action filter is also required ActionFilterAttribute

    public sealed class LogAttribute : ActionFilterAttribute
        {
            private ILog _log;
    
            // This is the magic part - Unity reads this attribute and sets injects the related property. This means no parameters are required in the constructor.
            [Microsoft.Practices.Unity.Dependency]
            public ILog Log
            { 
                get
                {
                    return this._log;
                } 
                set
                {
                    this._log = value;
                }  
            }
    
            public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
            {
                this._log.Info("Exited " + actionContext.Request.Method);
            }
    
            public override void OnActionExecuting(HttpActionContext actionContext)
            {
                this._log.Info("Entering" + actionContext.Request.Method);
            }
        }    
    

    Now lets configure the webapi controller. We need to decorate the class with our new attributes

    [UserTokenAuthentication] // magic attribute in use
    [Log] // magic attribute in use
    public class MyController: ApiController
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly ILoginService _loginService;
        private readonly ILog _log;
    
        public MyController(ILoginService loginService, IUnitOfWork unitOfWork, ILog log)
        {
            this._loginService = loginService;
            this._unitOfWork = unitOfWork;
            this._log = log;
        }
    
        [System.Web.Http.AllowAnonymous] // doesnt require authentication as were not logged in yet
        public HttpResponseMessage Get(Guid id)
        {
            _log.Log(log something);
            // some code thats gets some data using this._loginService
            _log.Log(log the save);
             _unitOfWork.Save();
        }
    
        public HttpResponseMessage GetMyDetails(Guid id)
        {
            _log.Log(log something);
            // some code thats gets some data using this._loginService
            _log.Log(log the save);
             _unitOfWork.Save();
        }
    }