Введение
На Хабре много раз затрагивалась тема OWIN, однако до сих пор то и дело всплывают вопросы о реализации приложений и компонентов с помощью OWIN. В данной публикации я начну со стандартного шаблона Visual Studio 2013 и продемонстрирую реализацию архитектуры приложения. Также я покажу, как использовать один DI-контейнер — как для MVC, так и для WebApi в рамках одного проекта.
Конфигурирование WebApi
В стандартном шаблоне VS2013 конфигурация WebApi выполняется в global.asax. Перенесем ее в класс Startup.
Теперь зарегистрируем OWIN модуль WebApi. Для этого нам необходимо установить соответствующий NuGet пакет. Открываем Package Manager Console и вводим:
PM> Install-Package Microsoft.AspNet.WebApi.Owin
После установки пакета мы можем зарегистрировать OWIN Middleware для WebApi.
var apiConfig = ConfigureWebApi();
ConfigureDependencyInjection(app, apiConfig);
app.UseWebApi(apiConfig);
О методе «ConfigureDependencyInjection» мы поговорим далее.
Конфигурирование DI контейнера
В данном примере я использую DI-контейнер Autofac, т.к. он уже располагает необходимыми реализациями классов DependencyResolver для WebApi и MVC, а также методами расширения для интеграции с OWIN.
Установим необходимые модули:
PM> Install-Package Autofac.Mvc5
PM> Install-Package Autofac.WebApi2.Owin
Для интеграции MVC и Owin необходимо поставить еще один пакет:
PM> Install-Package Autofac.Mvc5.Owin
Поскольку я хочу продемонстрировать использование единого контейнера для WebApi и MVC, инициализация контейнера будет расположена в конфигурационном классе OWIN.
private void ConfigureDependencyInjection(IAppBuilder app, HttpConfiguration apiConfig)
{
var builder = new ContainerBuilder();
Assembly executingAssembly = Assembly.GetExecutingAssembly();
builder.RegisterApiControllers(executingAssembly);
builder.RegisterControllers(executingAssembly);
RegisterComponents(builder);
var container = builder.Build();
app.UseAutofacMiddleware(container);
var apiResolver = new AutofacWebApiDependencyResolver(container);
apiConfig.DependencyResolver = apiResolver;
app.UseAutofacWebApi(apiConfig);
var mvcResolver = new AutofacDependencyResolver(container);
DependencyResolver.SetResolver(mvcResolver);
app.UseAutofacMvc();
}
С этим кодом все очень просто. Сначала мы создаем ContainerBuilder и регистрируем в нём наши контроллеры, затем регистрируем сервисы. После этого создаем контейнер и устанавливаем его как DependencyResolver для WebApi и Mvc.
Здесь необходимо обратить внимание на строчку app.UseAutofacMvc();. Вызов этого метода позволяет расширить LifetimeScope объектов, чтобы они задействовались в MVC.
Реализация компонента безопасности приложения на примере AspNet Identity
Регистрирование компонентов
В стандартном шаблоне приложения уже установлены пакеты AspNet Identity, однако если вы начали с пустого шаблона, то необходимо установить следующие пакеты:
PM> Install-Package Microsoft.AspNet.Identity.Owin
PM> Install-Package Microsoft.AspNet.Identity.EntityFramework
Для реализации безопасности AspNet Identity нам необходимо зарегистрировать четыре класса:
- UserManager<ApplicationUser>
- SignInManager<ApplicationUser, string>
- IUserStore<ApplicationUser>
- IAuthenticationManager
С регистрацией компонентов SignInManager<ApplicationUser, string> и IUserStore нет никаких проблем, код их регистрации приведен ниже.
private void RegisterComponents(ContainerBuilder builder)
{
builder.RegisterType<ApplicationDbContext>().As<DbContext>().InstancePerRequest();
builder.RegisterType<ApplicationSignInManager>().As<SignInManager<ApplicationUser, string>>().InstancePerRequest();
builder.RegisterType<UserStore<ApplicationUser>>().As<IUserStore<ApplicationUser>>().InstancePerRequest();
}
Стоит заметить, что в качестве IUserStore я использовал класс библиотеки AspNet.Identity.EntityFramework, поэтому в регистрации присутствует класс ApplicationDbContext.
Далее необходимо зарегистрировать IAuthenticationManager. Здесь необходимо обратить внимание, что имплементация интерфейса IAuthenticationManager не имеет открытого конструктора, поэтому задаем factory method.
builder.Register<IAuthenticationManager>((c, p) => c.Resolve<IOwinContext>().Authentication).InstancePerRequest();
Свойство IOwinContext.Authentication фактически является методом-фабрикой и предоставляет нам новый AuthenticationManager при каждом вызове.
Теперь необходимо зарегистрировать класс UserManager. Конструктор этого класса не представляет особого интереса, но ниже в этом классе определен factory method “Create”, который отвечает за создание и конфигурацию этого класса.
Перенесем создание и конфигурирование класса в factory method autofac, чтобы держать всю конфигурацию вместе. В этом случае мы столкнемся с небольшой проблемой. Метод “Create” принимает IdentityFactoryOptions в качестве одного из аргументов. Мы не можем создать IdentityFactoryOptions сами. К счастью существует метод IAppBuilder.GetDataProtectionProvider(), расположенный в неймспейсе Microsoft.Owin.Security.DataProtection.
var dataProtectionProvider = app.GetDataProtectionProvider();
builder.Register<UserManager<ApplicationUser>>((c, p) => BuildUserManager(c, p, dataProtectionProvider));
Бизнес логика
Теперь можно использовать наш DI-контейнер для реализации логики приложения. Если мы посмотрим в AccountController, то увидим там такие строки:
HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
HttpContext.GetOwinContext().Authentication;
С помощью этих строк разрешаются объекты классов UserManager, SignInManager и IAuthenticationManager соответственно. Такой подход предлагается библиотекой AspNet Identity. Он не подходит нам по нескольким причинам, самые очевидные из них:
- Использование ServiceLocator не позволяет нам контролировать зависимости внутри класса.
- Появление второго DI контейнера, который напрямую зависит от AspNet Identity.
Удалим свойства UserManager, SignInManager, AuthenticationManager и добавим инициализацию полей _userManager и _authenticationManager через конструктор. Также удалим конструктор без параметров. Аналогичным образом исправим ManageController. В методе конфигурации Identity убираем строки, регистрирующие наши классы в OwinContext.
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
Теперь можно удалить лишние пакеты, отвечавшие за интеграцию WebApi c IIS.
Uninstall-Package Microsoft.AspNet.WebApi
Uninstall-Package Microsoft.AspNet.WebApi.WebHost
Заключение
В данной публикации мы узнали, как реализовать модульную структуру ASP.Net приложения с регистрацией компонентов в качестве OWIN Middleware, зарегистрировали единый Dependency Injection контейнер для ASP.Net MVC и WebApi и реализовали с его помощью модуля безопасности приложения.
Полный код приложения доступен по ссылке на GitHub.
Автор: Agaspher20