...

пятница, 7 августа 2015 г.

Готовим ASP.NET5, выпуск №2 – повторим азы для самых начинающих

Друзья, перед вами второй выпуск колонки про ASP,NET5, в которой мы знакомимся с разными интересными вещами из мира веб-разработки на новой версии открытой платформы ASP.NET5.

В прошлый раз мы говорили про новые подходы в работе со статическим контентом на платформе. В комментариях возникло предложение поговорить в следующих выпусках про азы для веб-разработчиков, которые только начинают пользоваться ASP.NET и погружаться в тему. Мы прислушались к вам и предлагаем в этом выпуске материал от Андрея Веселова ( StealthDogg) – эксперта веб-разработки, автора множества статей по теме ASP.NET и Microsoft MVP.

Встречайте введение в азы ASP.NET5 – контроллеры, представления и модели.

Примечание. Данная статья актуальна для финальной версии Visual Studio 2015 с ASP.NET5 Beta5. Для обновления на новые версии ASP.NET воспользуйтесь примечаниями к релизу.


Работа с контроллерами


Давайте посмотрим на работу контроллера и действий шаблона MVC на примере небольшого веб-приложения.

aspnetcolumngithubСовет! Вы можете попробовать все самостоятельно или загрузив исходный код из GitHub http://ift.tt/1K6IspM.


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


Для этого создадим проект ControllerDemo (Рис.1).


Рис.1 – Создание проекта веб-приложения ASP,NET5

В настройках проекта выберем шаблон Empty из раздела ASP.NET 5 Templates (Рис. 2).


Рис.2 – Выбор шаблона веб-приложения

После создания проекта в Solution Explorer будут отражены следующие элементы (Рис. 3).


Рис.3. – Структура шаблона

Теперь необходимо подключить ASP.NET MVC 6 в проект.

1. В файле project.json укажем зависимость от библиотеки Microsoft.AspNet.Mvc.

"dependencies": {
    "Microsoft.AspNet.Server.IIS": "1.0.0-beta5",
    "Microsoft.AspNet.Server.WebListener": "1.0.0-beta5",
    "Microsoft.AspNet.Mvc": "6.0.0-beta5"
},

2. Затем необходимо включить ASP.NET MVC 6 в конвейер Owin для обработки запросов и настроить маршрутизацию. Для этого в файле Startup.cs добавим следующие строки:
public class Startup
{
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller}/{action}/{id?}",
                    defaults: new { controller = "Home", action = "Index" });
            });
        }
}

Проект подготовлен и можно приступать к созданию контроллеров и действий.

Контроллеры и действия


Как правило, контроллеры размещают в специальной папке Controllers. ASP.NET MVC 6 позволяет использовать и другие папки, т.к. ищет их по базовому классу. Однако в данном примере остановимся на классическом варианте расположения файлов. С помощью контекстного меню создадим папку Controllers ("Add > New Folder") и в нее новый класс HomeController ("Add > New item… " и выберем Class).

Примечание. Имя класса контроллера всегда состоит из имени контроллера (в данном случае "Home") и окончания "Controller".

Совет. В дальнейшем для создания контроллеров можно использовать готовый шаблон MVC Controller Class. Но сейчас, в целях изучения, создадим обычный пустой класс.


Базовым классом для всех контроллеров веб-приложения является Controller. Поэтому унаследуем созданный класс от него:
public class HomeController : Controller
{
}

Класс Controller является не только маркером контроллеров, но предоставляет основную функциональность для работы с полученным запросом и формирования ответа. В данный момент стоит отменить такие его свойства, как:
  • ViewBag и ViewData — предназначены для передачи произвольных данных в представления;
  • Request — содержит данные исходного запроса;
  • Response — позволяет создать ответ для клиента.

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

