【发布时间】: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