Razor pages что это
Перейти к содержимому

Razor pages что это

  • автор:

Добавляем функциональность Razor Pages в стандартный .NET

Razor Pages — новая фича, появившаяся в Core.Net 2.0. Razor Page — это страница, состоящая из стандартной разметки (View) и бэкенд класса. В каком то смысле напоминает Web Forms только без поддержки сохранения состояния. Преимущество такого решения очевидно — мы избавляемся от ненужной прослойки — модели страницы (модель данных в виде например Entity это само собой). Бэкенд страницы является и контроллером и моделью — классика ООП — инкапсуляция данных и методов работы с ними в одном объекте. В конце концов модель страницы — это просто класс, нет никаких причин почему этим классом не может быть контроллер.

Иными словами, Razor Pages — более вменяемое решение для веба чем MVC, теперь мы имеем дело с традиционным и логичным понятием «страница» а не с контролерами и моделями разбросанными по всему проекту. Но поскольку .NET будет развиваться по направлению Core.Net то вряд ли Razor Page появятся в стандартном фреймворке, несмотря на то что ближайшие годы большинство проектов будет оставаться на стандартном .NET. Тем не менее можно изобразить функциональность Razor Pages и на стандартном фреймворке.

Решение на самом деле выглядит достаточно тривиально — нужно добавить в контролер следующую конструкцию:

protected override void OnActionExecuting(ActionExecutingContext Context)

Метод OnActionExecuting -событие жизненного цикла который вызывается перед выполнением метода контролера (имеется ввиду обработчик запроса — Action).

UpdateModel выполняет непосредственно привязку параметров запроса к свойствам (properties) модели — в данном случае свойствам класса контроллера.

Дополнительное удобство — теперь нет необходимости вообще принимать явно параметры ни типа Model ни какие другие. Хотя ничего не мешает это делать, если параметр — простой id, который будет использовать чисто как локальная переменная, но привязка параметров как свойств контролера необходима например, если нужно обеспечить персистентность страницы, о чем пойдет речь дальше.

У нас есть обычная форма логина с двумя полями.

Приводить разметку нет смысла приведу только код контроллера

 public class AccountController : Controller < public string username< get; set; >public string userpass < get; set; >[HttpPost] public ActionResult OnLogin( ) < //некая функция проверки в БД checklogin(username,userpass); return View("Index",this); >protected override void OnActionExecuting(ActionExecutingContext Context) < UpdateModel(this); >> 

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

Разумеется, надо иметь ввиду что теперь возвращать как ActionResult тоже нужно контроллер ну и в шаблоне прописать имя класса контролера — типа @Model AccountController.

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

Вы кликаете по фильтру модель возвращается в представление и все хорошо, но когда кликните сортировку фильтр естественно сбросится. Пагинатор сбросит и сортировку и фильтр. В WebForms состояние страницы сохранялось автоматически, в MVC приходится применять разные громоздкие решения, например склеивать все параметры и гонять их по каждому запросу, то есть на ссылку сортировки нужно повесить все параметры которые пришли перед этим с фильтра.

Подобные затруднения являются одной из причин SPA и прочего яваскриптового безумия с перетаскиванием логики и данных в браузер с вытекающими из этого тормозами браузера (особенно мобильных девайсах), прыгающими и дергающимися за каждым движением мышки страницами и увеличением трудоемкости и стоимости работ — ведь бэкенд то все равно писать в том или ином виде, упаковывать распаковывать данные пересылаемые через ajax, плюс callbac hell, усложнения отладки и так далее, и все это без практической пользы для посетителей сайта которым нет дела как написана страница.

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

protected override void OnActionExecuted (ActionExecutedContext Context) < //сохраняем данные >

Восстанавливаются параметры из сессии в конструкторе контролера или перед вызовом UpdateModel(this). Когда придет запрос, например сортировки то новые параметры изменятся а остальное останется нетронутым и представление будет отображено в том же виде как было отправлено.

Такое решение имеет еще одно удобство — к примеру, пользователь отсортировал таблицу, и решил какой то элемент отредактировать, открывши для этого другую страницу. Естественно он хочет вернутся в то состояние списка которое оставил а так как состояние страницы у нас в сессии, то страница восстановится автоматически. Нет никакой необходимости, как часто делают, передавать весь «вареник» параметров в страницу редактирования и обратно. Если сохранять состояние между страницами нет необходимости то состояние страницы можно хранить не в сессии а в TempData.

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

Можно ли использовать Razor Pages в модели MVC?