За непосредственную обработку запроса в контроллере отвечает действие, к котором можно выделить следующие части:

  1. Указание конкретного типа запроса, используя атрибуты [HttpGet], [HttpPut], [HttpPost], [HttpPatch], [HttpDelete]. Так же можно указать несколько типов, используя [HttpMethod]. При этом запросы остальных типов будут игнорироваться. Если атрибуты отсутствуют, то действие будет вызвано для любого запроса.
  2. Результат действия — его тип это реализация IActionResult. Все действия должны вернуть экземпляр класса, реализующий указанный интерфейс. По сути, он отвечает за формирование ответа, который будет отправлен клиенту. Это может быть HTML страница, файл, переадресация или код ошибки.
    Базовый класс Controller содержит методы, подходящие в большинстве стандартных случаев:
    1. View() — создание ответа (html-страницы) с использованием представления;
    2. Content() — предназначен для передачи произвольной текстовой строки клиенту, например, это может быть сгенерированный CSS или JavaScript;
    3. File() — позволяет вернуть клиенту файл;
    4. Json() — преобразует объект к формату JSON и возвращает его клиенту;
    5. Redirect(), RedirectPermanent(), RedirectToAction(), RedirectToRoute() — переадресуют клиента по новому адресу.

  3. Название действия, по которому будет происходить его вызов. По умолчанию считается, что строка запроса соответствует формату: http://ift.tt/1K6IspP;
  4. Метод действия может иметь параметры. Значения для них ASP.NET MVC постарается взять из строки запроса. Сопоставление параметров действия и запроса осуществляется по именам.
  5. И наконец, тело метода, в котором подготавливаются необходимые данные и возвращается результат.

Настало время добавить первое действие — Index:
public class HomeController : Controller
{
    // GET: /Home/
    public IActionResult Index()
    {
            return this.Content("Hello ASP.NET MVC 6.");
    }
}

Если теперь запустить веб-приложение, то заданная строка "Hello ASP.NET MVC 6." будет отображена в браузере.

Давайте разберем что именно произошло:

  • Браузер отправил на сайт запрос вида http://localhost:[port]/
    * Примечание: Порт для запуска в режиме отладки автоматически выбирается при создании проекта из свободных.
  • Поскольку ни контроллер, ни действие явно не указаны, то маршрутизация ASP.NET MVC использует варианты заданные по умолчанию: Home и Index.
  • Был создан экземпляр класса HomeController.
  • У него было найдено и выполнено действие Index.
  • Метод Index() вызвал метод Content() для формирования ответа, состоящего из указанной строки.
  • Механизм ASP.NET MVC получил ответ и переслал его браузеру клиента.
  • Браузер отобразил полученную в качестве ответа строку.

Если запрашиваемый контроллер или действие не будут найдены, то будет отправлен ответ "404. Страница не найдена".

Немного усложним и создадим конструктор класса и еще одно действие:

    public class HomeController : Controller
    {
        private readonly string _time;

        public HomeController()
        {
            this._time = DateTime.Now.ToString("G");
        }

        // GET: /Home/
        public IActionResult Index()
        {
            return this.Content("Hello ASP.NET MVC 6");
        }

        // GET: /Home/Echo/
        public IActionResult Echo(string message)
        {
            return this.Content($"{this._time} > {message}");
        }
    }

Теперь в качестве ответа будет возвращаться время создания контроллера и переданная в запросе строка. Это позволит убедиться, что новый экземпляр HomeController создается при каждом запросе.

Для вызова созданного действия необходимо обратиться по пути, включающий имена контроллера и самого действия. Запустим веб-приложение и введем в браузере следующий адрес:

http://localhost:[port]/Home/Echo?message=Hello

Асинхронность — это просто


Рассмотрим, как легко ASP.NET MVC 6 позволяет создавать асинхронные методы. Переделаем действие Echo. Для симуляции вызова некого асинхронного метода воспользуемся Task.Delay():
public async Task<IActionResult> Echo(string message)
{
      await Task.Delay(100);
      return this.Content($"{this._time} > {message}");
}

