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

Geo*_*uer 7 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);
    }
Run Code Online (Sandbox Code Playgroud)

我相信这是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;
    }

}
Run Code Online (Sandbox Code Playgroud)

我在过去的项目中使用了这种模式,它运行良好.我正在将它应用于现有项目并看到一个非常奇怪的事情.

  • 当一个Asp.net Mvc控制器依赖于ICurrentUser一切正常
  • 的WebAPI控制器获取实例ICurrentUserGet,因为实例操作失败,IIdentity尚未从cookie解析,还没有装进去索赔(AuthenticationType == null)!奇怪的是,如果我在实例化WebApi控制器后暂停调试器,我可以HttpContext.Current.User.Identity看到AuthenticationType == "Cookie"并且所有声明都存在.

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

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

这当然毫无意义!

所以问题如下

  1. 我对管道中事物顺序的推断是否正确?
  2. 如何控制它才能正常工作?为什么这会对其他项目起作用,但会在这里造成问题?我是否以错误的顺序连接了什么?

请不要建议我创建一个IdentityProvider迟到的解决方案IIdentity.我理解如何解决这个问题,我不明白为什么会发生这种情况以及如何控制事物的管道顺序.

Ali*_*son 1

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

我安装了这些软件包:

  • 奥特法
  • 欧文
  • 欧文
  • 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);

    }

}
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

因此 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

...最后是一个简单的 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 "";
    }

}
Run Code Online (Sandbox Code Playgroud)

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

调试

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

在此输入图像描述

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