簡體   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

我決定清理這篇文章,並在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/來實現“Basic”使用消息處理程序在ASP.NET Web API中進行HTTP身份驗證“。 我是IoC / DI的新手,我正試圖讓它與Castle Windsor合作。

我一直在嘗試很多不同的東西但是我得到以下錯誤之一取決於我做錯了什么:

  • “看起來你忘了注冊http模塊Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule”
  • “你調用的對象是空的。” 對於BasicAuthMessageHandler中的PrincipalProvider
  • “沒有找到支持服務的組件* .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;
}

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

修改后的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();
    }
}

我假設IProvidePrincipal是您自己的實現。 最好的方法,唯一一個使用IoC容器的IMHO是Composition Root ploeh的博客很好地解釋了web api的入口點/組合根。 DelegatingHandler不是“請求解析”的一部分,因此您可以選擇在容器作為私有變量存在的全局asax Application_start中解析它。

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

如果您在容器中正確注冊了處理程序及其所有依賴項,則無需執行任何其他操作:從容器中提取並在MessageHandler中添加的處理程序實例將具有IPrincipalProvider和(I)UserRepository。 請記住,BasicAuthMessageHandler將扮演一個單例,因此如果您需要在每個方法調用上使用(I)UserRepository的新實例...您可以考慮使用TypedFactory創建您的(I)UserRepository作為后期依賴項

當然,從您的頂部圖形組件開始的任何組件都必須在容器中注冊

這是一種簡單的方法......如果你需要更加復雜的東西,你最終可能會為DelegatingHandlers創建你的“組合根”。

順便說一下:永遠不要像UserRepository userRepo = new UserRepository()這樣做。 或PrincipalProvider = new DummyPrincipalProvider()

沒有任何“行為實例”應該明確創建:容器負責在正確的時間提供正確的實例...

根據Jon Edit:現在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)作為Transient,但請記住它們將被設計為“singleton”:將它添加到MessageHandlers集合意味着它們將在每個請求上重用。

根據Jon Edit 2:

在github上添加了一個示例 您應該能夠構建它並使用NuGet Package Restore運行它

關於PerWebRequest的問題是關於NHibernate工廠會話創建會話“PerWebRequest”的UserRepository的依賴關系:由於HttpContext,您無法在Application_Start中解析IPrincipalProvider-> IUserRepository-> ISession。 如果你真的需要一個IUserRepositry工作w / IPrincipalProvider依賴必須是IUserRepositoryFactory(TypedFactory)。 我嘗試使用打字的工廠來修復你的樣本並且它有效,但是我遇到了NHibernate配置的問題,因為我不是那個專家,所以我沒有再進一步了。

如果您需要幫助w /工廠的東西... LMK和我將使用DummyPrincipalProvider中的工廠更新我的git示例

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM