Рассмотрим первый вариант создания модулей OWIN – в виде класса.
Соглашение для создания класса-модуля
В Katana нет интерфейса, который были бы должны реализовывать модули. Вместо него используется соглашение, что класс модуля должен иметь:
- произвольное имя;
- конструктор со следующими параметрами:
- обязательный AppFunc, в который передается следующий обработчик в цепочке конвейера;
- опциональные аргументы любого типа в любом количестве;
- метод Task Invoke(IDictionary<string, object> 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