Недавно начал изучать ASP.NET Core, делаю свой маленький проект. Вопрос. Можно ли использовать одновременно Razor Pages и модель MVC ? Добавил шаблон регистрации и входа в профиль для сайта (Identity), а там все сделано с файлами .cshtml и к ним файлы .cs, и не очень понятно можно ли смешивать их с контроллерами. Или нужно написать саму форму регистрации и входа с использованием контроллеров? Наверное вопрос глупый, но повторюсь, только начал изучать.

Отслеживать
задан 1 дек 2021 в 23:29
endingofourlife endingofourlife
11 2 2 бронзовых знака

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Можно и нужно использовать, потому что например, стандартная Идентификация с Microsoft.AspNet.Identity использует именно Razor Pages. Вот здесь в репозитории рабочий код, для интернет-магазина на ASP.NET Core MVC для .NET 5, где идентификация и авторизация настраивается со стандартными и дополнительными представлениями, построенными на Razor-pages. Вот в этом коммите, в файле StartUp.cs добавляется новая конечная точка, и таким образом, в одном приложении используется маршрутизации и для Razor и для MVC.

Отслеживать
ответ дан 2 дек 2021 в 0:04
Andrei Brizhak Andrei Brizhak
383 2 2 серебряных знака 8 8 бронзовых знаков

  • c#
  • .net
  • asp.net-core
  • asp.net-core-mvc
  • razor
    Важное на Мете
Похожие

Подписаться на ленту

Лента вопроса

Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.

Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.10.27.43697

Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.

Razor Pages

Начиная с версии 2.0 в ASP.NET Core была добавлена такая функциональность, как Razor Pages . Razor Pages предоставляют технологию, альтернативную системе Model-View-Controller. Razor Pages позволяют создавать страницы с кодом Razor, которые могут обрабатывать запросы. В некоторой степени эта функциональности напоминает работу веб-форм, которые представляли страницу с расширением aspx и имели файл логики на C#, связанный с данной страницей. В этом плане Razor Pages представляют альтернативу стандартной модели MVC для построения приложения.

Больше всего Razor Pages подходят для создания небольших приложений.

Для начала работы с Razor Pages создадим новый проект ASP.NET Core. При создании проекта укажем шаблон Web Application :

Razor Pages в ASP.NET Core

После создания проект будет иметь следующую структуру:

Проект Razor Pages в ASP.NET Core

Главным же отличием данного проекта от других типов проектов будет наличие папки Pages . Эта папка содержит все страницы Razor, которые есть в проекте по умолчанию. Каждая страница Razor представляет файл с расширением .cshtml и содержит смесь кода html и конструкций C#. По сути это те же самые представления, что используются в MVC. Но в отличие от MVC с каждой страницей Razor связан файл отделенного кода логики на C#. Например, с файлом Index.cshtml , который определяет визуальную часть с помощью синтаксиса Razor + HTML/CSS, связан файл Index.cshtml.cs , который определяет логику страницы или ее поведение с помощью кода C#. В какой-то степени эта модель похожа на то, что имеется в WPF или Xamarin Forms, где есть файл, который определяет визуальную часть, и есть связанный с ним файл кода, который определяет поведение.

По умолчанию в папке Pages имеются следующие файлы:

  • _Layout.cshtml : мастер-страница, в которую вставляются страницы Razor
  • _ViewStart.cshtml : задает мастер-страницу
  • _ViewImports.cshtml : определяет директивы Razor, которые добавляются на каждую страницу Razor
  • _ValidationScriptsPartial.cshtml : частичное представление, которое подключает js-скрипты валидации на стороне клиента
  • Index.cshtml , Error.cshtml и Privacy.cshtml : собственно страницы Razor, которые определяют визуальную часть страницы и логику обработки запроса.

Подключение Razor Pages

Для подключения сервисов Razor Pages применяется метод AddRazorPages . Например, во вновь созданном проекте мы можем увидеть в классе Startup в методе ConfigureServices подключение Razor Pages:

public void ConfigureServices(IServiceCollection services)

В действительности Razor Pages работают поверх фреймворка MVC, поэтому мы можем подключить и с помощью метода AddMvc() :

public void ConfigureServices(IServiceCollection services)

Однако AddMvc() подключает в целом всю возможную функциональность фреймворка MVC, в том числе возможность использования контроллеров с представлениями. Если мы не собираемся использовать контроллеры, то логично подключать Razor Pages с помощью метода AddRazorPages() .

Маршрутизация в RazorPages

Другим важным моментом является маршрутизация или как Razor Pages сопоставляются с запросами. По умолчанию проект с Razor Pages для обработки запросов используется конечные точки, то связку компонентов middleware EndpointMiddleware и EndpointRoutingMiddleware:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) < if (env.IsDevelopment()) < app.UseDeveloperExceptionPage(); >else < app.UseExceptionHandler("/Error"); app.UseHsts(); >app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => < endpoints.MapRazorPages(); // Добавляем маршрутизацию для Razor Pages >); >

