温莎城堡/ DelegatingHandler / IPrincipal的依赖注入(DI)中的ASP.NET Web API控制(IOC)的/反转 [英] Castle Windsor/DelegatingHandler/IPrincipal Dependency Injection (DI)/Inversion of Control (IoC) in ASP.NET Web API

查看:264
本文介绍了温莎城堡/ DelegatingHandler / IPrincipal的依赖注入(DI)中的ASP.NET Web API控制(IOC)的/反转的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我决定要整顿这个职位和我张贴在 ge.tt/3EwoZEd/一个示例项目v / 0摄氏度

大约30所花时间在这个早已和仍然无法弄清楚...帮助将是非常美联社preciated!

我有一个使用这种code的ASP.NET Web API的解决方案: <一个href=\"http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers/\" rel=\"nofollow\">http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers/实行使用消息处理程序的ASP.NET Web API基本HTTP验证。我是新来的IoC / DI,我试图让这个与温莎城堡工作。

我已经尝试了很多不同的东西,但我得到了以下错误1取决于我做错了什么:


  • 您似乎忘记注册HTTP模块Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule

  • 对象引用不设置到对象的实例。对于PrincipalProvider在BasicAuthMessageHandler

  • 支持服务的成分,不* .DummyPrincipalProvider发现

下面是我的code:


的Global.asax.cs:

 私有静态IWindsorContainer _container;保护无效的Application_Start()
{
    AreaRegistration.RegisterAllAreas();    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);    变种配置=(CustomErrorsSection)ConfigurationManager.GetSection(的System.Web /的customErrors);    IncludeErrorDetailPolicy errorDetailPolicy;    开关(config.Mode)
    {
        案例CustomErrorsMode.RemoteOnly:
            errorDetailPolicy
                = IncludeErrorDetailPolicy.LocalOnly;
            打破;
        案例CustomErrorsMode.On:
            errorDetailPolicy
                = IncludeErrorDetailPolicy.Never;
            打破;
        案例CustomErrorsMode.Off:
            errorDetailPolicy
                = IncludeErrorDetailPolicy.Always;
            打破;
        默认:
            抛出新ArgumentOutOfRangeException();
    }    GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = errorDetailPolicy;    ConfigureWindsor(GlobalConfiguration.Configuration);    GlobalConfiguration.Configuration.MessageHandlers.Add(新BasicAuthMessageHandler()
    {
        PrincipalProvider = _container.Resolve&LT; IProvidePrincipal&GT;()
    });
}公共静态无效ConfigureWindsor(HttpConfiguration配置)
{
    //创建/初始化容器
    _container =新WindsorContainer();    //找到我们从本届大会,并视情况从我们的DI组件,该组件是abother项目IWindsorInstallers。
    _container.Install(FromAssembly.This());
    _container.Kernel.Resolver.AddSubResolver(新CollectionResolver(_​​container.Kernel,真实));    //文档http://docs.castleproject.org/Windsor.Typed-Factory-Facility.ashx
    //设置的WebAPI DependencyResolver我们的新WindsorDependencyResolver
    VAR dependencyResolver =新WindsorDependencyResolver(_​​container);
    configuration.DependencyResolver = dependencyResolver;
}


温莎安装:

 公共类PrincipalsInstaller:IWindsorInstaller
{
    公共无效安装(IWindsorContainer容器,IConfigurationStore店)
    {
        container.Register(Classes.FromThisAssembly()支持算法FMP&所述; DelegatingHandler&GT;());        container.Register(
            Component.For&LT; IProvidePrincipal&GT;()ImplementedBy&LT; D​​ummyPrincipalProvider&GT;()
        );
    }
}


