“ASP.NET Mvc / Web Api应用程序中的上下文中没有找到owin.Environment项”异常

时间:2014-06-26 13:05:56

标签: asp.net-mvc asp.net-web-api visual-studio-2013 owin

我们有一个与Autofac DI配合使用的MVC 5.1应用程序。我已经实现了一些基本的web api功能来提取和发布有效的数据。我们现在想在web api控制器上添加身份验证。为了做到这一点,我们将使用我们写给Mvc的一些相同的Autofac配置。所以,我们打电话如:

        builder.RegisterControllers(assembly).InstancePerHttpRequest();
        builder.RegisterApiControllers(assembly);
        builder.RegisterModelBinders(assembly).InstancePerHttpRequest();
        builder.RegisterType<LogAttribute>().PropertiesAutowired();
        builder.RegisterFilterProvider();

        // Needed to allow property injection in custom action filters.
        builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
        if (modules != null)
        {
            foreach (var module in modules)
            {
                builder.RegisterModule(module);
            }
        }

我们还有一个Api模块,其中包含以下代码并已注册(我们知道因为我们已调试并看到了所调用的代码):

public class MvcModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterModule(new AutofacWebTypesModule());

        builder.Register(ctx => HttpContext.Current.GetOwinContext()).As<IOwinContext>();
        builder.Register(ctx => HttpContext.Current.User.Identity).As<IIdentity>().InstancePerLifetimeScope();
        builder.Register(ctx => HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>()).As<ApplicationUserManager>().InstancePerLifetimeScope();
        builder.Register(c => BundleTable.Bundles).As<BundleCollection>().InstancePerLifetimeScope();
        builder.Register(c => RouteTable.Routes).As<RouteCollection>().InstancePerLifetimeScope();
        builder.RegisterType<CurrentUser>().As<ICurrentUser>().InstancePerLifetimeScope();
        builder.RegisterType<ApplicationUser>().As<IApplicationUser>().InstancePerLifetimeScope();

        builder.RegisterType<UserManager<ApplicationUser, Guid>>();
        builder.Register(c=>new appContext()).InstancePerHttpRequest();

        base.Load(builder);
    }
}        

public class ApiModule: Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
                .Where(t => !t.IsAbstract && typeof(ApiController).IsAssignableFrom(t))
                .InstancePerMatchingLifetimeScope(AutofacWebApiDependencyResolver.ApiRequestTag);


        builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).PropertiesAutowired();
        builder.RegisterModule<AutofacWebTypesModule>();
    }
}

然后我们有这两个语句应该为mvc和web api设置解析器(我认为):

        GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
        DependencyResolver.SetResolver(new Autofac.Integration.Mvc.AutofacDependencyResolver(container));

我们有一个Mvc AccountController,看起来类似于以下内容(为清晰起见,删除了一些方法):

public class AccountController : BaseController
{
    public AccountController(IOwinContext owinContext, IDataManager itemsManager)
        : base(owinContext, itemsManager)
    {
    }

    // POST: /Account/Login
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            var user = await UserManager.FindAsync(model.Email, model.Password);
            if (user != null)
            {
                return await LoginCommon(user, model.RememberMe, returnUrl);
            }
            ModelState.AddModelError("", "Invalid username or password.");
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }
}

当应用程序启动时,用户可以登录。正如所料,IOofContext由Autofac注入控制器。换句话说,Mvc模块中的这一行正确地注册了OwinContext:

        builder.Register(ctx => HttpContext.Current.GetOwinContext()).As<IOwinContext>();

如果用户以前注册过,则可以找到该用户。

在我们的Api帐户控制器中,我们会执行以下操作:

        [Route("{email}")]
    public async Task<HttpResponseMessage> GetUser(string email)
    {
        var user = await UserManager.FindByEmailAsync(email);
        if (user != null)
        {
            return Request.CreateResponse(HttpStatusCode.OK, user);
        }

        return Request.CreateResponse(HttpStatusCode.NotFound);
    }         

但是,UserManager不起作用 - 引发异常:

System.InvalidOperationException occurred
HResult=-2146233079
Message=No owin.Environment item was found in the context.
Source=Microsoft.Owin.Host.SystemWeb
StackTrace:
   at System.Web.HttpContextExtensions.GetOwinContext(HttpContext context)
   at application1.UI.Infrastructure.Modules.MvcModule.<Load>b__0(IComponentContext ctx) in c:\tfs\application1\MAIN\Source\.NET\application1.UI\Infrastructure\Modules\MvcModule.cs:line 25
InnerException: 

Autofac调用的模块是Mvc模块,而不是web api模块。发生此异常的行是这一行:

builder.Register(ctx => HttpContext.Current.GetOwinContext()).As<IOwinContext>();

换句话说,HttpContext对象似乎没有正确配置。但是,如前所述,它是设置的,因为它在Mvc的相同应用程序中工作。

所以问题是,如果在同一个应用程序中使用Mvc和web api,应该在哪里设置应用程序的owin配置?

我之前已经看过一些有关此主题的问题,但无济于事。

Is it possible to configure Autofac to work with ASP.NET MVC and ASP.NET Web Api

MVC Web API not working with Autofac Integration

在搜索此特定异常的解决方案时,我发现了一些关于缺少owin配置的讨论:

No owin.Environment item was found in the context

No owin.Environment item was found in the context - only on server

但是,当Owin适用于应用程序的Mvc部分时,我无法理解配置是如何丢失的。

我们正在使用VS 2013,.Net 4.5.1,Mvc 5.1和Autofac与web.api集成。

2 个答案:

答案 0 :(得分:0)

我们在项目中做了非常相似的事情,我使用Autofac工作。我将UserManager注入UserService(目前只是一个域层类而不是实际服务),但可以从Api和MVC控制器方法成功访问UserManager。这是我如何设置,这可能会有所帮助。我没有为API和MVC创建单独的模块,只是将它们全部放在一起(顺便说一句,你的解析器设置正确)。

首先,网络模块:

public class WebModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterFilterProvider();
        builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
        builder.RegisterControllers(Assembly.GetExecutingAssembly())
          .InjectActionInvoker();
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        // OWIN
        builder.Register(c => HttpContext.Current.GetOwinContext())
          .As<IOwinContext>().InstancePerLifetimeScope();

        // Module chaining
        builder.RegisterModule<DataModule>();
    }
}

接下来,数据模块(还有其他模块,但它们不相关):

public class DataModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        var context = new EntityContext();
        builder.RegisterInstance(context).As<IEntityContext>().SingleInstance();
        builder.Register(x => new UserStore<ApplicationUser>(context))
          .As<IUserStore<ApplicationUser>>().InstancePerLifetimeScope();
        builder.Register(x =>
        {
            var userStore = x.Resolve<IUserStore<ApplicationUser>>();
            var userManager = new UserManager<ApplicationUser>(userStore);
            return userManager;
        });
    }
}

我的EntityContext是一个IdentityDbContext<ApplicationUser>,我也在一个界面中包装,所以我可以设置一个虚假的用户商店和类似的东西。

我能看到的唯一不同之处在于,当你注册时,你似乎没有将IdentityContext传递给UserManager

答案 1 :(得分:0)

似乎我的设置没有标准。我再次从Mvc模板重新创建了项目,并添加了一个web api控制器。然后我可以按照预期通过web api登录和检索用户数据。

所以,问题已经解决,但我仍然不知道我们做了什么来打破它。