Потребовалось только заменить тип результата с IActionResult на Task и добавить модификатор async. Такое действие может использовать вызовы асинхронных методов, например, для обращения к различным источникам данных или сервисам.

Немного практики


И в завершении, два небольших практических задания:
  1. Напишите действие, так же выводящее значение параметров, но которые передаются запросе только типа POST.

    Подсказка. Для формирования запроса можно использовать обычную HTML страницу с формой. Её файл разместите в уже существующей папке wwwroot, которая предназначена для любого статического содержимого сайта (html страницы, файлы и т.д.)

  2. Создайте действие, которое отвечает только на Get запрос и:
    • возвращает файл;
    • возвращает объект в формате Json (для простоты можно использовать анонимный класс);
    • переадресует на другую страницу.

И не спешите заглядывать в приложенный исходный код, где есть все решения. Постарайтесь сделать это самостоятельно.

Работа с представлениями


Перейдем к рассмотрению следующей составляющей шаблона MVC — представлений.

aspnetcolumngithubСовет! Вы можете попробовать все самостоятельно или загрузив исходный код из GitHub http://ift.tt/1gOFfo7.


Создаем веб-приложение с представлением


Для этого создадим пустое ASP.NET MVC веб-приложение ViewDemo, аналогично тому как это было сделано в предыдущей части. Только в этот раз контроллер HomeController будет выглядеть следующим образом:
    public class HomeController : Controller
    {
        // GET: /Home/
        public IActionResult Index()
        {
            return this.View();
        }
    }

Метод View() создает результат действия, используя представление. Попробуем запустить проект и сразу получим сообщение об ошибке, т.к. в проекте еще нет представления для действия Index (Рис. 4).


Рис.4 – Сообщение об ошибке при отсутствующем представлении

Если внимательно прочитать текст сообщения, то можно заметить, что для представлений существует четко определенное место в структуре проекта. Это папка Views. В ней размещаются вложенные папки с именами соответствующими именам контроллеров. В данном примере это будет Home. И уже в ней хранятся файлы представлений, имена файлов которых, в общем случае, совпадают с названиями действий, для которых они предназначены.

Кроме того, есть специальная папка Views/Shared. В ней размещаются представления доступные для любых действий в проекте.

Создадим указанные выше папки Views и Home. После этого, в последней, с помощью пунктов контекстного меню "Add > New Item …", добавим представление (MVC View Page) с именем Index.cshtml. Расширение cshtml показывает что будет использоваться язык разметки Razor. В результате проект будет выглядеть следующим образом (Рис. 5):


Рис.5 – Структура проекта с новым представлением

Добавим в Index.cshtml следующий код:

<h1>View Demo</h1>

Запустим веб-приложение. При этом произойдет следующее:
  1. Будет вызвано действие Index контроллера Home (как это было описано в предыдущей статье).
  2. В действии Index, метод View() будет использовать представление View/Home/Index.cshtml для создания результата (экземпляра класса реализующего IActionResult).
  3. Результат действия сформирует HTML код в ответ клиенту (в методе ExecuteResultAsync интерфейса IActionResult).
  4. Созданная страница будет отправлена клиенту и отображена его браузером.

Метод View() ищет представление, используя следующие пути:
  1. Сначала View/<Имя контроллера>/<имя действия>.cshtml
  2. Затем View/Shared/<имя действия>.cshtml

Если оба поиска ничего не принесли, то выводится сообщение об ошибке.

Существует перегрузка метода, позволяющая явно указывать имя представления. Например, вызов

this.View("MyView") будет искать файл View/<Имя контроллера>/MyView.cshtml или View/Shared/MyView.cshtml. Это может быть полезно, например, если действие должно давать различный результат в зависимости от какого-то условия.