修改DummyPrincipalProvider(从我从<一个得到原href=\"http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers/\"相对=nofollow>网址上面):

 公共类DummyPrincipalProvider:IProvidePrincipal
{
    私人IUserRepository _userRepo;    公共DummyPrincipalProvider(IUserRepository userRepo)
    {
        this._userRepo = userRepo;
    }    公众的IPrincipal CreatePrincipal(用户名字符串,字符串密码)
    {
        尝试
        {
            如果(!this._userRepo.ValidateUser(用户名,密码))
            {
                返回null;
            }
            其他
            {
                VAR身份=新的GenericIdentity(用户名);
                主要的IPrincipal =新的GenericPrincipal(身份,新的[] {用户});                如果(!identity.IsAuthenticated)
                {
                    抛出新ApplicationException的(未授权);
                }                返回本金;
            }
        }
        抓住
        {
            返回null;
        }
    }
}


WindsorDependencyResolver.cs:

 内部密封类WindsorDependencyResolver:的IDependencyResolver
{
    私人只读IWindsorContainer _container;    公共WindsorDependencyResolver(IWindsorContainer容器)
    {
        如果(集装箱== NULL)
        {
            抛出新的ArgumentNullException(容器);
        }        _container =容器;
    }    公共对象GetService的(T型)
    {
        返回_container.Kernel.HasComponent(T)? _container.Resolve(T):空;
    }    公共IEnumerable的&LT;对象&gt; GetServices(T型)
    {
        返回_container.ResolveAll(t)的.Cast&所述;对象方式&gt;()ToArray的();
    }    公共IDependencyScope BeginScope()
    {
        返回新WindsorDependencyScope(_container);
    }    公共无效的Dispose()
    {    }
}


WindsorDependencyScope.cs:

 内部密封类WindsorDependencyScope:IDependencyScope
{
    私人只读IWindsorContainer _container;
    私人只读了IDisposable _scope;    公共WindsorDependencyScope(IWindsorContainer容器)
    {
        如果(集装箱== NULL)
        {
            抛出新的ArgumentNullException(容器);
        }
        _container =容器;
        _scope = container.BeginScope();
    }    公共对象GetService的(T型)
    {
        返回_container.Kernel.HasComponent(T)? _container.Resolve(T):空;
    }    公共IEnumerable的&LT;对象&gt; GetServices(T型)
    {
        返回_container.ResolveAll(t)的.Cast&所述;对象方式&gt;()ToArray的();
    }    公共无效的Dispose()
    {
        _scope.Dispose();
    }
}


解决方案

我假设IProvidePrincipal是你自己的实现。
最好的办法,是唯一一个恕我直言,使用IoC容器是成分根
用于Web API的入口点/根组成由已 ploeh的博客得到很好的解释。
DelegatingHandler不是请求解决的一部分,所以你可以选择全球ASAX的Application_Start其中容器作为生活私有变量。

内解决它<$p$p><$c$c>GlobalConfiguration.Configuration.MessageHandlers.Add(container.Resolve<BasicAuthMessageHandler>());

如果你正确地注册了处理程序及其所有依赖于容器内,没有其他的工作要做:你从容器中提取出来并添加MessageHandlers之间的处理程序实例将有一个IPrincipalProvider和(I)UserRepository。请记住BasicAuthMessageHandler将作为一个单独的,所以如果你需要在每个方法调用(I)UserRepository的新实例......你可以考虑<一个href=\"http://docs.castleproject.org/Windsor.Typed-Factory-Facility-interface-based-factories.ashx?HL=typedfactory\">TypedFactory创建(I)UserRepository晚依赖

当然,任何组件都从你开始顶部图形组件必须注册>。

这是最简单的方式......如果你需要更多的somenthing老油条,你可能最终创建作文根为DelegatingHandlers为好。

BTW:永远,永远,做somenthing像
UserRepository userRepo =新UserRepository();
或PrincipalProvider =新DummyPrincipalProvider()

没有行为实例应明确创建:集装箱照顾在正确的时间正确的提供实例...

按乔恩编辑:
现在DummyPrincipalProvider看起来很好。只要记住,因为DummyPrincipalProvider在消息处理程序(充当单因全球注册)中创建的,你总是重复使用相同的实例

而不是你的水暖

  VAR dependencyResolver =新WindsorDependencyResolver(_​​container);
configuration.DependencyResolver = dependencyResolver;

我宁愿用ploeh实现(见上文)。

您注册

  container.Register(
    Component.For&LT; IProvidePrincipal&GT;()ImplementedBy&LT; D​​ummyPrincipalProvider&GT;()
    .UsingFactoryMethod(内核= GT; kernel.Resolve&LT; D​​ummyPrincipalProvider&GT;())
);

应替换

  container.Register(
    Component.For&LT; IProvidePrincipal&GT;()ImplementedBy&LT; D​​ummyPrincipalProvider&GT;()
);

这是错误的......容器具有解决它,而不是你明确地

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

坚持我的配置如上:BasicAuthMessageHandler通过容器解决

让我知道,如果它的工作原理。

PS:你注册在容器中的TypedFactory设施,但你不使用它...只是让你知道。
您注册DelegatingHandler(S)为瞬态,但要记住,他们会是单身的设计:将它添加到收藏MessageHandlers他们暗示将要对每个请求重复使用

按乔恩编辑2:

我添加GitHub上一个样本。你应该能够建立并使用的NuGet包恢复运行

您关于UserRepository对NHibernate的工厂会议会话创建PerWebRequest的depencies PerWebRequestdepends问题:您无法解决IPrincipalProvider-> IUserRepository->的ISession中的Application_Start由于HttpContext的。如果你真的需要一个IUserRepositry工作瓦特/ IPrincipalProvider依赖已是一个IUserRepositoryFactory(TypedFactory)代替。
我试着用类型化的工厂,以解决您的样品,它的工作原理,但比我有一个问题瓦特/ NHibernate的配置和因为我不是这方面的一个专家,我没有去任何进一步。

如果您需要帮助瓦特/工厂的事情... LMK,我会用DummyPrincipalProvider内的工厂更新我的git样品

I decided to clean this post up and I posted a sample project at 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!

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". I'm new to IoC/DI and I'm trying to get this to work with 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"
  • "Object reference not set to an instance of an object." for the PrincipalProvider in BasicAuthMessageHandler
  • "No component for supporting the service *.DummyPrincipalProvider was found"

Below is my code:


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:

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):

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:

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:

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. Best way, the only one IMHO, to use an IoC container is the Composition Root. The entry point/composition root for web api has been well explained by ploeh's blog. 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.

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. 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

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.

BTW: never, ever, doing somenthing like UserRepository userRepo = new UserRepository(); or 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.

Instead of your plumbing

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

I rather use ploeh implementation(see above).

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.

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. 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.

As per Jon Edit 2:

I added a sample on github. You should be able to build it and run it using 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. If you really need a IUserRepositry working w/ IPrincipalProvider dependency has to be to a IUserRepositoryFactory(TypedFactory) instead. 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.

If you need help w/ the factory thing... LMK and I'll update my git sample using a factory within the DummyPrincipalProvider

这篇关于温莎城堡/ DelegatingHandler / IPrincipal的依赖注入(DI)中的ASP.NET Web API控制(IOC)的/反转的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