Первую часть можно прочитать здесь. Когда мы работаем над сайтом, неплохо иметь под рукой простой способ управлять разделом с документацией или блогом, просто перекидывая Markdown-файлы в нужную папку, которые затем используются на этих страницах в качестве контента, аккуратно вплетенного в айдентику бренда на нашем сайте, не так ли? В этой идее нет ничего нового — большинство CMS-систем и генераторов контента именно так и делают. Но это, как правило, специализированные инструменты, обособленные от нашего веб-приложения. Вот бы здорово было, если бы у нас был простой способ добавить поддержку Markdown на страницах нашего уже существующего веб-приложения? Давайте же взглянем на middleware-компонент ASP.NET Core, который упрощает настройку папок для Markdown-файлов, которые используются в качестве контента нашего веб-сайта. Большинство динамических сайтов содержат как минимум пару страниц с контентом, которые в основном статичны, а потому их легче создавать с помощью Markdown, а не “тегового супа” HTML. Вот что нам потребуется для работы со статическими Markdown-страницами: Страница-оболочка, реализующая пользовательский интерфейс нашего сайта вокруг Markdown-контента Область контента, в которую будет рендериться результирующий HTML Файл с Markdown-текстом, который мы собираемся рендерить  (Опционально) YAML-парсинг для тайтла и хедеров (Опционально) парсинг тайтла на основе хедера или имени файла. Конфигурация папок для шаблона и параметров  Весь наш замысел предельно прост: для каждой сконфигурированной иерархии папок мы можем указать шаблон Razor, представляющий из себя страницу для размещения Markdown-контента. Эта страница достаточно примитивна — на практике, для большинства сайтов на этой странице не будет ничего, кроме встроенного Markdown-контента и ссылки на страницу _layout.cshtml нашего сайта. В этот шаблон Markdown-страницы передается модель, содержащая отрендеренный Markdown-текст, который мы затем можем встроить в шаблон в нужном месте. Мы можем создать шаблон любым удобным для нас способом: либо в виде отдельной HTML-страницы, либо просто ссылаясь на мастер-страницу, встроив свойство @Model.RenderedMarkdown в страницу. Нам также нужно создать конфигурацию для подключения middleware, которое указывает, с какими папками (или root) работать, и хотим ли мы обрабатывать URL без расширений в дополнение к обработке расширения .md. Затем мы можем просто добавить файлы с расширением .md в папку wwwroot нашего сайта и сконфигурировать пути, точно так же как мы делали бы это со статическими html-файлами. Если вы хотите пощупать middleware, которое я описываю в этой статье, вы можете установить его отсюда: Мы можем начать использовать middleware сразу после установки соответствующего NuGet-пакета. Это делается путем подключения конфигурации middleware: Запустим AddMarkdown() для конфигурации обработки страниц. Запустим UseMarkdown() для подключения middleware. Создадим шаблон представления Markdown (по умолчанию: ~/Views/__MarkdownPageTemplate.cshtml). Создадим .md-файлы для нашего контента. Следующий код сконфигурирует папку /posts/ для работы с Markdown-документами в вашем приложении:  В рамках этой базовой конфигурации мы указываем путь к папке, в которой будут размещены Markdown-документы. Мы можем сконфигурировать несколько папок, включая даже root (/). Мы также можем указать Razor-шаблон, который будет использоваться в качестве контейнера для markdown-контента. Приведенный выше код является простым примером, не включающим некоторые дополнительные параметры, которые мы можем использовать в нашей конфигурации. Мы можем задать в конфигурации несколько путей, указать, как должны обрабатываться файлы, настроить предварительную обработку кода, предоставив обработчик, который будет запускаться перед рендерингом Markdown-документов, и мы также можем изменить конфигурацию нашего Markdown-конвейера MarkDig. Затем нам нужно подключить это middleware к middleware-конвейеру с помощью app.UseMarkdown() в методе Configure() Startup: Markdown-middleware требует MVC для рендеринга Markdown-контента, чтобы переписать путь запроса и поместить текущий путь запроса в кастомный контроллер, предоставляемый этим компонентом. Затем контроллер рендерит Razor-шаблон, который мы указали в конфигурации AddMarkdown(), показанной ранее. Наиболее примитивный шаблонов выглядит так: В модели содержится два интересующих нас значения: RenderedMarkdown и Title, которые можно встроить в шаблон. Title выводится из YAML-хедера title, если таковой имеется, или из первого заголовочного тега # в документе. Если ничего из это нет, то тайтл остается не установленным. Если вы хотите передать другие значения в свое представление, конфигурация также поддерживает хук для предварительной обработки, который подключается к Markdown-запросу: Модель также имеет свойства относительного и физического пути и обеспечивает доступ к конфигурации активной папки, поэтому ваш код предварительной обработки может выполнять сложную логику в зависимости от того, какой файл активирован, и может передавать эти данные в ваше представление через свойства контроллера ViewBag или ViewData. На этом этапе мы уже можем начинать добавлять Markdown-файлы в нашу папку /wwwroot/posts/. Мы создадим иерархию папок, которая соответствует типичной структуре поста в блоге, где будет дата и имя сообщения, как показано на рисунке 2, и добавим в нее один пост из моего блога вместе с парой изображений. Это все, что нужно, и теперь мы можем получить доступ к этой странице по следующему URL: Или версия без расширения: Конфигурация по умолчанию работает как с расширением .md, так и без него. Если расширение не указано, middleware просматривает каждый запрос без расширения, пытается добавить .md, проверяет, существует ли файл, и рендерит его. Результат показан на рисунке 3. Markdown-контент корректно отображается в виде HTML, и, как и ожидалось, мы получили соответствующий пользовательский интерфейс шаблонного ASP.NET Core сайта. Мы не вносили никаких изменений в стандартный шаблон New Project, но даже в этом случае этот markdown-текст прекрасно отображается на странице, согласуясь с контекстом пользовательского интерфейса сайта. Есть одна вещь, которую определенно стоило бы улучшить: наш пример кода отображается скучным монохромным текстом без подсветки синтаксиса. Чтобы исправить это, мы теперь можем изменить наш Razor-шаблон, добавив немного JavaScript, чтобы реализовать подсветку синтаксиса, и добавить немного Bootstrap в наш стиль, чтобы он выглядел лучше. В листинге 2 показан наш обновленный шаблон: На рисунке 4 показано, как сейчас выглядит отрендеренная страница: Для форматирования и подсветки кода мы использовали JavaScript-библиотеку highlightJS. Чтобы использовать эту библиотеку от нас требуется ссылка на нее, тема (VS2015, тема, похожая на темную тему Visual Studio Code) и небольшой скрипт, который находит pre>code элементы на странице и применяет подсветку синтаксиса. Отрендеренный Markdown также включает атрибут языка, который HighlightJS понимает и использует для выбора соответствующего поддерживаемого языка. HighlightJS можно кастомизировать, и мы также можем создавать собственные пакеты, включающие нужные нам языки. Я создал свой собственный пакет, включающий наиболее распространенные языки .NET, Windows и Web, который вы можете найти в GitHub-репозитории библиотеки. На данном этапе я могу просто поместить файлы Markdown в свою папку wwwroot/posts/, и они будут отображаться как вполне самодостаточные страницы. Чудеса! Так как же все это работает? Как вы, наверное, и сами догадываетесь, процесс создания middleware не очень сложен, но он включает довольно много подвижных частей, что является привычным делом при создании middleware. Вот что нам потребуется: Реализация middleware для обработки маршрутизации запросов. Middleware-расширения, которые настраивают и подключают middleware. Контроллер MVC, который обрабатывает запрос рендеринга Razor-шаблон, используемый для визуализации отрендеренного HTML Middleware — это класс ASP.NET Core, который реализует метод InvokeAsync (HttpContext context). С другой стороны, Middleware также может быть реализовано непосредственно в классе Startup или как часть Middleware-расширения через app.Use() (для завершения middleware  app.Run()). Идея, стоящая за middleware довольно проста: вы реализуете обработчик middleware, который получает объект context и вызывает next(context), который передает контекст следующему middleware, определенному в цепочке, которое вызывает следующее middleware, и так далее, пока все middleware-компоненты не будут вызваны. Цепочка разворачивается, и каждый из этих вызовов возвращает свой статус обратно вверх по цепочке. По сути, обработчики middleware могут перехватывать входящие и исходящие запросы с помощью одной и той же реализации. Рисунок 5 иллюстрирует поток этого связанного взаимодействия middleware-компонентов. Обратите внимание, что порядок в конвейере играет важную роль значение. Таким образом, для правильной работы функция предварительной обработки, такая как аутентификация или CORS, должна выполняться до функции обслуживания страниц, такой как обслуживание MVC или StaticFile. Реализация специального middleware-компонента обычно подразумевает создание самого middleware-компонента, а также нескольких middleware-расширений, используемых для конфигурации и подключения middleware с помощью app.Add<Middleware>() и app.Use<middleware>(), которые являются шаблоном, используемым большинством встроенных middleware-компонентов ASP.NET Core. Основная задача нашего middleware для обработки Markdown-страниц — выяснить, запрашивает ли входящий запрос Markdown-документ, проверяя URL. Если запрос указывает на .md-файл Markdown, middleware фактически переписывает URL запроса и направляет его на кастомный хорошо известный эндпоинт контроллера, который предоставляется как часть библиотеки компонентов. Листинге 3 демонстрирует, как выглядит middleware. Очень важный элемент в этом middleware  — Context.Path.Value, который содержит текущий путь запроса. На основе этого пути компонент проверяет, указывает ли он либо на .md напрямую, либо на URL без расширения, к которому он добавляет .md, и проверяет наличие файла. Если путь указывает на Markdown-файл, middleware устанавливает флаг и сохраняет исходный путь и путь к файлу в нескольких элементах контекста. Что еще более важно, он перзаписывает текущий путь, чтобы указать на хорошо известный MarkdownPageProcessorController, который имеет фиксированный маршрут /markdownprocessor/markdownpage. Переписать путь запроса в middleware в ASP.NET Core так же просто, как изменить свойство HttpContext.Path. Это перенаправит запрос на мой кастомный контроллер, который затем может отображать содержимое физического файла с использованием сконфигурированного Razor-шаблона: Этот захардкоженный маршрут атрибутов будет найден, даже если он находится в отдельной библиотеке, получаемой через NuGet. Обратите внимание, что этот маршрут работает только в сочетании с middleware, поскольку он зависит от предустановленных значений Context.Items, которые были сохранены middleware ранее в запросе. В листинге 4 показан экшн-метод, отвечающий за обслуживание Markdown-файла. Код начинается с получения переменных Context, которые были установлены в middleware при пересылке запроса, проверки файла и, если он найден, чтения его с диска и рендеринга в HTML. Модель создается и строится с важным тайтлом и отрендеренным HTML, которые используются непосредственно представлением, наряду с некоторой информацией о файле и информацией о конфигурации, которые могут использоваться либо внутри представления, либо в средстве предварительной обработки. Когда все сказано и сделано, модель отправляется в представление для рендеринга. После того, как вы создали middleware-компонент, его еще нужно подключить и добавить в конвейер. ASP.NET Core предоставляет универсальные функции для добавления типизированного middleware, но эти функции нелегко обнаружить. Лучший способ — сделать то, что делают нативные middleware, то есть предоставить методы расширения, расширяющие IServiceCollection и IApplicationBuilder. В листинге 5 показан код расширения. Метод AddMarkdown() предоставляет конфигурацию службы, создавая дефолтный объект конфигурации, а затем используя опциональный Action<MarkdownConfiguration>(), который мы предоставляем для конфигурирования middleware. Это распространенный шаблон, который вызывает конфигурацию с задержкой при поступлении первого запроса. Последним действием метода является явное добавление конфигурации в контейнер внедрения зависимостей, чтобы ее можно было получить в middleware и контроллере посредством внедрения зависимостей. UseMarkdown() очень прост и просто делегирует builder.UseMiddleware<MarkdownPageProcessor>(). Единственная цель метода-оболочки — предоставить обнаруживаемый метод в Startup.Configure(), который согласуется с тем, как ведет себя встроенное middleware ASP.NET Core. Теперь у нас есть полностью реюзабельный механизм рендеринга Markdown-страниц, который можно легко подключить к любому приложению ASP.NET Core с помощью нескольких строк конфигурационного кода. С этого момента я могу просто поместить Markdown-файлы и связанные с ними ресурсы в свою папку wwwroot. И на этом все. Markdown вам в помощь! Приглашаем всех желающих на открытое занятие «Создание консольного калькулятора». На занятии сделаем с нуля консольный калькулятор, используя такие понятия как: ветвления, функции и циклы в C#. Регистрируйтесь по ссылке.