使用ninject解决OWIN WEB API Startup.cs中的依赖项

时间:2014-08-17 16:09:59

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

我有一个Web Api 2应用程序,有两个类都依赖于另一个类,并且我使用ninject来解析依赖性。

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    private IUserService _userService;


    public AuthorizationServerProvider(IUserService userService)
    {
        _userService = userService;
    }

}

public class RefreshTokenProvider : IAuthenticationTokenProvider
{
    private IUserService _userService;

    public RefreshTokenProvider(IUserService userService)
    {
        _userService = userService;
    }

在startup.cs类中我需要使用上面两个类,但当然我不能在启动类中使用构造函数注入,因为它是在Ninject之前初始化的。

解决这个问题的方法是什么,以便引用_tokenProvider和 ConfigureAuth方法中的_authServerProvider?

public class Startup
{
    private AuthorizationServerProvider _authServerProvider;        
    private RefreshTokenProvider _tokenProvider;

    public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
    public void Configuration(IAppBuilder app)
    {
       var config = new HttpConfiguration();

        app.UseNinjectMiddleware(CreateKernel);
        app.UseNinjectWebApi(config);

        ConfigureOAuth(app);

        WebApiConfig.Register(config);
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        app.UseWebApi(config);

    }

    public void ConfigureOAuth(IAppBuilder app)
    {

        var oAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            AllowInsecureHttp = true, //TODO: HTTPS
            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
            Provider = _authServerProvider, 
            RefreshTokenProvider = _tokenProvider
        };

}

这里是CreateKernel Method

private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        try
        {
            kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
            kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();


            RegisterServices(kernel);
            return kernel;
        }
        catch
        {
            kernel.Dispose();
            throw;
        }
    }

这是我注册服务的地方

private static void RegisterServices(IKernel kernel)
    {

        kernel.Bind<SimpleAuthorizationServerProvider>().ToSelf();
        kernel.Bind<SimpleRefreshTokenProvider>().ToSelf();

}

我已经按照ninject docs中的建议,但无济于事。

1 个答案:

答案 0 :(得分:35)

你这是错误的方式。好吧,反正部分是错误的方式。您无法让OWIN将依赖项注入Startup类。因此,您必须使用配置了app.UseNinjectMiddleware()的内核来解析选项配置类。我们将使用Lazy内核执行此操作。

首先,您应该在Startup.Auth.cs中进行配置。此外,您还有一些冗余.. app.UseNinjectWebApi(config)会调用app.UseWebApi(config)(请参阅the source)。我也不知道为什么你在那里调用WebApiConfig.Register(),因为它通常在Global.asax.cs中调用

无论如何,它应该看起来像这样(我没有测试过,但它应该是关闭的):

首先,我们将把你的内核创建转移到一个惰性方法,然后在UseNinjectMiddleware()方法中调用Startup.Configuration()使用Startup类中的lazy kerel作为成员。我认为这是一个简单的lambda委托,而不是创建一个静态的CreateKernel方法。

<击>

<击>
public partial class Startup
{
    private readonly Lazy<IKernel> _kernel = new Lazy<IKernel>(() =>
    {
        var kernel = new StandardKernel();

        kernel.Load(Assembly.GetExecutingAssembly());

        // here for brevity, move this to a RegisterServices or similar method,
        // 
        kernel.Bind<IOAuthAuthorizationServerOptions>()
            .To<MyOAuthAuthorizationServerOptions>();
        kernel.Bind<IOAuthAuthorizationServerProvider>()
            .To<AuthorizationServerProvider>();
        kernel.Bind<IAuthenticationTokenProvider>().To<RefreshTokenProvider>();
        kernel.Bind<IUserService>().To<MyUserService>();
        return kernel;
    });

    public void Configuration(IAppBuilder app)
    {
        app.UseNinjectMiddleware(() => _kernel.Value);
        var config = new HttpConfiguration();
        app.UseNinjectWebApi(config);

        ConfigureAuth(app);
    }
}

然后在您的ConfigureAuth()

public void ConfigureAuth(IAppBuilder app)
{
   // .... other auth code

   // Yes, boo hiss, service location, not much choice...
   // Setup Authorization Server
   app.UseOAuthAuthorizationServer(_kernel.Value
       .Get<MyOAuthAuthorizationServerOptions>().GetOptions());
}

<击>

然后创建一个界面:

public interface IOAuthAuthorizationServerOptions 
{
    OAuthAuthorizationServerOptions GetOptions();
};

创建您的实施:

public class MyOAuthAuthorizationServerOptions : IOAuthAuthorizationServerOptions 
{
     private IOAuthAuthorizationServerProvider _provider;
     private IAuthenticationTokenProvider _tokenProvider;

     public MyOAuthAuthorizationServerOptions(IAuthenticationTokenProvider tProvider,
         IOAuthAuthorizationServerProvider provider)
     {
         _provider = provider;
         _tokenProvider = tProvider;
     }
     public OAuthAuthorizationServerOptions GetOptions()
     {
         return new OAuthAuthorizationServerOptions()
                    {
                       AllowInsecureHttp = true, //TODO: HTTPS
                       TokenEndpointPath = new PathString("/token"),
                       AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
                       Provider = _provider, 
                       RefreshTokenProvider = _tokenProvider
                    };
     }
}

编辑(4/6/15):

在进一步考虑之后,我认为Lazy<T>添加了一个非常不必要的附加引用。这可以修改为以更清洁的方式实现相同的结果:

创建一个新的Startup.Ninject.cs类,并将其放在App_Start中:

public partial class Startup
{
    public IKernel ConfigureNinject(IAppBuilder app)
    {
        var config = new HttpConfiguration();
        var kernel = CreateKernel();
        app.UseNinjectMiddleware(() => kernel)
           .UseNinjectWebApi(config);

        return kernel;
    }

    public IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Load(Assembly.GetExecutingAssembly());
        return kernel;
    }
}

public class NinjectConfig : NinjectModule
{
    public override void Load()
    {
        RegisterServices();
    }

    private void RegisterServices()
    {
        kernel.Bind<IOAuthAuthorizationServerOptions>()
            .To<MyOAuthAuthorizationServerOptions>();
        kernel.Bind<IOAuthAuthorizationServerProvider>()
            .To<AuthorizationServerProvider>();
        kernel.Bind<IAuthenticationTokenProvider>().To<RefreshTokenProvider>();
        kernel.Bind<IUserService>().To<MyUserService>();
    }
}

然后,在Startup中执行此操作:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var kernel = ConfigureNinject(app);
        ConfigureAuth(app, kernel);
    }
}

最后,修改ConfigureAuth以获取第二个参数并改为使用它。

public void ConfigureAuth(IAppBuilder app, IKernel kernel)
{
   // .... other auth code

   // Yes, boo hiss, service location, not much choice...
   // Setup Authorization Server
   app.UseOAuthAuthorizationServer(
       kernel.Get<MyOAuthAuthorizationServerOptions>().GetOptions());
}