Реализация клиент-серверного приложения с веб-интерфейсом с помощью OWIN

в 5:58, , рубрики: .net, ASP, ASP.NET, Katana, OWIN

Введение

На Хабре много раз затрагивалась тема 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. Он не подходит нам по нескольким причинам, самые очевидные из них:

  1. Использование ServiceLocator не позволяет нам контролировать зависимости внутри класса.
  2. Появление второго 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

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js