OWIN и Katana. Часть 3 – Принципы создания модулей

Давайте рассмотрим основные принципы создания собственных модулей OWIN.

Создаем проект

В качестве сервера вновь будет использоваться IIS. Поэтому создадим проект OwinDemo на базе шаблона пустого ASP.NET веб-приложения (ASP.NET Empty Web Application).

Для того, чтобы была возможность запускать создаваемые модули установим OWIN для IIS:

PM> Install-Package Microsoft.Owin.Host.SystemWeb
Attempting to resolve dependency 'Owin (≥ 1.0)'.

Подождите… Устанавливается библиотека Owin? Но если OWIN это только спецификация, то откуда взялась dll и что в ней содержится? Сейчас разберемся.

Библиотека OWIN

Внутри сборки Owin.dll находится определение единственного интерфейса:

namespace Owin
{
    public interface IAppBuilder
    {
        IDictionary<string, object> Properties { get; }
        object Build(Type returnType);
        IAppBuilder New();
        IAppBuilder Use(object middleware, params object[] args);
    }
}

IAppBuilder предназначен для создания конфигурации конвейера обработки запросов. При запуске веб-приложения с помощью параметра middleware метода Use() указываются используемые модули. В общем случае, выглядеть это будет так:

appBuilder.Use(authModule);
appBuilder.Use(staticHtmlModule, arguments);
appBuilder.Use(userModule, arg1, arg2); 

Обратите внимание, что рассматриваемая сборка не содержит реализации IAppBuilder.

Точка конфигурирования конвейера

Реализация Katana базируется на соглашениях. Одно из них уже было упомянуто в предыдущей части: при запуске веб-приложения, Katana создает экземпляр класса Startup и вызывает метод Configuration(). В него передается экземпляр класса, реализующего IAppBuilder, что позволяет определить конфигурацию конвейера обработки запросов.

Создадим указанный класс в проекте OwinDemo (файл Startup.cs):

namespace OwinDemo
{
    using Owin;

    public class Startup
    {
        public void Configuration(IAppBuilder appBuilder)
        {
        }
    }
}

Правила создания конвейера

Конвейер обработки запросов является реализацией шаблона "Цепочка ответственностей".

Модули получают пришедший запрос по очереди, порядок которой определяется порядком их регистрации в методе Configuration(). При этом в им передаются:

  • делегат AppFunc, которому можно передать управление дальше по цепочке;
  • словарь IDictionary<string, object> с данными запроса, окружения сервера и объектами для формирования ответа.

После получения данных каждый обработчик может:

  • проигнорировать и передать управление дальше (пример: модуль WebAPI получил запрос на вывод страницы сайта);
  • обработать и передать управление дальше (пример: ведение журнала запросов);
  • обработать и завершить цепочку вызовов (пример: модуль WebAPI получил запрос к Контроллеру);
  • сообщить о критической ошибке (исключении), тем самым прервав цепочку вызовов аварийно.

Легко заметить, что нет никакой гарантии обработки запроса хотя бы одним модулем. Поэтому появляется необходимость в обработчике по умолчанию. Katana вполне логично в этой роли использует класс NotFound, который всегда устанавливает код ответа равным 404 (Page not found).

Разумеется, в методе Configuration() можно определить свой обработчик по умолчанию:

appBuilder.Properties["builder.DefaultApp"] = defaultModule;

где defaultModule – модуль (класс или функция), который будет получать все необработанные запросы.

Очень важен порядок в котором зарегистрированы, а значит и будут вызываться, модули. Например, расположим блок записи в журнал в конце конвейера. Тогда запрос до него может просто не дойти, поскольку выполняющийся перед ним код может завершить цепочку вызовов или выбросить исключение. Поэтому подобные обработчики необходимо размещать одними из первых. И наоборот, есть обработчики, которые должны выполняться последними. Пример: упомянутый выше NotFound.

В следующей части перейдем к непосредственному написанию модулей.

Добавить комментарий