为什么在解析WebApi时未加载标识,而是在解析Mvc控制器时

时间:2016-01-10 19:50:39

标签: asp.net asp.net-mvc asp.net-web-api asp.net-identity

我正在使用Autofac进行Inversion of Control容器,其配置如下

    public void Configuration(IAppBuilder app) {
        configureIoC(app);
        configureAuth(app);
    }

    void configureIoC(IAppBuilder app) {
        var b = new ContainerBuilder();
        //...
        b.Register(c => HttpContext.Current?.User?.Identity 
                    ?? new NullIdentity()).InstancePerLifetimeScope();
        var container = b.Build();
        app.UseAutofacMiddleware(container);
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
        GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
    }

我认为这是Autofac与其他容器的事实可能与我所看到的无关。这里的关键行是配置IIdentity的任何依赖关系,以便从HttpContext.Current中提取。

我这样使用它,以便我可以随时随地访问当前用户。

public interface ICurrentUser {
    Task<AppUser> Get();
}
public class CurrentUserProvider : ICurrentUser {
    public async Task<AppUser> Get() => await users.FindByNameAsync(currentLogin.GetUserId());

    public CurrentUserProvider(AppUserManager users, IIdentity currentLogin) {
        this.users = users;
        this.currentLogin = currentLogin;
    }

}

我在过去的项目中使用了这种模式并且运行正常。我目前正在将它应用于现有项目并看到一件非常奇怪的事情。

  • Asp.net Mvc 控制器依赖于ICurrentUser时,一切正常
  • WebApi 控制器获取ICurrentUser的实例时,Get操作失败,因为IIdentity的实例尚未从cookie中解析而不是但已将声明加载到其中(AuthenticationType == null)!奇怪的是,如果我在实例化WebApi控制器后暂停调试器,我可以点击HttpContext.Current.User.Identity并看到AuthenticationType == "Cookie"并且所有声明都在那里。

这导致我得出的结论是,某些事情按照以下顺序发生

  • 如果这是web api路由,则Web Api控制器会创建实例
  • Asp.Net Identity填写当前的HttpContext身份
  • 如果这是mvc路由,则mvc控制器会创建一个实例
  • 执行任何操作

这当然毫无意义!

所以问题如下

  1. 我对管道中事物顺序的推断是否正确?
  2. 如何控制它才能正常工作?为什么这会对其他项目起作用,但会在这里造成问题?我是否以错误的顺序接线?
  3. 请不要建议我创建一个IdentityProvider来解决IIdentity。我理解如何解决这个问题,我不明白为什么会发生这种情况以及如何控制事物的管道顺序。

1 个答案:

答案 0 :(得分:1)

我修改了你的代码,因为我没有NullIdentity()且你的CurrentUserProvider没有在这里编译。

我安装了这些软件包:

  • Autofac
  • Autofac.Owin
  • Autofac.Owin
  • Autofac.Mvc5
  • Autofac.Mvc5.Owin
  • Autofac.WebApi2
  • Autofac.WebApi2.Owin

我的Startup.cs看起来像这样:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        configureIoC(app);
        ConfigureAuth(app);
    }

    void configureIoC(IAppBuilder app) {

        var b = new ContainerBuilder();
        //...
        b.RegisterType<CurrentUserProvider>().As <ICurrentUser>().InstancePerLifetimeScope();
        b.Register(c => HttpContext.Current.User.Identity).InstancePerLifetimeScope();

        b.RegisterControllers(typeof(MvcApplication).Assembly);
        b.RegisterApiControllers(typeof(MvcApplication).Assembly);

        var x = new ApplicationDbContext();
        b.Register<ApplicationDbContext>(c => x).InstancePerLifetimeScope();
        b.Register<UserStore<ApplicationUser>>(c => new UserStore<ApplicationUser>(x)).AsImplementedInterfaces().InstancePerLifetimeScope();
        b.RegisterType<ApplicationUserManager>().InstancePerLifetimeScope();
        b.RegisterType<ApplicationSignInManager>().InstancePerLifetimeScope();

        var container = b.Build();

        app.UseAutofacMiddleware(container);
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
        GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);

    }

}

您的ICurrentUser内容:

public interface ICurrentUser
{
    Task <ApplicationUser> Get();
}

public class CurrentUserProvider : ICurrentUser
{

    private  ApplicationUserManager users;
    private  IIdentity currentLogin;

    public async Task<ApplicationUser> Get()
    {
        return await users.FindByNameAsync(currentLogin.GetUserId());
    }

    public CurrentUserProvider(ApplicationUserManager users, IIdentity currentLogin)
    {
        this.users = users;
        this.currentLogin = currentLogin;
    }

}

因此Global.asax:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}

我的HomeController非常简单:

public class HomeController : Controller
{

    private ICurrentUser current;

    public HomeController(ICurrentUser current)
    {
        this.current = current;
    }

    public ActionResult Index()
    {
        var user = current.Get();
        if (user  == null)
            throw new Exception("user is null");
        return View();
    }

}

...最后是一个简单的ApiController,我通过输入 localhost / api / TestApi / 5 来访问:

public class TestApiController : ApiController
{

    private ICurrentUser current;

    public TestApiController(ICurrentUser current)
    {
        this.current = current;
    }

    public string Get(int id)
    {
        var user = current.Get();
        if (user == null)
            throw new Exception("user is null");
        return "";
    }

}

如果我刚启动项目(甚至没有登录),我会收到一个GenericIdentity对象来支持IIdentity接口,看看这个:

Debugging

当我在Get()方法中介入(F11)时,IIdentity已正确设置为GenericIdentity,因为实际上没有人登录应用程序。这就是为什么我认为你实际上并不需要那个NullableIdentity。

enter image description here

尝试将您的代码与我的代码进行比较并修复您的代码,以便我们查看它是否有效,然后最终您会发现问题的真正原因,而不仅仅是修复它(我们开发人员喜欢知道为什么有些东西正常工作。