【问题标题】:"No owin.Environment item was found in the context" exception in ASP.NET Mvc/Web Api applicationASP.NET Mvc/Web Api 应用程序中的“在上下文中找不到 owin.Environment 项”异常
【发布时间】:2014-06-26 13:05:55
【问题描述】:

我们有一个可与 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);
    }
}

当应用程序启动时,用户可以登录。正如预期的那样,IOwinContext 由 Autofac 注入到控制器中。也就是说,Mvc 模块中的这一行正确地注册了 OwinContext:

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

UserManager 可以找到之前注册过的用户。

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

        [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 config 应该在哪里设置呢?

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

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 集成。

【问题讨论】:

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


    【解决方案1】:

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

    首先,Web 模块:

    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&lt;ApplicationUser&gt;,我还封装在一个界面中,这样我就可以设置一个虚假的用户存储和类似的东西。

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

    【讨论】:

    • 感谢 levelnis。我会看看。我假设您从 MVC 应用程序开始。您在集成 web-api 身份验证时是否需要在 Startup 类 re: Owin 中做任何特别的事情?
    • 也许这就是区别 - 我们网站上的所有身份验证都是通过 MVC 完成的 - 没有进行特定于 Web API 的身份验证
    • 所以,你的意思是所有的 api 动作都是公开的?或者您通过 MVC 进行身份验证,并且只有经过身份验证的用户才能使用 api?
    • 后者 - 通过 MVC 进行身份验证,只有经过身份验证的用户才能使用 API
    【解决方案2】:

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

    所以,问题解决了,但我仍然不知道我们首先做了什么来解决它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-16
      • 2015-11-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多