温莎城堡/ DelegatingHandler / IPrincipal的依赖注入(DI)中的ASP

2019-08-18 11:40发布

我决定清理这个帖子了,我发布了一个示例项目在ge.tt/3EwoZEd/v/0?c

30小时左右花在这个已经和仍然无法弄清楚...帮助将是非常感谢!

我有一个使用此代码的ASP.NET Web API的解决方案: 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的成分,不被发现”

下面是我的代码:


的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;
}

温莎安装:

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

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

修改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:

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();
    }
}

Answer 1:

我认为IProvidePrincipal是自己的实现。 最好的办法,唯一的一个恕我直言,使用IoC容器是构成根 。 为Web API的入口点/组成根已经得到了很好的解释ploeh的博客 。 DelegatingHandler不是“请求解决”的一部分,所以你可以选择全球ASAX的Application_Start内解决它在容器生命为私有变量。

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

如果您正确注册您的处理程序及其所有依赖于容器,没有别的工作要做:你从容器中取出,并添加MessageHandlers中处理程序实例将有一个IPrincipalProvider和(I)UserRepository。 请记住BasicAuthMessageHandler将扮演一个单身,所以如果你需要在每个方法调用(I)UserRepository的新实例......你可以考虑TypedFactory创建你(I)UserRepository晚依赖

当然,任何组分从你开始顶部图组件必须在容器中注册 。

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

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

无“行为实例”应明确创建:容器拿在正确的时间提供正确的实例的照顾...

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

而不是你的水暖

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

我宁愿用ploeh执行(见上文)。

您的注册

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

应改为

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

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

GlobalConfiguration.Configuration.MessageHandlers.Add(new 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样本



文章来源: Castle Windsor/DelegatingHandler/IPrincipal Dependency Injection (DI)/Inversion of Control (IoC) in ASP.NET Web API