OWIN и Katana. Часть 3.1 – Класс как модуль OWIN

Рассмотрим первый вариант создания модулей OWIN – в виде класса.

Соглашение для создания класса-модуля

В Katana нет интерфейса, который были бы должны реализовывать модули. Вместо него используется соглашение, что класс модуля должен иметь:

  • произвольное имя;
  • конструктор со следующими параметрами:
    • обязательный AppFunc, в который передается следующий обработчик в цепочке конвейера;
    • опциональные аргументы любого типа в любом количестве;
  • метод Task Invoke(IDictionary environment), в котором должна содержаться вся логика модуля. В завершении работы необходимо:
    • или вызвать следующий обработчик;
    • или вернуть результат в виде экземпляра Task, тем самым завершив обработку запроса.

Также необходимо зарегистрировать класс в качестве модуля в методе Configuration() класса Startup. При запуске веб-приложения, Katana создаст его экземпляр, который и будет использоваться для обработки всех поступающих запросов.

Пример реализации модуля

Напишем простой модуль, который будет выводить отладочную информацию и не прерывать обработку запроса. Разумеется, основной целью будет показать создание класса, передачу в его конструктор параметров и обработку исключительных ситуаций.

Код будет выглядеть следующим образом (файл LoggerModule.cs):

namespace OwinDemo
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Threading.Tasks;

    using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;

    public class LoggerModule
    {
        private readonly AppFunc _next;
        private readonly string _prefix;

        public LoggerModule(AppFunc next, string prefix)
        {
            if (next == null)
                throw new ArgumentNullException("next");

            if (string.IsNullOrEmpty(prefix))
                throw new ArgumentException("prefix can't be null or empty");

            this._next = next;
            this._prefix = prefix;
        }

        public Task Invoke(IDictionary<string, object> env)
        {
            try {
                Debug.WriteLine("{0} Request: {1}", this._prefix, env["owin.RequestPath"]);
            }
            catch (Exception ex) {
                var tcs = new TaskCompletionSource<object>();
                tcs.SetException(ex);
                return tcs.Task;
            }

            return this._next(env);
        }
    }
}

Рассмотрим данный код подробнее:

  • Как и определено в соглашении, конструктор класса получает:
    • делегат next, представляющий собой следующий обработчик в цепочке;
    • дополнительный параметр prefix, который будет использоваться в отладочной строке.
  • Метод Invoke() содержит логику модуля. Ключи словаря env описаны в спецификации OWIN.
  • Блок try-catch здесь представлен для демонстрации обработки исключений. В случае их возникновения, они перехватываются и возвращаются в экземпляре класса Task. Это остановит обработку запросов, а клиент получит соответствующее сообщение. Для проверки работоспособности блока можно, например, внести ошибку в имя ключа owin.RequestPath.

Обратите внимание, что для создания модуля не потребовалось никаких дополнительных библиотек. Достаточно просто следовать заявленному соглашению.

Остается только зарегистрировать класс в цепочке обработки сообщений:

namespace OwinDemo
{
    using Owin;

    public class Startup
    {
        public void Configuration(IAppBuilder appBuilder)
        {
            appBuilder.Use(typeof(LoggerModule), "Log Module >");
        }
    }
}

При этом обязательно передать значения параметров, которые требует конструктор класса модуля. В данном случае это строка “Log Module >” для prefix.

Можно запустить проект и убедиться в его работоспособности: в окне Output будут выводиться сообщения, начинающиеся со значения prefix и содержащие запрошенный путь. При этом, поскольку других модулей нет, сам запрос закончится выводом ошибки 404 (т.к. сработает модуль Katana по умолчанию – NotFound).

В следующей части посмотрим на еще один способ создания модулей.


Исходный код проекта (C#, Visual Studio 2012): OwinDemo-part3-1.zip