【问题标题】:Is there a way to use AutoFac Web Api Authorization Filters through Attributes instead of injection?有没有办法通过属性而不是注入来使用 AutoFac Web Api 授权过滤器?
【发布时间】:2015-06-05 14:48:31
【问题描述】:

我有一个这样的 Autofac Web Api 授权过滤器:

public class MyAuthorizationFilter : IAutofacAuthorizationFilter
{
    public void OnAuthorization(HttpActionContext actionContext){}
}

public class MyAuthorizationAttribute : Attribute
{
    public MyAuthorizationAttribute() { }
}

目前,我拥有 Autofac Web Api 授权过滤器的唯一方法是将其注入 AutofacConfig.cs:

builder.RegisterType<MyAuthorizationFilter>()
.AsWebApiAuthorizationFilterFor<MyController>(
    c => c.MyMethod(default(MyModel))
).InstancePerDependency();

如果我没有像上面那样注入它,似乎该属性会被忽略

public MyController : ApiController {

    [MyAuthroziationFilter] // ignored
    [POST("")]
    public HttpResponseMessage MyMethod(MyModel myModel) { 
        [...]
    }
}

有没有办法使用 AutoFac Web Api 授权过滤器的属性/注释而不是通过 AutoFac 进行注入,并且还可以正确注入它们的依赖项?