Непосредствено для связи с Razor Pages применяется метод endpoints.MapRazorPages() . В остальных аспектах класс Startup не отличается от того, что мы имеем в проекте MVC.

В итоге строка запроса URL будет сопоставляться с определенной страницей Razor на основании ее расположения в проекте в папке Pages. Примеры строк URL и сопоставленных с ними страниц:

Модели и компоновки в Razor

Механизм визуализации обрабатывает код ASP.NET и ищет инструкции, которые обычно вставляют динамическое содержимое в вывод, отправляемый браузеру, a Razor — это название механизма визуализации MVC Framework. В версии MVC 5 механизм Razor не изменился, и если вы уже знакомы с его синтаксисом по предшествующим версиям, можете пропустить этот материал.

Мы собираемся начать с простой модели предметной области по имени Product, определенной в файле класса Product.cs, который добавляется в папку Models. Ниже показано содержимое этого нового файла:

namespace Razor.Models < public class Product < public int ProductID < get; set; >public string Name < get; set; >public string Description < get; set; >public decimal Price < get; set; >public string Category < set; get; >> >

Мы собираемся последовать соглашению MVC Framework и определить в качестве стартовой точки проекта контроллер по имени Home. Щелкните правой кнопкой мыши на папке Controllers в окне Solution Explorer, выберите в контекстном меню пункт Add —> Controller, укажите вариант MVC 5 Controler — в диалоговом окне Add Scaffold и щелкните на кнопке Add. В диалоговом окне Add Controller задайте для имени контроллера HomeController и щелкните на кнопке Add, чтобы создать файл Controllers/HomeController.cs. Приведите содержимое этого файла в соответствие с кодом:

using System.Web.Mvc; using Razor.Models; namespace Razor.Controllers < public class HomeController : Controller < Product myProduct = new Product < ProductID = 1, Name = "Каяк", Description = "Лодка на одного человека", Category = "Водные виды спорта", Price = 275M >; public ActionResult Index() < return View(myProduct); >public ActionResult NameAndPrice() < return View(myProduct); >> >

В контроллере Home определен метод действия по имени Index(). Объект Product, созданный в конструкторе, передается методу View(), так что он используется в качестве модели во время визуализации представления. При вызове метода View() имя файла представления не указывается, поэтому будет выбрано стандартное представление для метода действия.

Щелкните правой кнопкой мыши на методе действия Index и выберите в контекстном меню пункт Add View (Добавить представление). Удостоверьтесь, что именем представления является Index, измените Template на Empty и выберите в раскрывающемся списке Model class (Класс модели) вариант Product. (Если вы не видите класса Product в раскрывающемся списке, скомпилируйте проект и попробуйте создать представление заново.) Снимите отметку с флажка View Options (Параметры представления) и щелкните на кнопке Add, чтобы создать файл Index.cshtml в папке Views/Home.

Начальное содержимое этого нового файла показано в примере ниже:

@model Razor.Models.Product @ < Layout = null; >    Index  

В последующих разделах мы рассмотрим различные аспекты представления Razor и продемонстрируем разнообразные вещи, которые с ним можно делать. При изучении Razor полезно помнить, что представления существуют для выражения пользователю одной или более частей модели — и это означает генерацию HTML-разметки, которая отображает данные, извлеченные из одного или множества объектов. Если не забывать, что мы всегда пытаемся строить HTML-страницу, которая может быть отправлена клиенту, то вся активность механизма визуализации Razor обретает смысл.

Работа с объектом модели

Давайте начнем с самой первой строки в представлении:

@model Razor.Models.Product

Операторы Razor начинаются с символа «@». В этом случае оператор @model объявляет тип объекта модели, который будет передаваться представлению из метода действия Это позволяет ссылаться на методы, поля и свойства объекта модели представления с помощью @Model, как показано в примере ниже, где демонстрируется простое дополнение к представлению Index.cshtml:

@model Razor.Models.Product @ < Layout = null; >    Index   
@Model.Name

Обратите внимание, что тип объекта модели представления объявляется с использованием @model (со строчной буквой «m»), а доступ к свойству Name производится с применением @Model (с прописной буквой «M»). Поначалу это может немного запутывать, но со временем станет вполне привычным.

Запустив проект, вы увидите вывод, показанный на рисунке:

Результат чтения значения свойства внутри представления