Кроме того, существует вариант View(), позволяющий передавать данные в представление. Это будет подробнее рассмотрено далее. А сейчас немного разберем возможности Razor.

Язык разметки Razor


Базовые конструкции

Razor допускает следующие конструкции:


В них C# код может быть совмещен с HTML кодом, например:
@for(int i=0; i<10;i++) {
    <p>@i</p>
}

Необходимо отметить, что представление по сути является классом, реализующим IRazorPage, со своими свойствами и методами. Например, доступны ViewBag и ViewData, которые содержат значения в которые были добавлены еще в действии.

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

  • @Нtml.Raw() — добавляет в HTML код текстовое значение без экранирования.
  • @Нtml.CheckBoxFor(), @Нtml.TextBoxFor() и прочие методы для генерации кода различных HTML элементов.

Еще одно вспомогательное свойство — Url. Оно содержит методы для работы с путями в веб-приложении. Наверное, самый востребованный из них это @ Url.Action( … ), который выводит путь до указанного действия указанного контроллера, например: Home page.

Необходимо так же отметить специальный файл Views/_ViewStart.cshtml. Его наличие не обязательно, но если он присутствует в проекте, то его содержимое выполняется перед каждым представлением. С его помощью можно задать общий для всех представлений шаблон страниц.

Шаблоны страниц


Как правило, веб-приложение содержит десятки страниц. Практически каждой из них будет соответствовать свое представление, а значит и свой файл с HTML кодом. При этом многие страницы имеют одинаковый шаблон и различаются лишь частью с основным содержимым (контентом).

Чтобы не вставлять копию HTML кода страницы в каждый файл представления, можно воспользоваться разметкой (layout). Для этого необходимо:

  1. Создать файл с общей разметкой в View/Shared
  2. В _ViewStart.cshtml или самом действии указывается файл разметки с помощью свойства Layout.

    Подсказка: Если шаблон указан и в файле и в представлении, то будет использоваться тот, что задан в представлении. Таким образом можно задать шаблон по умолчанию в _ViewStart.cshtml, а при необходимости переопределять его в представлении.

  3. Место, где код представления будет вставлен в шаблон, отмечается вызовом @RenderBody().

Итак, в папке View/Shared создадим файл с шаблоном HTML разметки _Layout.cshtml (имя может быть произвольным):
<html>
<head>
    <meta charset="utf-8" />
    <title>@this.ViewBag.Title</title>
</head>
<body>
    <nav>Menu</nav>

    <section id="mainContent">
        @RenderBody()
    </section>

    <footer>Footer</footer>
</body>
</html>