【问题讨论】:

    标签: asp.net-web-api filter dependency-injection authorization autofac


    【解决方案1】:

    很遗憾,如果您希望在过滤器中使用 DI,则不能使用属性。 Per the docs:

    与 MVC 中的过滤器提供程序不同,Web API 中的过滤器提供程序不允许您指定不应缓存过滤器实例。这意味着 Web API 中的所有过滤器属性实际上都是在应用程序的整个生命周期中存在的单例实例。

    如果您想使用属性,最好的办法是在标准 Web API 属性中使用服务位置。从请求消息中获取请求生命周期范围并手动解析您需要的服务。

    【讨论】:

    • 我在想应该有更好的方法根据这个评论:groups.google.com/d/msg/autofac/JZViZwr0sNA/C4h67OhMI7UJ(即使他们在谈论动作过滤器)。顺便说一句,我不得不重构旧代码,因为过滤器的注入在我所有的 apicontrollers 上,所有请求都命中了过滤器。每次检查 apicontroller 是否用相应的属性装饰以过滤正确的请求。现在我正在注入问题中描述的特定方法。但我觉得代码正在成为授权列表......
    • 在评论中注意它是针对 MVC 的。正如我所提到的,Web API 中没有类似的选项。相信我,我们在 ASP.NET 5 的背景下与 ASP.NET 人员就这一问题进行了长时间的交谈,试图告诉他们停止过度缓存。
    • 好的,谢谢 :) - 我没有看到您是 Autofac 的共同所有者 :)
    【解决方案2】:

    您可以为 Autofac 库的 ContainerBuilder 类使用自定义扩展:

    public static class ContainerBuilderExtensions
    {
        public static void RegisterWebApiFilterAttribute<TAttribute>(this ContainerBuilder builder, Assembly assembly) where TAttribute : Attribute, IAutofacAuthorizationFilter
        {
            Type[] controllerTypes = assembly.GetLoadableTypes()
                .Where(type => typeof(ApiController).IsAssignableFrom(type)).ToArray();
            RegisterFilterForControllers<TAttribute>(builder, controllerTypes);
            RegisterFilterForActions<TAttribute>(builder, controllerTypes);
        }
    
        // We need to call 
        // builder.RegisterType<TFilter>().AsWebApiAuthorizationFilterFor().InstancePerDependency()
        // for each controller marked with TAttribute
        private static void RegisterFilterForControllers<TAttribute>(ContainerBuilder builder, IEnumerable<Type> controllerTypes) where TAttribute : Attribute, IAutofacAuthorizationFilter
        {
            foreach (Type controllerType in controllerTypes.Where(c => c.GetCustomAttribute<TAttribute>(false)?.GetType() == typeof(TAttribute)))
            {
                GetAsFilterForControllerMethodInfo(controllerType).Invoke(null, new object[] { builder.RegisterType(typeof(TAttribute)) });
            }
        }
    
        // We need to call 
        // builder.RegisterType<TFilter>().AsWebApiAuthorizationFilterFor(controller => controller.Action(arg)).InstancePerDependency()
        // for each controller action marked with TAttribute
        private static void RegisterFilterForActions<TAttribute>(ContainerBuilder builder, IEnumerable<Type> controllerTypes)
            where TAttribute : Attribute, IAutofacAuthorizationFilter
        {
            foreach (Type controllerType in controllerTypes)
            {
                IEnumerable<MethodInfo> actions = controllerType.GetMethods().Where(method => method.IsPublic && method.IsDefined(typeof(TAttribute)));
                foreach (MethodInfo actionMethodInfo in actions)
                {
                    ParameterExpression controllerParameter = Expression.Parameter(controllerType);
                    Expression[] actionMethodArgs = GetActionMethodArgs(actionMethodInfo);
                    GetAsFilterForActionMethodInfo(controllerType).Invoke(null,
                        new object[] {
                            builder.RegisterType(typeof(TAttribute)),
                            Expression.Lambda(typeof(Action<>).MakeGenericType(controllerType), Expression.Call(controllerParameter, actionMethodInfo, actionMethodArgs),
                                controllerParameter)
                        });
                }
            }
        }
    
        private static Expression[] GetActionMethodArgs(MethodInfo actionMethodInfo)
        {
            return actionMethodInfo.GetParameters().Select(p => Expression.Constant(GetDefaultValueForType(p.ParameterType), p.ParameterType)).ToArray<Expression>();
        }
    
        /// <summary>
        ///     Returns info for <see cref="Autofac.Integration.WebApi.RegistrationExtensions" />.AsWebApiAuthorizationFilterFor()
        ///     method
        /// </summary>
        private static MethodInfo GetAsFilterForControllerMethodInfo(Type controllerType)
        {
            return GetAsFilterForMethodInfo(controllerType, parametersCount: 1);
        }
    
        /// <summary>
        ///     Returns info for <see cref="Autofac.Integration.WebApi.RegistrationExtensions" />
        ///     .AsWebApiAuthorizationFilterForerFor(actionSelector) method
        /// </summary>
        private static MethodInfo GetAsFilterForActionMethodInfo(Type controllerType)
        {
            return GetAsFilterForMethodInfo(controllerType, parametersCount: 2);
        }
    
        private static MethodInfo GetAsFilterForMethodInfo(Type controllerType, int parametersCount)
        {
            return typeof(RegistrationExtensions).GetMethods()
                .Single(m => m.Name == nameof(RegistrationExtensions.AsWebApiAuthorizationFilterFor) && m.GetParameters().Length == parametersCount)
                .MakeGenericMethod(controllerType);
        }
    
        private static object GetDefaultValueForType(Type t)
        {
            return typeof(ContainerBuilderExtensions).GetMethod(nameof(GetDefaultGeneric))?.MakeGenericMethod(t).Invoke(null, null);
        }
    
        private static T GetDefaultGeneric<T>()
        {
            return default;
        }
    }
    

    然后您将能够为整个程序集进行一次注册,而不是为每个控制器:

    public class LoggerFilterAttribute : Attribute, IAutofacAuthorizationFilter
    {
        private readonly ILog _logger;
        //if you don't want empty constructor you can use separate "marker" attribute and current class will not have to inherit Attribute, but you will need to modify ContainerBuilderExtensions
        public LoggerFilterAttribute() { }
        public LoggerFilterAttribute(ILog logger)
        {
            _logger = logger;
        }
    
        public Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
        {
            _logger.Trace($"Authorizing action '{actionContext.ControllerContext.ControllerDescriptor.ControllerName}.{actionContext.ActionDescriptor.ActionName}'");
            return Task.CompletedTask;
        }
    }
    

    注册:

    builder.RegisterWebApiFilterAttribute<LoggerFilterAttribute>(Assembly.GetExecutingAssembly());
    

    你可以在控制器或动作上使用你的属性:

    public class AuthorsController : ApiController
    {
        // GET api/authors
        [LoggerFilter]
        public IEnumerable<string> Get()
        {
            return new string[] { "author1", "author2" };
        }        
    }
    
    [LoggerFilter]
    public class BooksController : ApiController
    {
        // GET api/books
        public IEnumerable<string> Get()
        {
            return new string[] { "book1", "book2" };
        }
    }
    

    整个演示项目在github

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-02
      相关资源
      最近更新 更多