Реалізація клієнт-серверного додатка з веб-інтерфейсом за допомогою 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.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.