1

我来自 WCF 背景,我成功地将 IoC 与 Aspects/Interceptors 一起使用来抽象身份验证和日志记录等功能。我只需将所需的接口添加到方面构造函数,就像使用任何典型的 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

4 回答 4

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 实现。这需要在 Unity.WebApi nuget 包创建的 UnityConfig.cs 文件中的 RegisterComponents() 方法中执行。

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();
    }
}   
于 2014-11-14T14:24:59.140 回答
0

我自己是 Autofac 人,但根据这篇博文,同样的事情可以在 Unity 中完成:你将控制器实例化交给你的 IoC 容器,然后你可以在控制器中使用依赖注入。

话虽如此,我从未使用过拦截器,所以我不确定如何注册这些拦截器以使其正常工作。而且似乎 IInterceptionBehavior 在 Unity 中已过时,不再维护

于 2014-11-13T11:50:52.140 回答
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.

于 2014-11-13T12:00:31.517 回答
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 WebApi 扩展中的方法配置为这样做, Autofac可以解析操作过滤器。RegisterWebApiFilterProvider

于 2014-11-13T12:01:33.820 回答