【问题标题】:Autofac, Owin, Webapi and injecting to AuthorizationServerProviderAutofac、Owin、Webapi 和注入到 AuthorizationServerProvider
【发布时间】:2014-12-24 01:54:22
【问题描述】:

在阅读了有关在 owin 和 webapi 中使用 autofac 的问题和文章后,我发现了一个注入服务的解决方案,但它不起作用。这是我的代码:

public class StartUp
{
    public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();
        WebApiConfig.Register(config);

        var builder = new ContainerBuilder();                                    // Create the container builder.
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());         // Register the Web API controllers.

        var authcontext = new AuthContext();

        builder.RegisterInstance(authcontext).AsSelf().SingleInstance();

        //Updated
        //var simpleauth = new SimpleAuthorizationServerProvider();
        //Updated
        // builder.RegisterInstance(simpleauth).SingleInstance().AsSelf().PropertiesAutowired();

        builder.Register(x => new UserStore<IdentityUser>(authcontext)).As<IUserStore<IdentityUser>>();

        //updated

        builder.Register(x =>
        {
            var p = new SimpleAuthorizationServerProvider();
            var userStore = x.Resolve<IUserStore<IdentityUser>>();
            p.userManager = new UserManager<IdentityUser>(userStore);
            return p;
        }).AsSelf().PropertiesAutowired();

        builder.RegisterType<AuthRepository>().As<IAuthRepository>().InstancePerRequest().PropertiesAutowired();

        var container = builder.Build();

        var resolver = new AutofacWebApiDependencyResolver(container);           // Create an assign a dependency resolver for Web API to use.
        config.DependencyResolver = resolver;

        app.UseAutofacMiddleware(container);

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

        ConfigureOAuth(app, resolver);
    }

    public void ConfigureOAuth(IAppBuilder app, AutofacWebApiDependencyResolver resolver)
    {
        OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
           //updated
            Provider =  new SimpleAuthorizationServerProvider()
//resolver.GetService(typeof(SimpleAuthorizationServerProvider)) as SimpleAuthorizationServerProvider
        };

        // Token Generation
        app.UseOAuthAuthorizationServer(OAuthServerOptions);
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

    }


}

但是在 SimpleAuthorizationServerProvider 类中,当开始调用像 ValidateClientAuthentication 这样的方法时,所有的服务都是空的,代码如下:

        public readonly IAuthRepository repository;
        public readonly UserManager<IdentityUser> userManager;
        public readonly AuthContext dbContext;

        public SimpleAuthorizationServerProvider()
        {

        }

        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            string clientId;
            string clientSecret;

            if (context.TryGetFormCredentials(out clientId, out clientSecret))
            {


                try
                {
                    Client client = await repository.FindClientById(clientId);
                }
            }
        }

你能帮帮我吗?

更新

如果在 ConfigureOAuth 方法中我使用以下方法:

            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = resolver.GetService(typeof(SimpleAuthorizationServerProvider)) as SimpleAuthorizationServerProvider
            };

我得到错误:

An exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll but was not handled in user code

Additional information: No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.

