【问题标题】:Resolve dependency explicitly using WebAPI 2 resolver使用 WebAPI 2 解析器显式解析依赖关系
【发布时间】:2018-04-05 06:59:32
【问题描述】:

环境:ASP.NET WebAPI 2、NInject Web API (3.3.0.0)

用例:我正在使用 OwinStartup 类设置自定义 Owin 身份验证。该类使用 ASP.NET Identity.IUserStore 来设置用户。我的挑战是让配置的解析器查询此参考。但是,MVC 中的解析器检索在 WebAPI 中不起作用

var userStore = DependencyResolver.Current.GetService(typeof(IUserStore<ExtendedUser, string>)) as IUserStore<ExtendedUser, string>;

我的启动配置如下:-

    public partial class Startup
    {
        System.Web.Http.Dependencies.IDependencyResolver resolver;

        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration httpConfig = new HttpConfiguration();

            // Due to "new" config, the underlying resolver gets empty
            this.resolver = httpConfig.DependencyResolver;

            this.ConfigureOAuthTokenGeneration(app);

            // ... other code removed

        }

        private void ConfigureOAuthTokenGeneration(IAppBuilder app)
        {
            // Fails due to resolver being empty
            var userStore = this.resolver.GetService(typeof(IUserStore<ExtendedUser, string>)) as IUserStore<ExtendedUser, string>;
            UserService.UserStore = userStore;  
            app.CreatePerOwinContext<UserService>(UserService.Create);

            // Other code removed
        }
    }

用户服务类:-

public class UserService : UserManager<ExtendedUser, string>
{
    public UserService(IUserStore<ExtendedUser, string> store)
        : base(store)
    {
    }

    //[Ninject.Inject]
    public static IUserStore<ExtendedUser, string> UserStore { get; set; }

    public static UserService Create(IdentityFactoryOptions<UserService> options, IOwinContext context)
    {
        // var userStore = DependencyResolver.Current.GetService(typeof(IUserStore<ExtendedUser, string>)) as IUserStore<ExtendedUser, string>;
        var manager = new UserService(UserStore);
....
     }
}

接口在 NInject 配置中配置正确。 NInject 否则设置正确,因为没有 Owin 集成,我可以解决我的服务。

kernel.Bind<Microsoft.AspNet.Identity.IUserStore<ExtendedUser, string>>().To<UserStore<ExtendedUser, string>>();

已经尝试过

  1. 使用“Inject”参数进行属性注入
  2. 将请求的 UserManager 与服务分开(这也需要一个需要显式初始化的构造函数

更新 1:类似情况的另一个用例:我正在尝试在 ExceptionFilter 上设置自定义记录器,如下所示:-

   public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Exception handling
            config.Filters.Add(new ExceptionFilters());
        }
    }
  public class ExceptionFilters : ExceptionFilterAttribute
    {
       public ExceptionFilters(ZLogging.ILogger logger)
        {
            this.logger = logger;
        }
        public override void OnException(HttpActionExecutedContext context)
        {
this.logger.Log(....)
        }
   }

这里再次调用 ExceptionFilters 构造函数在 WebApiConfig 的 Register 方法中被阻止。

在 WebApiConfig 中,我们可以使用传递的 HttpConfiguration 实例获取 DependencyResolver,然后获取 logger 服务。因此在这里它不是问题。但是,在无法访问 HttpConfiguration 的任何地方,都面临着让解析器退出的问题

更新 2 在 Startup 类中使用 DI 设置如下:-

public void Configuration(IAppBuilder app)
{
    HttpConfiguration httpConfig = new HttpConfiguration();

    httpConfig.DependencyResolver = new NinjectDependencyResolver(new Ninject.StandardKernel() );
    this.resolver = httpConfig.DependencyResolver;

.... }