Так же изменим код представления:
@{
    this.ViewBag.Title = "Home page";
    this.Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>View Demo</h1>

Как хорошо видно, данный код:
  • Создает у ViewBag свойство Title со значением "Home page".
  • Устанавливает Layout указывает на используемый шаблон HTML разметки.

Теперь при запуске веб-приложения будет выведена страница с использованием указанного шаблона. В ее заголовок будет вставлена строка "Home page".

Секции


Существует возможность для вставки кода из представления в шаблон страницы вне кода блока, создаваемого @RenderBody(). Это секции, которые работают следующим образом:
  1. В коде шаблона указываются места для вставки секций с помощью @RenderSection(name, required) или @RenderSectionAsync(name, isRequired). Где name определяет уникальное имя секции, а required — является ли она обязательной.
  2. Представления определяют содержимое для секций с помощью конструкции @section name { … }
  3. При отсутствии кода для обязательной секции, будет выведено сообщение об ошибке.

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

Перейдем к примеру. Определим необязательную секцию в шаблоне страницы

<html>
<head>
    <meta charset="utf-8" />
    <title>@this.ViewBag.Title</title>

</head>
<body>
    <nav>Menu</nav>

    <section id="mainContent">
        @RenderBody()
    </section>

    <footer>Footer</footer>

    @RenderSection("scripts", required: false)
</body>
</html>

Теперь зададим код для данной секции в представлении. Для наглядности вызовем alert():
@{
    this.ViewBag.Title = "Home page";
}
<h1>View Demo</h1>

@section scripts {
    <script>
        alert("Demo");
    </script> 
}

Запустим проект и убедимся JavaScript был добавлен на страницу и выполнен.

Частичные представления


Еще одна удобная возможность Razor — создание частичных представлений. Это может использоваться как для хранения повторяющихся элементов страниц (всплывающих диалогов, форм и т.д.), так и для логического разделения большой страницы на части.

Использовать частичные представления очень просто:

  1. Создать частичное представление как обычное
  2. Указать в нем Layout = null, чтобы отменить использование шаблона страницы.
  3. Для вставки частичного представления использовать Html.Partial() или Html.PartialAsync().

Как всегда, перейдем к примеру. В новой папке Views/Shared/Forms создадим файл частичного представления_ContactForm.cshtml со следующим содержимым:
<form>
    Message: <input type="text" /><button type="submit">Send</button>
</form>

Выведем его в представлении Index:
@{
    this.ViewBag.Title = "Home page";
}
<h1>View Demo</h1>

@Html.Partial("Forms/_ContactForm")

@section scripts {
    <script>
        alert("Demo");
    </script> 
}

Обратите внимание как задан путь до частичного представления. Его поиск идет по общим правилам, и в данном случае путь указан относительно Shared. Файл с ним можно разместить в любой папке внутри View, он при этом надо будет указывать в параметре метода уже полный путь относительно корня сайта. Например, Html.Partial("~/Views/Shared/Forms/_ContactForm")

Остаётся только запустить проект и убедиться в результате.

Компоненты представлений (View Components)


Говоря о представлениях, необходимо отменить такое нововведение ASP.NET MVC 6 как компоненты. По сути это развитие идеи частичных представлений путем добавления к ним собственных контроллеров. Однако, прежде необходимо разобраться использованием моделей. И тогда можно будет вернуться к компонентам в следующих частях.

Работаем с моделями


Созданные в предыдущих частях приложения уже содержат контроллер и представление. Остается рассмотреть как использовать еще один компонент шаблона MVC — модели.

aspnetcolumngithubСовет! Вы можете попробовать все самостоятельно или загрузив исходный код из GitHub http://ift.tt/1gOFfEp.


Создаем модель


Воспользуемся созданным в прошлой части приложением ViewDemo и добавим в него работу с моделью.

Для размещения моделей в проекте создадим папку Models. Данное имя является рекомендованным, однако не обязательным. Их код может быть размещен в папках с другими именами. Кроме того, можно использовать вложенные папки для логической группировки классов.

Модель — это простой объект (POCO). Нет необходимости наследовать его от какого-либо класса или реализовывать какой-либо интерфейс. Создадим модель, которая, для примера, будет содержать имя пользователя и текущее время. Для этого добавим в папку Models файл IndexPageModel.cs с следующим кодом:

using System;

namespace ViewDemo.Models
{
    public class IndexPageModel
    {
        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string FullName => $"{this.FirstName} {this.LastName}";

        public DateTime CurrentTime { get; set; }
    }
}

Модель готова. Обратите внимание, что нет ограничений на класс модели. Например, в данном случае присутствует вычисляемое свойство FullName. Однако не стоит увлекаться и переносить логику приложения в модели. Лучше оставлять их максимально простыми, чтобы облегчить дальнейшее сопровождение приложения.

Дорабатываем контроллер


Итак, модель уже есть. Необходимо создать ее экземпляр и записать в него какие-нибудь значения. Для простоты воспользуемся константами. Перейдём к контроллеру Home и модифицируем действие Index следующим образом:
public IActionResult Index()
{
    var model = new IndexPageModel()
    {
        FirstName = "John",
        LastName = "Doe",
        CurrentTime = DateTime.Now
    };

    return View(model);
}

Для передачи модели в представление достаточно подставить ее экземпляр в качестве параметра метода View(). В данном примере используются константы для заполнения модели. Разумеется в реальном проекте будет обращение к бизнес-логике приложения и полученный результат будет подставлен в модель.

Модифицируем представление


Откроем представление Index.cshtml. В первую очередь необходимо указать, что оно будет использовать определенный класс модели. Для этого в первой строке файла напишем следующее:

@model ViewDemo.Models.IndexPageModel

Обратите внимание, что имя класса указано вместе с его пространством имен.

Теперь можно использовать свойство this.Model, в котором будет содержаться переданная модель, указанного типа. Выведем данные на страницу (полный код представления Index.cshtml):

@model ViewDemo.Models.IndexPageModel
@{
    this.ViewBag.Title = "Home page";
}

<h1>Model Demo</h1>
<p><strong>Имя и фамилия:</strong> @this.Model.FullName</p>
<p><strong>Текущее дата и время:</strong> @this.Model.CurrentTime.ToString()</p>

Запустим приложение. При каждом обновлении страницы будет выводиться указанные в контроллере имя, фамилия, а так же текущие дата и время.

Итак, создавать и использовать модели не сложнее чем обычные классы. Всю работу по передачи ее экземпляра в представление берет на себя ASP.NET MVC. Кроме того, в представлении свойство this.Model становится того же типа, что и модель. Это позволяет использовать IntelliSense при создании и редактировании представлений.

Свежие новости


Как вы уже знаете выпущена Visual Studio 2015 с ASP.NET5 Beta5. Подробности о том, что именно включено в релиз Visual Studio можно почитать в этом блоге.

Выпущено обновление ASP.NET5 Beta6 с множеством изменений, улучшений и исправлений ошибок. Подробности обновления можно найти в этом блоге. Вы можете загрузить обновление по этой ссылке.

Опубликованы планы по выпуску релизов платформы в течение ближайших месяцев до выпуска финальной версии ASP.NET5. Согласно им нас ждут версии Beta7 и Beta8, после чего в ноябре мы получим первую версию, готовую к продакшну (RC1), финальная же версия выйдет в первом квартале 2016 года. Подробности каждой версии можно найти по ссылке.

Опубликованы доклады конференции DevCon 2015, в том числе по веб-разработке и теме ASP.NET.

Полезные ссылки


Самая свежая документация по ASP.NET5 расположена по адресу http://ift.tt/1GVSSJQ.

Приглашаем вас подключаться к живым трансляциям периодического шоу ASP.NET 5 Community Standup, где разработчики из команды Microsoft делятся последними новостями о платформе. Записи доступны по этой ссылке.

Познакомьтесь с демонстрационным проектом PiDnx от Дэмиана Эдвардса, который показывает как запускать ASP.NET5 на Raspberry Pi 2 на базе Windows 10 IoT Core.

Изучите основы ASP.NET5 с новым бесплатным курсом виртуальной академии Microsoft.

Авторам


Друзья, если вам интересно поддержать колонку своим собственным материалом, то прошу написать мне на vyunev@microsoft.com для того чтобы обсудить все детали. Мы разыскиваем авторов, которые могут интересно рассказать про ASP.NET и другие темы.

Об авторе


Веселов Андрей
Senior Developer, CodeFirst, Ireland

Сертифицированный разработчик с 15-летним стажем, Microsoft MVP (ASP.Net/IIS). Последние 9 лет занимается веб-разработкой с использованием технологий Microsoft. Сфера основных профессиональных интересов: ASP.NET и Azure. В настоящее время работает в компании CodeFirst в должности Senior Developer. Ведет профессиональный блог.

Twiter: http://twitter.com/AndreyVeselov
Facebook: http://ift.tt/1fj6C8t
Блог: andrey.moveax.ru

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Комментариев нет:

Отправить комментарий