如何在ASP WebApi中使用带Ninject构造注入参数的ActionFilterAttribute?
在ASP WebApi中使用带Ninject构造注入的ActionFilterAttribute
当然可以实现!我之前也踩过这个坑——Mvc里直接用构造注入过滤器很顺畅,但WebApi的过滤器实例化逻辑和Mvc不一样,默认不走DI容器,所以得做点额外配置。下面是我亲测有效的方案:
核心思路
WebApi默认通过Activator.CreateInstance直接实例化过滤器,不会经过你的Ninject容器,所以带参数的构造函数会直接报错。解决办法是替换WebApi的默认过滤器提供器,让它从Ninject容器中获取过滤器实例,这样就能支持构造注入了。
分步实现
1. 定义过滤器和依赖接口
先把需要注入的依赖抽成接口,过滤器通过构造函数接收这个依赖:
// 依赖接口 public interface IFilterDependency { void ValidateRequest(); } // 依赖实现 public class FilterDependency : IFilterDependency { public void ValidateRequest() { // 这里写你的业务逻辑,比如权限校验、日志记录等 } } // 带构造注入的过滤器 public class InjectedApiFilter : ActionFilterAttribute { private readonly IFilterDependency _dependency; // 构造函数接收依赖,由Ninject注入 public InjectedApiFilter(IFilterDependency dependency) { _dependency = dependency ?? throw new ArgumentNullException(nameof(dependency)); } public override void OnActionExecuting(HttpActionContext actionContext) { // 使用注入的依赖 _dependency.ValidateRequest(); base.OnActionExecuting(actionContext); } }
2. 实现自定义Ninject过滤器提供器
创建一个实现IFilterProvider的类,让它从Ninject容器中解析过滤器:
public class NinjectFilterProvider : IFilterProvider { private readonly IKernel _ninjectKernel; public NinjectFilterProvider(IKernel kernel) { _ninjectKernel = kernel; } public IEnumerable<FilterInfo> GetFilters(HttpConfiguration config, HttpActionDescriptor actionDescriptor) { // 解析控制器上的过滤器 foreach (var attr in actionDescriptor.ControllerDescriptor.GetCustomAttributes<FilterAttribute>(inherit: true)) { yield return new FilterInfo(ResolveFilter(attr), FilterScope.Controller); } // 解析动作上的过滤器 foreach (var attr in actionDescriptor.GetCustomAttributes<FilterAttribute>(inherit: true)) { yield return new FilterInfo(ResolveFilter(attr), FilterScope.Action); } } private FilterAttribute ResolveFilter(FilterAttribute filterAttr) { // 如果过滤器类型已在Ninject注册,就从容器获取实例,否则返回原实例 var filterType = filterAttr.GetType(); return _ninjectKernel.Get(filterType) as FilterAttribute ?? filterAttr; } }
3. 注册依赖和替换过滤器提供器
在WebApiConfig.cs的Register方法中,完成Ninject的依赖注册和过滤器提供器替换:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { // 初始化Ninject内核 var kernel = new StandardKernel(); // 注册你的依赖和过滤器 kernel.Bind<IFilterDependency>().To<FilterDependency>(); kernel.Bind<InjectedApiFilter>().ToSelf(); // 移除WebApi默认的过滤器提供器,替换成我们的Ninject版本 config.Services.RemoveAll(typeof(IFilterProvider), provider => provider is ActionDescriptorFilterProvider); config.Services.Add(typeof(IFilterProvider), new NinjectFilterProvider(kernel)); // 常规WebApi路由配置 config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } }
4. 在控制器/动作上使用过滤器
现在就可以像普通过滤器一样标记了,Ninject会自动注入构造参数:
[InjectedApiFilter] public class ProductsController : ApiController { // 控制器动作... }
备选方案:服务定位器模式(不推荐)
如果你不想替换整个过滤器提供器,也可以在过滤器内部通过WebApi的依赖解析器获取依赖,但这种方式会增加耦合,不推荐用于大型项目:
public class InjectedApiFilter : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { var dependency = actionContext.Request.GetDependencyResolver() .GetService(typeof(IFilterDependency)) as IFilterDependency; dependency?.ValidateRequest(); base.OnActionExecuting(actionContext); } }
关键注意点
- 一定要移除WebApi默认的
ActionDescriptorFilterProvider,否则它会和我们的自定义提供器冲突,导致过滤器被实例化两次。 - 确保所有需要注入的过滤器都在Ninject中完成注册,不然自定义提供器会返回原实例,构造注入会失败。
内容的提问来源于stack exchange,提问作者neuser
相关产品推荐
相关产品推荐