【问题讨论】:

    标签: asp.net-web-api autofac owin


    【解决方案1】:

    当您注册对象的实例而不是类型时,即使您指定PropertiesAutowired 也不会生效,因为 Autofac 假定您在创建实例时已完成所有您想要的工作。如果您想要连接属性,则需要在 OnActivated 处理程序中执行此操作。

    在这个示例代码中实际上有很多东西不起作用。

    • SimpleAuthorizationServerProvider 中的值是fields 而不是properties,所以PropertiesAutowired 对它们不起作用。
    • 这些字段被标记为只读且从未设置。
    • 您已将 UserManager&lt;IdentityUser&gt; 注册为 lambda,但也有 PropertiesAutowired,这将不起作用 - 您只能在基于反射的组件上使用 PropertiesAutowired(例如,RegisterType&lt;T&gt;)。

    考虑为您的提供者注册一个 lambda 并在 lambda 中设置所有内容:

    builder.Register(c => {
      var p = new SimpleAuthorizationServerProvider();
      p.repository = c.Resolve<UserManager<IdentityUser>>();
      // ...and so on
      return p;
    }).AsSelf().SingleInstance();
    

    另外,请记住,如果您注册了一个实例(或注册为 SingleInstance,则属性将被解析一次,仅此而已。因此,如果您有一些依赖项是 InstancePerDependencyInstancePerRequest,那就是不会像你想象的那样工作——它们会被一次性解决,之后就会变成单例。


    更新 1

    根据原始和更新的代码,我想到 如果您可以查看一些 Autofac 文档以更好地了解它的工作原理,那将是一件好事。例如,使用SimpleAuthorizationServerProvider 中的字段显示您可能无法完全了解 Autofac 中的注入工作原理或如何正确注册内容以便 Autofac 可以为您完成工作。

    例如,查看更新...

    • 您现在为 SimpleAuthorizationServerProvider 注册了一个 lambda,但我看不到您在哪里设置了 repository 字段。
    • SimpleAuthorizationServerProvider 注册上不需要PropertiesAutowired,因为您正在注册一个lambda,并且属性不会自动装配(如前所述)。
    • 我看到的唯一注册为InstancePerRequest 的组件是AuthRepository,但是,就像我说的,我看不到它在哪里被解决或设置——这是唯一会产生你提到的异常的东西。 There is an FAQ on dealing with that exact exception that you should look into.

    此外,您正在展示两个不同版本的 OAuthServerOptions 正在初始化,很难分辨哪个是“真实的”。

    我建议进行相当大的重构,以使事物能够正确使用 DI。

    更改 SimpleAuthorizationServerProvider 以停止使用公共字段并将它们添加为构造函数参数,以便 Autofac 可以为您连接这些东西。

    public class SimpleAuthorizationServerProvider
    {
      public IAuthRepository Repository { get; private set; }
      public UserManager<IdentityUser> UserManager {get; private set; }
      public AuthContext Context { get; private set; }
      public SimpleAuthorizationServerProvider(
        IAuthRepository repository,
        UserManager<IdentityUser> userManager,
        AuthContext context)
      {
        this.Repository = repository;
        this.UserManager = userManager;
        this.AuthContext = context;
      }
    }
    

    在启动期间,修复您的注册以删除无关内容并利用 Autofac 自动连接的优点。

    public class StartUp
    {
      public void Configuration(IAppBuilder app)
      {
        var config = new HttpConfiguration();
        WebApiConfig.Register(config);
    
        var builder = new ContainerBuilder();
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
    
        // Register the auth context instance but skip
        // the extra .AsSelf() and .SingleInstance() because
        // it's implicit.
        builder.RegisterInstance(new AuthContext());
    
        // Use the lambda to resolve the auth context rather
        // than making a closure over an instance.
        builder.Register(c => new UserStore<IdentityUser>(c.Resolve<AuthContext>()))
               .As<IUserStore<IdentityUser>>();
    
        // Just register the provider type and let Autofac
        // do the work without all this manual stuff. Skip
        // the .AsSelf() because it's implicit if you don't
        // specify other interfaces and don't auto-wire properties
        // because you don't need it.
        builder.RegisterType<SimpleAuthorizationProvider>();
    
        // This is fine, but I can't tell where it's used - if
        // you are using it at app startup or OUTSIDE a request,
        // you will get that exception you noted. Also, unless
        // you're actually using property injection, lose the
        // .PropertiesAutowired() call.
        builder.RegisterType<AuthRepository>()
               .As<IAuthRepository>()
               .InstancePerRequest()
               .PropertiesAutowired();
    
        var container = builder.Build();
        var resolver = new AutofacWebApiDependencyResolver(container);
        config.DependencyResolver = resolver;
    
        app.UseAutofacMiddleware(container);
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        app.UseWebApi(config);
    
        ConfigureOAuth(app, resolver);
      }
    
      public void ConfigureOAuth(IAppBuilder app, AutofacWebApiDependencyResolver resolver)
      {
        var options = new OAuthAuthorizationServerOptions()
        {
          AllowInsecureHttp = true,
          TokenEndpointPath = new PathString("/token"),
          AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
    
          // If you want the values to be wired up, you have
          // to do a resolve. Note, however, that since you're
          // doing this wire-up at app startup, there's no request
          // scope, so if something in here is registered `InstancePerRequest`
          // you will get an exception.
          Provider =  resolver.GetService(typeof(SimpleAuthorizationServerProvider)) as SimpleAuthorizationServerProvider
        };
    
        app.UseOAuthAuthorizationServer(options);
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
      }
    }
    

    假设所有代码都应该没问题。如果没有设置 - 比如 SimpleAuthorizationServerProvider 属性之一作为 null 出现,或者如果你得到一个异常,因为它是缺少依赖项,或者如果您收到关于没有请求范围的异常......那么还有其他一些您没有提出问题的事情。

    再一次,请花时间查看文档并熟悉 Autofac。 我认为您遇到的许多问题都是由于对连接方式的一些误解造成的.

    【讨论】:

    • 你说得对,我忘了将字段作为属性。但是我尝试了您的解决方案,但对于提到的属性,我仍然为 null。
    • 你能用新的/固定的代码更新你的问题吗?看不到的东西很难解决。
    • @persianDeveloper 我想我目前遇到了同样的问题。有什么解决办法吗?
    • @rudimenter 实际上我更喜欢该应用程序的另一种解决方案
    • @rudimenter 请查看我的回答here,我在其中解释了我对类似问题的解决方案。希望对你有帮助。
    猜你喜欢
    • 1970-01-01
    • 2019-02-09
    • 1970-01-01
    • 2020-04-18
    • 2015-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-02
    相关资源
    最近更新 更多