Steve Parry
Steve Parry

Reputation: 428

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?

Upvotes: 1

Views: 3068

Answers (4)

Steve Parry
Steve Parry

Reputation: 428

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

Upvotes: 3

Maris
Maris

Reputation: 4776

Better write your custom AuthorizeAttribute implementation. Example:

public class ApiAuthorizeAttribute : AuthorizeAttribute{

   protected override bool IsAuthorized(HttpActionContext actionContext){
      // Make your logic to check is user authorized
   }
    public override void OnAuthorization(HttpActionContext actionContext){
      // Make your authorization logic
    }
}

Then use it for all your ApiController's

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

    // That method don't need authorization
    [AllowAnonymous]
    public LoginController(ILoginService loginService, IUnitOfWork unitOfWork, ILog log)
    {
        this._loginService = loginService;
        this._unitOfWork = unitOfWork;
        this._log = log;
    }

    // Before calling that method will be called logic from `ApiAuthorizeAttribute`
    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();
    }
}

It will work the same as Aspect(actualy it is an Aspect but in ASP.NET MVC way) except that you will work with HttpActionContext(more hight level model, which will help you to achive the same but in the faster way. Also you will able to manipulate with HTTP headers) instead of working with low-level IInvocation.

Upvotes: 0

Trevor Pilley
Trevor Pilley

Reputation: 16393

In WebApi, you can create an ActionFilter which can be called before and after a controller action is invoked:

public sealed class UserTokenAuthenticationAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
         ...
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
         ...
    }
}

There are different types of filter available depending on what you need do do for example AuthorizationFilterAttribute and ExceptionFilterAttribute.

You can register a single instance of this in GlobalConfiguration.Configuration.Filters.Add(new UserTokenAuthenticationAttribute ()) if you want it to apply for all controller actions, or you can apply it to an individual controller or action if you need more flexibility about when that action filter is used.

By default, action filters are not resolved from an IOC container, they are essentially created by Activator.CreateInstance.

I'm not sure if it is possible in Unity as I've never used it but Autofac can resolve action filters if configured to do so using the RegisterWebApiFilterProvider method in the Autofac WebApi extension.

Upvotes: 0

Daniel Persson
Daniel Persson

Reputation: 2233

I'm an Autofac guy myself, but according to this blog post the same thing can be done in Unity: You hand over controller instantiation to your IoC container and then you can use dependency injection in the controllers.

This being said, I've never worked with interceptors so I'm not sure how these can be registered to work properly. And it seems that the IInterceptionBehavior is obsolete in Unity and no longer maintained.

Upvotes: 0

Related Questions