虽然这清除了 Owin 引用解析的问题,但它破坏了 WebAPI 中的异常过滤器(参见更新 1),因为 Global.asax 在执行周期中的启动之前首先被解析:(

在获取解析器以提供服务方面需要帮助。可以提供任何必要的见解。

【问题讨论】:

    标签: asp.net-web-api2 owin ninject


    【解决方案1】:

    在整整 2 天对不同的代码排序方式进行多种组合之后,终于想出了一个可行的解决方案。发布我的解决方案,以免其他人头疼:)

    需要 NuGet 包

    • Ninject.Web.WebApi.WebHost(所有其他都作为依赖项自动安装)
    • WebActivatorEx(确保 DI 配置代码首先运行)

    涉及的文件/类(按运行时执行顺序)

    请注意,执行顺序很重要,因为依赖关系在另一个上下文需要它们时可用。

    1. NInjectWebCommon.cs(有些是通过WebApi.Host组件生成的,有些可能需要手动设置)
    2. Global.asax(一些通常放在这里的代码可能会重新定位)
    3. Startup.cs(放置 Owin 代码的位置)

    NInjectWebCommon(初始化 DI 框架,将其注入执行管道)。发布完整代码以帮助那些无法通过 NInject.Web.WebAPi.WebHost Nuget 包获得它的人)

    命名空间将被 WebActivator 属性修饰为

    [assembly: WebActivatorEx.PreApplicationStartMethod(typeof(NinjectWebCommon), "Start")]
    [assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(NinjectWebCommon), "Stop")]
    

    底层类

    public static class NinjectWebCommon
    {
        private static readonly Bootstrapper bootstrapper = new Bootstrapper();
    
        public static void Start()
        {
            DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
            DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
            bootstrapper.Initialize(CreateKernel);
        }
        public static void Stop()
        {
            bootstrapper.ShutDown();
        }
        private static IKernel CreateKernel()
        {
            var kernel = new StandardKernel();
            kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
            kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
    
            RegisterServices(kernel);
            return kernel;
        }
        private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind<ILogger>().To<NLogLogger>().InSingletonScope();
            // .... other services as below
            kernel.Bind<IEmployeeRepository>().To<EmployeeRepository>();
        }
    }
    

    Global.asax(有关路由和 DI 的所有其他配置都将从此处删除)

    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            Startup.HttpConfiguration = GlobalConfiguration.Configuration;
        }
    }
    

    Startup.cs(包含 Owin + WebApi 简介)

        public partial class Startup
        {
            public static HttpConfiguration HttpConfiguration { get; set; }
    
            public void Configuration(IAppBuilder app)
            {
                this.ConfigureOAuthTokenGeneration(app);
    
                this.ConfigureOAuthTokenConsumption(app);
    
                this.ConfigureWebApi();
    
                app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
    
                app.UseWebApi(HttpConfiguration);
    
                HttpConfiguration.EnsureInitialized();
    
            }
    
            private void ConfigureOAuthTokenGeneration(IAppBuilder app)
            {
                var userStore = HttpConfiguration.DependencyResolver.GetService(typeof(IUserStore<ExtendedUser, string>)) as IUserStore<ExtendedUser, string>;
                UserService.UserStore = userStore;
                app.CreatePerOwinContext<UserService>(UserService.Create);
                app.CreatePerOwinContext<SignInService>(SignInService.Create);
    ...
            }
     ....  other methods removed for simplification
            private void ConfigureWebApi()
            {
                HttpConfiguration.MapHttpAttributeRoutes();
    
                WebApiConfig.Register(HttpConfiguration);
             }
      }
    

    WebApiConfig

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
    
            // Exception handling
            var logger = config.DependencyResolver.GetService(typeof(ILogger)) as ILogger;
            config.Filters.Add(new ExceptionFilters(logger));
        }
    }
    

    (更新:冻结配置) 更新了 Startup 以确保配置完成(由于在多个地方设置)

    瞧瞧 一切顺利:) 如果这有帮助,请微笑并拥抱你的队友:)

    【讨论】:

    • 我对这个确切的设置有一个奇怪的问题,除了我想通过依赖来自 HTTP 上下文声明的信息的工厂来解析数据库上下文。但是当我提出请求时,DI 在用户通过身份验证之前就已注册......有什么想法吗?
    • DI 和此类组件是底层运行时的一部分,将始终在用户特定交互出现之前执行。您可以拥有一个多解析器 DI,它已经设置了所有解析器,然后根据用户的上下文,选择合适的解析器...参数特定的 DI 解析。
    • @DaveMorrison 如果您正在计划多租户应用程序,则需要在 api-gateway 级别做出此决定。您可以实现一个父 webapi,它解析这个细节并调用实际功能 webapi 的适当端点
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-07
    • 2014-05-26
    • 1970-01-01
    相关资源
    最近更新 更多