We've got an MVC 5.1 app that works with Autofac DI. I've implemented some basic web api functionality to pull and post data which works. We now want to add authentication on the web api controllers. In order to do this, we are going to utilise some of the same Autofac configuration we've written to Mvc. So, we make calls such as:
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);
}
}
We've also got an Api module that contains the code below and is registered (we know because we've debugged and seen the code called):
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>();
}
}
we then have these two statements which should set resolvers for both mvc and web api (I think):
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
DependencyResolver.SetResolver(new Autofac.Integration.Mvc.AutofacDependencyResolver(container));
We've got an Mvc AccountController which looks similar to the following (some methods removed for clarity):
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);
}
}
When the app is started, a user can login. As expected, the IOwinContext is injected into the controller by Autofac. In other words, this line in the Mvc module correctly registers the OwinContext:
builder.Register(ctx => HttpContext.Current.GetOwinContext()).As<IOwinContext>();
The UserManager can find the user if they've previously registered.
Within our Api Account controller, we then have the following action:
[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);
}
However, the UserManager doesn't work - an exception is raised:
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:
The module that's being called by Autofac is the Mvc module, not the web api one. The line where this exception occurs is this one:
builder.Register(ctx => HttpContext.Current.GetOwinContext()).As<IOwinContext>();
In other words, the HttpContext object doesn't appear to be configured correct. However, as discussed earlier, it is setup because it works in the same application for Mvc.
So the question is, if Mvc and web api are being used in the same application, where should the owin config for the application be set?
I have viewed some questions asked previously about this topic, but to no avail.
Is it possible to configure Autofac to work with ASP.NET MVC and ASP.NET Web Api
and
MVC Web API not working with Autofac Integration
In searching for resolutions to this particular exception, I've found a couple which talk about missing owin configuration:
No owin.Environment item was found in the context
and
No owin.Environment item was found in the context - only on server
However, I can't understand how the configuration could be missing when Owin works fine for the Mvc part of the application.
We're using VS 2013, .Net 4.5.1, Mvc 5.1 and Autofac with web.api integration.