我来自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>();
我在这个例子中使用了统一。 有人能指出我正确的方向吗?
答案 0 :(得分:3)
感谢大家的帮助,我最终想通了。
我从本文https://unity.codeplex.com/discussions/446780
获得了大部分答案我使用了以下nuget包。
首先,我需要一个新的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)
{
...
}
}
根据您需要执行的操作,可以使用不同类型的过滤器,例如AuthorizationFilterAttribute
和ExceptionFilterAttribute
。
如果您希望将其应用于所有控制器操作,则可以在GlobalConfiguration.Configuration.Filters.Add(new UserTokenAuthenticationAttribute ())
中注册此单个实例,或者如果您需要更灵活地应用该操作过滤器,则可以将其应用于单个控制器或操作使用
默认情况下,不会从IOC容器中解析操作过滤器,它们实际上是由Activator.CreateInstance
创建的。
我不确定Unity中是否可以使用它,但Autofac如果使用RegisterWebApiFilterProvider
方法配置为可以解决操作过滤器Autofac WebApi扩展。