能否在自定义AuthorizeFilter Attribute中用Autofac解析依赖供ApiController使用?
当然可以在自定义AuthorizeAttribute里通过Autofac解析依赖!不过要注意:AuthorizeAttribute是由.NET框架直接实例化的,不属于Autofac的管理范畴,所以没法直接用构造函数注入依赖。不过我们可以从请求上下文里拿到Autofac的请求作用域,进而解析需要的服务。
结合你的代码,我给你调整出具体的实现方案:
1. 修改自定义Authorization Attribute
我们需要从HttpActionContext中获取Autofac的依赖作用域,然后解析IMyAccessHelper(建议把你的myAccessHelper抽象成接口,符合依赖倒置原则):
public class CustomAuthorizationFilterAttribute : AuthorizeAttribute { private const string REQUEST_KEY_USERACCESS = "UserAccess"; protected override bool IsAuthorized(HttpActionContext actionContext) { try { // 从请求上下文获取Autofac的请求级依赖作用域 var dependencyScope = actionContext.Request.GetDependencyScope(); var myAccessHelper = dependencyScope.GetService(typeof(IMyAccessHelper)) as IMyAccessHelper; // 安全获取Authorization Token,避免空引用 var token = actionContext.Request.Headers.Authorization?.Parameter; if (string.IsNullOrWhiteSpace(token)) { return false; } MyUserAccess userAccess = myAccessHelper.GetUserAccess(token); actionContext.Request.Properties.Add(REQUEST_KEY_USERACCESS, userAccess); return true; } catch (Exception ex) { // 这里建议添加日志记录,方便排查问题 // _logger.LogError(ex, "Authorization failed"); return false; } } }
2. 确保依赖已注册到Autofac
在你的Autofac容器配置中,要把IMyAccessHelper和对应的实现类注册为请求级实例(因为每个请求的上下文是独立的):
var builder = new ContainerBuilder(); // 注册Web API控制器 builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); // 注册你的访问助手,请求级生命周期 builder.RegisterType<MyAccessHelper>().As<IMyAccessHelper>().InstancePerRequest(); // 注册委托工厂(你原来的代码已经在用,这里保持不变) builder.RegisterType<ServiceData>().AsSelf(); builder.RegisterGeneratedFactory<ServiceData.Factory>(); var container = builder.Build(); GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
3. 优化控制器中的代码
你原来的控制器代码可以稍微优化一下,让类型转换更安全简洁:
public class MyController : ApiController { private readonly ServiceData.Factory _myServiceFactory; public MyController(ServiceData.Factory myServiceFactory) { _myServiceFactory = myServiceFactory; } public IHttpActionResult GetData([FromUri] MyFilter filter) { if (Request.Properties.TryGetValue("UserAccess", out var userAccessObj) && userAccessObj is MyUserAccess userAccess) { var myService = _myServiceFactory.Invoke(userAccess); return Ok(myService.GetSomething()); } return Content(HttpStatusCode.Forbidden, "No User Access"); } }
额外的优雅方案:使用Autofac管理过滤器生命周期
如果你的过滤器依赖较多,或者想更贴合Autofac的依赖注入理念,可以不用继承AuthorizeAttribute,而是实现IAuthorizationFilter接口,然后通过自定义IFilterProvider让Autofac来管理过滤器的实例化:
- 创建一个依赖注入的过滤器:
public class CustomAuthorizationFilter : IAuthorizationFilter { private readonly IMyAccessHelper _myAccessHelper; // 直接构造函数注入依赖 public CustomAuthorizationFilter(IMyAccessHelper myAccessHelper) { _myAccessHelper = myAccessHelper; } public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) { try { var token = actionContext.Request.Headers.Authorization?.Parameter; if (string.IsNullOrWhiteSpace(token)) { return Task.FromResult(actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized)); } var userAccess = _myAccessHelper.GetUserAccess(token); actionContext.Request.Properties.Add("UserAccess", userAccess); return continuation(); } catch (Exception) { return Task.FromResult(actionContext.Request.CreateResponse(HttpStatusCode.Forbidden)); } } public bool AllowMultiple => false; }
- 注册自定义过滤器提供程序:
public class AutofacFilterProvider : ActionDescriptorFilterProvider, IFilterProvider { private readonly IComponentContext _container; public AutofacFilterProvider(IComponentContext container) { _container = container; } public new IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor) { var filters = base.GetFilters(configuration, actionDescriptor); foreach (var filter in filters) { _container.InjectProperties(filter.Instance); } // 这里可以添加全局过滤器,或者按条件注册过滤器 filters = filters.Concat(new[] { new FilterInfo(_container.Resolve<CustomAuthorizationFilter>(), FilterScope.Global) }); return filters; } }
- 在容器配置中替换默认的过滤器提供程序:
var builder = new ContainerBuilder(); // ... 其他注册代码 ... builder.RegisterType<AutofacFilterProvider>().As<IFilterProvider>().SingleInstance(); var container = builder.Build(); var config = GlobalConfiguration.Configuration; config.Services.Replace(typeof(IFilterProvider), container.Resolve<AutofacFilterProvider>()); config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
这个方案更适合复杂场景,能充分利用Autofac的生命周期管理和依赖注入能力。
内容的提问来源于stack exchange,提问作者zeroflaw
相关产品推荐
相关产品推荐