С помощью выражения @model мы сообщаем инфраструктуре MVC вид объекта, с которым будет выполняться работа, а среда Visual Studio использует эту информацию парой способов. Прежде всего, при написании кода представления при вводе @Model с последующей точкой среда Visual Studio откроет окно с предполагаемыми именами членов:

Среда Visual Studio предлагает предположительные варианты имен членов, основываясь на выражении @Model

Не менее полезно и то, что Visual Studio будет помечать ошибки при наличии проблем с членами объекта модели представления, на которые производится ссылка. Пример этого можно видеть на рисунке ниже, где предпринимается попытка ссылки на несуществующее свойство. Среде Visual Studio известно, что класс Product, указанный в качестве типа модели, не содержит такого свойства, поэтому ошибка подсвечивается в редакторе.

Среда Visual Studio сообщает о проблеме с выражением @Model

Работа с компоновками

Ниже приведено еще одно выражение Razor из файла представления Index.cshtml:

Это пример блока кода Razor, который позволяет включать в представление операторы C#. Блок кода открывается посредством «@», а содержащиеся в нем операторы оцениваются при визуализации представления.

Показанный выше блок кода устанавливает значение свойства Layout в null. Как будет объясняться позже, представления Razor компилируются в классы C# приложения MVC и в базовом классе, который они используют, определено свойство Layout. Достаточно знать, что результатом установки свойства Layout в null является уведомление инфраструктуры MVC о том, что наше представление является самодостаточным, и оно будет визуализировать все свое содержимое, которое необходимо возвратить клиенту.

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

Создание компоновки

Чтобы создать компоновку, щелкните правой кнопкой мыши на папке Views в окне Solution Explorer, выберите в контекстном меню пункт Add —> New Item и укажите шаблон MVC 5 Layout Page (Razor), как показано на рисунке ниже:

Создание новой компоновки

Введите _BasicLayout.cshtml (обратите внимание, что первым символом является подчеркивание) в поле Name и щелкните на кнопке Add для создания файла. Сгенерированное Visual Studio для этого файла содержимое приведено в примере ниже:

    @ViewBag.Title   
@RenderBody()

Файлы в папке Views, имена которых начинаются с символа подчеркивания «_», не возвращаются пользователю. С помощью таких имен файлов можно различать представления, которые должны визуализироваться, и файлы, предназначенные для их поддержки. Имена файлов компоновок, являющиеся файлами поддержки, снабжаются префиксом в виде подчеркивания.

— это специализированная форма представлений. Вызов метода @RenderBody() вставляет в разметку компоновки содержимое представления, указанное методом действия. Другое выражение Razor в компоновке обращается к свойству по имени Title в объекте ViewBag, чтобы установить содержимое элемента title.

Элементы компоновки будут применяться к любому представлению, которое использует эту компоновку, и именно поэтому компоновки, в сущности, являются шаблонами. Чтобы продемонстрировать, как это работает, в примере ниже к компоновке добавлена простая разметка:

    @ViewBag.Title  

Информация о товаре

@RenderBody()

Посетите сайт ProfessorWeb

Здесь была добавлена пара элементов заголовка и применены стили CSS к элементу div, содержащему выражение @RenderBody. Это сделано просто для прояснения, какое содержимое поступает из компоновки, а какое — из представления.

Применение компоновки

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

@model Razor.Models.Product @ < Layout = "~/Views/_BasicLayout.cshtml"; ViewBag.Title = "Название товара"; >Название товара: @Model.Name

Здесь также устанавливается значение свойства ViewBag.Title, которое будет использоваться в качестве содержимого для элемента title в HTML-документе, отправляемом обратно пользователю — это необязательная, однако хорошая практика. Если значение этого свойства не задано, инфраструктура MVC возвратит пустой элемент title.

Даже для такого простого представления трансформация оказалась значительной. Остался лишь код, сосредоточенный на отображении пользователю данных из объекта модели представления, что идеально — это не только позволяет иметь дело с более простой разметкой, но также означает, что не придется дублировать общие элементы в каждом создаваемом представлении. Чтобы увидеть работу компоновки, запустите пример приложения. Результаты можно видеть на рисунке ниже:

Результат применения простой компоновки к представлению

Использование файла запуска представления

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

Решить указанную проблему можно с использованием файла запуска представления. При визуализации представления инфраструктура MVC ищет файл с именем _ViewStart.cshtml. Содержимое этого файла будет трактоваться так, если бы оно присутствовало внутри самого файла представления, и это средство можно применять для автоматической установки значения свойства Layout. Чтобы создать файл запуска представления, добавьте в папку Views новый файл представления и назовите его _ViewStart.cshtml (опять-таки, обратите внимание на подчеркивание в начале имени) и приведите его содержимое в соответствие с кодом:

