我可以使用Web Api与IoC Aspects / Interceptor一起工作吗?

时间:2014-11-13 11:09:27

标签: c# wcf asp.net-web-api aspects unity-interception

我来自WCF背景,我成功地使用IoC和Aspects / Intercept来抽象身份验证和日志记录等功能。我只需将方法构造函数添加到所需的接口,就像使用任何典型的IoC设置一样。

我现在正在尝试将相同类型的进程应用于webapi,但由于控制器继承自ApiController并且未实现接口。我假设有一种不同的应用方式可能吗?

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

方面

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.RegisterType<IUnitOfWork, UnitOfWork.UnitOfWork>(new HierarchicalLifetimeManager());
container.RegisterType<ILoginService , LoginService>();
container.RegisterType<ILog, LogService>();

我在这个例子中使用了统一。 有人能指出我正确的方向吗?

4 个答案:

答案 0 :(得分:3)

感谢大家的帮助,我最终想通了。

我从本文https://unity.codeplex.com/discussions/446780

获得了大部分答案

我使用了以下nuget包。

  • Unity(我先添加了这个1)
  • Unity.WebApi(如果不首先添加统一版,则会出现统一版本问题)

首先,我需要一个新的IFilterProvider实现。 它的工作就是用容器注册所有动作过滤器。

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

然后需要注册方法来注册新的actionfilterprovider并删除原始的webapi实现。 这需要在RegisterComponents()方法中执行,该方法位于Unity.WebApi nuget包创建的UnityConfig.cs文件中。

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

}

在同一个RegisterComponents()方法中,我注册了我的类型

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

接下来,我需要创建一个基于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
    }
}      

日志操作过滤器也是必需的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);
        }
    }    

现在让我们配置webapi控制器。我们需要使用新属性

来装饰类
[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();
    }
}   

答案 1 :(得分:0)

我自己就是一个Autofac人,但根据this blog post,在Unity中可以做同样的事情:你将控制器实例化交给你的IoC容器,然后你可以在控制器中使用依赖注入。

话虽这么说,我从未使用过拦截器,所以我不确定这些是如何注册才能正常工作的。似乎IInterceptionBehavior在Unity和no longer maintained中已经过时了。

答案 2 :(得分:0)

更好地编写自定义AuthorizeAttribute实现。例如:

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

然后将其用于所有ApiController

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

它将与Aspect一样工作(实际上它是一个Aspect,但是在ASP.NET MVC方式中),除了你将使用HttpActionContext(更高级别的模型,这将帮助你实现同样的但是以更快的方式。您也可以使用HTTP标头操作而不是使用低级IInvocation

答案 3 :(得分:0)

在WebApi中,您可以创建一个ActionFilter,可以在调用控制器操作之前和之后调用它:

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

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

根据您需要执行的操作,可以使用不同类型的过滤器,例如AuthorizationFilterAttributeExceptionFilterAttribute

如果您希望将其应用于所有控制器操作,则可以在GlobalConfiguration.Configuration.Filters.Add(new UserTokenAuthenticationAttribute ())中注册此单个实例,或者如果您需要更灵活地应用该操作过滤器,则可以将其应用于单个控制器或操作使用

默认情况下,不会从IOC容器中解析操作过滤器,它们实际上是由Activator.CreateInstance创建的。

我不确定Unity中是否可以使用它,但Autofac如果使用RegisterWebApiFilterProvider方法配置为可以解决操作过滤器Autofac WebApi扩展。