Microsoft OWIN и конвейер обработки запросов ASP.NET. Часть третья, детальное описание конвейера OWIN для веб-приложения не использующего IIS и ASP.NET.

В данной статье я опишу конвейер обработки запросов веб-приложения, которое построено по спецификации OWIN  с использованием проекта Katana. При этом не используются IIS/ASP.NET. Спецификация OWIN была описана в данной статье, а пример реального приложения, построенного по этой спецификации, был показан в данной. Не использовать IIS/ASP.NET, именно в этом случае мы получаем всю мощь и гибкость, используя Katana. Но я при этом не говорю, что надо отказываться от IIS. Просто, у нас уже есть альтернативный вариант. А списывать ASP.NET и IIS со счетов ещё рано.  А про то, как будет выглядеть конвейер IIS/ASP.NET при использовании OWIN, я покажу в следующей статье. Предлагаю посмотреть и познакомиться с полным описанием конвейера обработки запросов IIS и ASP.NET, который был проиллюстрирован тут, если вы пока ещё этого не сделали. Чтобы можно было наглядно представить и сравнить, какие кординальные изменения произошли. Модернизация классического конвейера IIS 6, которая была сделана при переходе на IIS 7 и выше, так называемый "интегрированный режим" (как вы знаете он используется и в IIS 7.5, IIS 8, IIS 8.5), была большим шагом вперёд. Большим шагом впрерёд в сторону ещё большего использования управляемой среды и упраляемого кода (среда CLR, платформа .NET Framework) и уменьшения использования неуправляемого, что сильно облегчает жизнь и не может не радовать. Но вот полностью отказаться от использования "старого багажа" не так то просто. Одна из основных причин – поддержка и совместимость уже имеющихся приложений. Без этого никак. Ещё больший шаг впрерёд, или даже целых два это – OWIN, и проект Katana. Это новая инфраструктура, которая целиком и полностью опирается на управляемый код. Нативного кода тут нет, ну или почти нет. Весь конвейер обработки запросов строится при помощи управляемого кода .NET и он открыт. Ниже показана схема работы конвейера (ссылка на pdf файл высокого качества).



Всё начинается с того, что не используются никакие WAS и W3SVC, хотя эти службы имеют и выполняют свою положительную роль в составе IIS. А управляемая обёртка System.Net.HttpListener, которая напрямую работает с HTTP.SYS. Класс не новый, он появился ещё со времён .NET 2.0 и широко используется., в том числе и для написания собственнх HTTP-серверов или простых прослушивателей протокола. Но это не главное, главное то, что строится целая веб-инфраструктура при помощи управляемого кода (платформа .NET Framework) по спецификации OWIN. Примером такого сервера и служит класс OwinHttpListener из проекта Katana, использующий HttpListener из библиотеки FCL и представляет собой OWIN-совместимый сервер. Непосредственно он занимается приёмом и отправкой запросов. Здесь важную роль играет метод StartProcessingRequest(HttpListenerContext context). Дальше запрос передаётся на обработку модулям. Настройка модулей происходит не путём использования XML файла конфигурации, а при помощи кода конфигурации. Реальный пример, как я уже отметил есть тут. В отличие от конвейера IIS/ASP.NET запросы обрабатываются только модулями. Нет обработчиков HTTP-данных (HttpHandler) специально предназначенных для обработки результирующих данных. Модули OWIN Не имеют ничего общего с HTTP-модулями (HttpModule) IIS. Они не подписываются на события, тут нет такого понятия, а вызываются цепочкой: каждый модуль может вызвать следующий или завершить обработку запроса. На них не закладывается жёсткое ограничение в виде реализации интерфейса. Но всё же, есть некоторые соглашения, вез которых модуль не будет работать. В роли может выступать как класс, так и отдельный метод. Метод обычно используется если задачи модуля небольшие, например – трассировка. Если надо подключить целую платформу типа Web API или SignalR, то тут уже разумнее иметь класс. Ниже представлены примеры кода в случае метода
 using AppFunc = Func<IDictionary<string, object>, Task>;
 
 //. . . . . . . . . . . . . . . . .
 
 // Делегат в роли модуля.
 var module = new Func<AppFunc, AppFunc>(
     nextModule => enviroment =>
         {
             // Обработать запрос и отправить ответ
             {
                 // Работа по обработке запроса.
             }
 
             // или передать управление следующему модулю.
             return nextModule(enviroment);
         });

и класса.
 // Произвольный класс как модуль OWIN.
 public class ArbitraryClass
 {
     private readonly Func<IDictionary<string, object>, Task> nextModule;
 
     // Параметр nextModule обязательный, можно также передать любое количество 
     // других параметров.
     public ArbitraryClass(Func<IDictionary<string, object>, Task> nextModule, params object[] args)
     {
         Contract.Requires<ArgumentNullException>(nextModule != null);
 
         this.nextModule = nextModule;
     }
 
     // Обработать запрос. Имя – Invoke и сигнатура обязательны. 
     public Task Invoke(IDictionary<string, object> enviroment)
     {
         // Обработать запрос и отправить ответ
         {
             // Работа по обработке запроса.
         }
 
         // или передать управление следующему модулю.
         return this.nextModule(enviroment);
     }
 }
Как видно из вышеописанного, всё достаточно просто и открыто. Нет нативных модулей, отдельных обработчиков и прочего. То есть ковейер не так сложно устроен, как здесь. И это – здорово.