Файл запуска представления содержит значение для свойства Layout, а это означает что соответствующий оператор из файла Index.cshtml можно удалить:

@model Razor.Models.Product @ < ViewBag.Title = "Название товара"; >Название товара: @Model.Name

Специально указывать, что должен применяться файл запуска представления, не требуется. Инфраструктура MVC автоматически обнаруживает данный файл и использует его содержимое. Преимущество отдается значениям, определяемым в файле представления, и это позволяет легко переопределять файл запуска представления.

Важно понимать разницу между удалением свойства Layout из файла представления и его установкой в null. Если представление является самодостаточным и применять компоновку не нужно, установите свойство Layout в null. Если просто не указывать свойство Layout, инфраструктура MVC предполагает, что компоновка необходима, и должно использоваться значение, найденное в файле запуска представления _ViewStart.cshtml.

Демонстрация разделяемых компоновок

В качестве быстрой и простой демонстрации совместного использования компоновок к контроллеру Home добавлен новый метод действия по имени NameAndPrice. Определение этого метода приведено в примере ниже, содержащем измененное содержимое файла HomeController.cs:

using System.Web.Mvc; using Razor.Models; namespace Razor.Controllers < public class HomeController : Controller < Product myProduct = new Product < ProductID = 1, Name = "Каяк", Description = "Лодка на одного человека", Category = "Водные виды спорта", Price = 275M >; public ActionResult Index() < return View(myProduct); >public ActionResult NameAndPrice() < return View(myProduct); >> >

Этот метод действия передает объект myProduct методу View(), в точности как это делает метод действия Index(); подобное вряд ли встретится в реальном проекте, но для демонстрации функциональности Razor вполне подойдет и такой очень простой пример. Щелкните правой кнопкой мыши на методе действия NameAndPrice и выберите в контекстном меню пункт Add View (Добавить представление), чтобы открыть диалоговое окно Add View. Заполните это диалоговое окно, как показано на рисунке ниже: введите в поле View Name имя NameAndPrice, выберите в списке Template вариант Empty, а в списке Model Class (Класс модели) — вариант Product:

Создание представления, которое использует компоновку

Удостоверьтесь, что флажок Use a layout page (Использовать страницу компоновки) отмечен и обратите внимание на текст ниже поля ввода. Он гласит, что вы должны оставить это поле пустым, если используемое представление было указано в файле запуска представления. Если в данный момент щелкнуть на кнопке Add, представление будет создано без оператора C#, устанавливающего значение свойства Layout.

Мы собираемся явно указать представление, поэтому щелкните на кнопке с троеточием справа от текстового поля. Среда Visual Studio отобразит диалоговое окно, которое позволяет выбрать файл компоновки:

Выбор файла компоновки

Соглашение, принятое для проекта MVC, предполагает помещение файлов компоновки в папку Views. Тем не менее, это всего лишь соглашение, поэтому левая панель окна позволяет осуществлять навигацию в рамках проекта на тот случай, если вы решили не придерживаться указанного соглашения.

Мы определили только один файл компоновки, поэтому выберите файл _BasicLayout.cshtml и щелкните на кнопке ОК для возврата в диалоговое окно Add View. Вы увидите, что имя файла компоновки появилось в текстовом поле:

Указание файла компоновки при создании представления

Щелкните на кнопке Add для создания файла /Views/Home/NameAndPrice.cshtml Содержимое этого файла приведено в примере ниже:

@model Razor.Models.Product @< ViewBag.Title = "NameAndPrice"; Layout = "~/Views/_BasicLayout.cshtml"; >

В случае указания компоновки среда Visual Studio применяет слегка отличающееся стандартное содержимое для файлов представлений, но внутри них находятся те же самые выражения Razor, которые мы используем, когда применяем компоновку к представлению напрямую.

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

@model Razor.Models.Product @ < ViewBag.Title = "Название и цена"; Layout = "~/Views/_BasicLayout.cshtml"; >

Название и цена

Название продукта - @Model.Name, он стоит $@Model.Price

Как и ожидалось, общие элементы и стили, определенные в компоновке, будут применены и к представлению, что демонстрирует возможность использования компоновки для создания общего внешнего вида (несмотря на его простоту и непривлекательность в рассматриваемом примере).

Мы получили бы тот же самый результат, если бы оставили текстовое поле в диалоговом окне Add View пустым и понадеялись на файл запуска представления. Файл компоновки был указан явно только для того, чтобы продемонстрировать средство Visual Studio, помогающее делать выбор.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *