简体   繁体   English

ASP.NET Web API中的Castle Windsor / DelegatingHandler / IPrincipal依赖注入(DI)/控制反转(IoC)

[英]Castle Windsor/DelegatingHandler/IPrincipal Dependency Injection (DI)/Inversion of Control (IoC) in ASP.NET Web API

I decided to clean this post up and I posted a sample project at ge.tt/3EwoZEd/v/0?c 我决定清理这篇文章,并在ge.tt/3EwoZEd/v/0?c上发布了一个示例项目

Spent around 30 hours on this already and still can't figure it out... help would be really appreciated! 已经花费了大约30个小时,但仍然无法弄明白......帮助真的很感激!

I have an ASP.NET Web API solution that uses this code: http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers/ to implement "Basic HTTP authentication in ASP.NET Web API using Message Handlers". 我有一个使用此代码的ASP.NET Web API解决方案: http//www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers/来实现“Basic”使用消息处理程序在ASP.NET Web API中进行HTTP身份验证“。 I'm new to IoC/DI and I'm trying to get this to work with Castle Windsor. 我是IoC / DI的新手,我正试图让它与Castle Windsor合作。

I've been trying a lot of different things but I get 1 of the following errors depending on what I did wrong: 我一直在尝试很多不同的东西但是我得到以下错误之一取决于我做错了什么:

  • "Looks like you forgot to register the http module Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule" “看起来你忘了注册http模块Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule”
  • "Object reference not set to an instance of an object." “你调用的对象是空的。” for the PrincipalProvider in BasicAuthMessageHandler 对于BasicAuthMessageHandler中的PrincipalProvider
  • "No component for supporting the service *.DummyPrincipalProvider was found" “没有找到支持服务的组件* .DummyPrincipalProvider被发现”

Below is my code: 以下是我的代码:


Global.asax.cs: 的Global.asax.cs:

private static IWindsorContainer _container;

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    var config = (CustomErrorsSection)ConfigurationManager.GetSection("system.web/customErrors");

    IncludeErrorDetailPolicy errorDetailPolicy;

    switch (config.Mode)
    {
        case CustomErrorsMode.RemoteOnly:
            errorDetailPolicy
                = IncludeErrorDetailPolicy.LocalOnly;
            break;
        case CustomErrorsMode.On:
            errorDetailPolicy
                = IncludeErrorDetailPolicy.Never;
            break;
        case CustomErrorsMode.Off:
            errorDetailPolicy
                = IncludeErrorDetailPolicy.Always;
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }

    GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = errorDetailPolicy;

    ConfigureWindsor(GlobalConfiguration.Configuration);

    GlobalConfiguration.Configuration.MessageHandlers.Add(new BasicAuthMessageHandler()
    {
        PrincipalProvider = _container.Resolve<IProvidePrincipal>()
    });
}

public static void ConfigureWindsor(HttpConfiguration configuration)
{
    // Create / Initialize the container  
    _container = new WindsorContainer();

    // Find our IWindsorInstallers from this Assembly and optionally from our DI assembly which is in abother project.  
    _container.Install(FromAssembly.This());
    _container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));

    //Documentation http://docs.castleproject.org/Windsor.Typed-Factory-Facility.ashx  
    // Set the WebAPI DependencyResolver to our new WindsorDependencyResolver  
    var dependencyResolver = new WindsorDependencyResolver(_container);
    configuration.DependencyResolver = dependencyResolver;
}

Windsor Installer: Windsor安装人员:

public class PrincipalsInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly().BasedOn<DelegatingHandler>());

        container.Register(
            Component.For<IProvidePrincipal>().ImplementedBy<DummyPrincipalProvider>()
        );
    }
}

Modified DummyPrincipalProvider (from the original I got from the URL above ): 修改后的DummyPrincipalProvider(来自我从上面URL获得的原始内容):

public class DummyPrincipalProvider : IProvidePrincipal
{
    private IUserRepository _userRepo;

    public DummyPrincipalProvider(IUserRepository userRepo)
    {
        this._userRepo = userRepo;
    }

    public IPrincipal CreatePrincipal(string username, string password)
    {
        try
        {
            if (!this._userRepo.ValidateUser(username, password))
            {
                return null;
            }
            else
            {
                var identity = new GenericIdentity(username);
                IPrincipal principal = new GenericPrincipal(identity, new[] { "User" });

                if (!identity.IsAuthenticated)
                {
                    throw new ApplicationException("Unauthorized");
                }

                return principal;
            }
        }
        catch
        {
            return null;
        }
    }
}

WindsorDependencyResolver.cs: WindsorDependencyResolver.cs:

internal sealed class WindsorDependencyResolver : IDependencyResolver
{
    private readonly IWindsorContainer _container;

    public WindsorDependencyResolver(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        _container = container;
    }

    public object GetService(Type t)
    {
        return _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null;
    }

    public IEnumerable<object> GetServices(Type t)
    {
        return _container.ResolveAll(t).Cast<object>().ToArray();
    }

    public IDependencyScope BeginScope()
    {
        return new WindsorDependencyScope(_container);
    }

    public void Dispose()
    {

    }
}

WindsorDependencyScope.cs: WindsorDependencyScope.cs:

internal sealed class WindsorDependencyScope : IDependencyScope
{
    private readonly IWindsorContainer _container;
    private readonly IDisposable _scope;

    public WindsorDependencyScope(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        _container = container;
        _scope = container.BeginScope();
    }

    public object GetService(Type t)
    {
        return _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null;
    }

    public IEnumerable<object> GetServices(Type t)
    {
        return _container.ResolveAll(t).Cast<object>().ToArray();
    }

    public void Dispose()
    {
        _scope.Dispose();
    }
}

I assume IProvidePrincipal is your own implementation. 我假设IProvidePrincipal是您自己的实现。 Best way, the only one IMHO, to use an IoC container is the Composition Root . 最好的方法,唯一一个使用IoC容器的IMHO是Composition Root The entry point/composition root for web api has been well explained by ploeh's blog . ploeh的博客很好地解释了web api的入口点/组合根。 DelegatingHandler are not part of the "request resolving", so you may choose to resolve it within global asax Application_start where the container lives as private variable. DelegatingHandler不是“请求解析”的一部分,因此您可以选择在容器作为私有变量存在的全局asax Application_start中解析它。

GlobalConfiguration.Configuration.MessageHandlers.Add(container.Resolve<BasicAuthMessageHandler>());

If you properly registered your handler and all its dependencies in the container, nothing else has to be done: handler instance you extracted from the container and added among MessageHandlers will have an IPrincipalProvider and (I)UserRepository. 如果您在容器中正确注册了处理程序及其所有依赖项,则无需执行任何其他操作:从容器中提取并在MessageHandler中添加的处理程序实例将具有IPrincipalProvider和(I)UserRepository。 Keep in mind BasicAuthMessageHandler will act a singleton, so if you need a new instance of (I)UserRepository on each method call... you may consider TypedFactory to create your (I)UserRepository as late dependencies 请记住,BasicAuthMessageHandler将扮演一个单例,因此如果您需要在每个方法调用上使用(I)UserRepository的新实例...您可以考虑使用TypedFactory创建您的(I)UserRepository作为后期依赖项

Of course, any component starting from you top graph component have to be register in the container . 当然,从您的顶部图形组件开始的任何组件都必须在容器中注册

That's the easy way... in case you need somenthing more sophisticate, you may end up creating your "composition root" for DelegatingHandlers as well. 这是一种简单的方法......如果你需要更加复杂的东西,你最终可能会为DelegatingHandlers创建你的“组合根”。

BTW: never, ever, doing somenthing like UserRepository userRepo = new UserRepository(); 顺便说一下:永远不要像UserRepository userRepo = new UserRepository()这样做。 or PrincipalProvider = new DummyPrincipalProvider() 或PrincipalProvider = new DummyPrincipalProvider()

none of the "Behaviour instance" should be created explicitly: container take care of providing right instance at the right time... 没有任何“行为实例”应该明确创建:容器负责在正确的时间提供正确的实例...

As per Jon Edit: now DummyPrincipalProvider looks fine: just keep in mind since DummyPrincipalProvider is created among the message handler(act as singleton due to global registration), you are reusing always same instance. 根据Jon Edit:现在DummyPrincipalProvider看起来很好:请记住,因为DummyPrincipalProvider是在消息处理程序中创建的(由于全局注册而充当单例),所以你总是重用相同的实例。

Instead of your plumbing 而不是你的管道

var dependencyResolver = new WindsorDependencyResolver(_container);
configuration.DependencyResolver = dependencyResolver;

I rather use ploeh implementation(see above). 我宁愿使用ploeh实现(见上文)。

Your registration 你的注册

container.Register(
    Component.For<IProvidePrincipal>().ImplementedBy<DummyPrincipalProvider>()
    .UsingFactoryMethod(kernel => kernel.Resolve<DummyPrincipalProvider>())
);

should be replaced with 应该换成

container.Register(
    Component.For<IProvidePrincipal>().ImplementedBy<DummyPrincipalProvider>()
);

that's wrong... container has to resolve it, not you explicitly 那是错的......容器必须解决它,而不是你明确地解决它

GlobalConfiguration.Configuration.MessageHandlers.Add(new BasicAuthMessageHandler());

stick with my configuration as above: BasicAuthMessageHandler resolved via container. 坚持我的配置如上:BasicAuthMessageHandler通过容器解决。

Let me know if it works. 如果有效,请告诉我。

PS: You registered the TypedFactory facility in the container, but you are not using it... just to let you know. PS:您在容器中注册了TypedFactory工具,但您没有使用它......只是为了让您知道。 You registered DelegatingHandler(s) as Transient, but keep in mind they gonna be "singleton" by design: adding it to the MessageHandlers collection imply they gonna be reused on each request. 您注册了DelegatingHandler(s)作为Transient,但请记住它们将被设计为“singleton”:将它添加到MessageHandlers集合意味着它们将在每个请求上重用。

As per Jon Edit 2: 根据Jon Edit 2:

I added a sample on github . 在github上添加了一个示例 You should be able to build it and run it using NuGet Package Restore 您应该能够构建它并使用NuGet Package Restore运行它

Your issue about PerWebRequestdepends on the depencies of UserRepository on the NHibernate factory session creating session "PerWebRequest": you cannot resolve IPrincipalProvider->IUserRepository->ISession in Application_Start due to HttpContext. 关于PerWebRequest的问题是关于NHibernate工厂会话创建会话“PerWebRequest”的UserRepository的依赖关系:由于HttpContext,您无法在Application_Start中解析IPrincipalProvider-> IUserRepository-> ISession。 If you really need a IUserRepositry working w/ IPrincipalProvider dependency has to be to a IUserRepositoryFactory(TypedFactory) instead. 如果你真的需要一个IUserRepositry工作w / IPrincipalProvider依赖必须是IUserRepositoryFactory(TypedFactory)。 I tried to fix your sample using the typed factory and it works, but than I had an issue w/ NHibernate configuration and since I'm not an expert of that, I didn't go any further. 我尝试使用打字的工厂来修复你的样本并且它有效,但是我遇到了NHibernate配置的问题,因为我不是那个专家,所以我没有再进一步了。

If you need help w/ the factory thing... LMK and I'll update my git sample using a factory within the DummyPrincipalProvider 如果您需要帮助w /工厂的东西... LMK和我将使用DummyPrincipalProvider中的工厂更新我的git示例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM