Макет в ASP.NET Core

Авторы: Стив Смит (Steve Smith) и Дейв Брок (Dave Brock)

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

  • Использование общих макетов.
  • Совместное использование директив.
  • Запуск общего кода до отрисовки страниц или представлений.

В этом документе рассматриваются макеты двух различных подходов к ASP.NET Core MVC: Razor Pages и контроллеров с представлениями. С этой точки зрения различия минимальны:

  • Razor Страницы находятся в папке Pages .
  • Контроллеры с представлениями используют папку Views для представлений.

Что такое макет

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

Page Layout example

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

В соответствии с соглашением макет по умолчанию для приложения ASP.NET Core имеет имя _Layout.cshtml. В новых проектах ASP.NET Core, созданных с применением шаблонов, используются следующие файлы макета.

  • Razor Страниц: Pages/Shared/_Layout.cshtml

    Pages folder in Solution Explorer

  • Контроллер с представлениями: Views/Shared/_Layout.cshtml

    Views folder in Solution Explorer

Макет определяет шаблон верхнего уровня для представлений в приложении. Для приложений макет не требуется. В приложении может быть определено несколько макетов для разных представлений.

В следующем коде показан файл макета для проекта, созданного по шаблону, с контроллером и представлениями:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - WebApplication1</title>

    <environment include="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
        <link rel="stylesheet" href="~/css/site.css" />
    </environment>
    <environment exclude="Development">
        <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
        <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
    </environment>
</head>
<body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a asp-page="/Index" class="navbar-brand">WebApplication1</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a asp-page="/Index">Home</a></li>
                    <li><a asp-page="/About">About</a></li>
                    <li><a asp-page="/Contact">Contact</a></li>
                </ul>
            </div>
        </div>
    </nav>

    <partial name="_CookieConsentPartial" />

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; 2018 - WebApplication1</p>
        </footer>
    </div>

    <environment include="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/js/site.js" asp-append-version="true"></script>
    </environment>
    <environment exclude="Development">
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery"
                crossorigin="anonymous"
                integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT">
        </script>
        <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
                crossorigin="anonymous"
                integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
        </script>
        <script src="~/js/site.min.js" asp-append-version="true"></script>
    </environment>

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

Указание макета

Razor представления имеют Layout свойство. С его помощью указывается макет в отдельных представлениях:

@{
    Layout = "_Layout";
}

Указанный макет может использовать полный путь (например, /Pages/Shared/_Layout.cshtml или) или /Views/Shared/_Layout.cshtmlчастичное имя (например: _Layout). При указании Razor частичного имени подсистема просмотра ищет файл макета с помощью стандартного процесса обнаружения. Сначала поиск выполняется в папке, где существует метод обработчика (или контроллер), а затем в папке Shared. Процесс обнаружения аналогичен тому, который применяется для поиска частичных представлений.

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

Разделы

Макет может при необходимости ссылаться на один или несколько разделов, вызывая метод RenderSection. Разделы — это средство для упорядочения размещения определенных элементов на странице. В каждом вызове RenderSection можно указывать, является ли раздел обязательным или необязательным:

<script type="text/javascript" src="~/scripts/global.js"></script>

@RenderSection("Scripts", required: false)

Если обязательный раздел не найден, создается исключение. Отдельные представления указывают содержимое, отображаемое в разделе с помощью синтаксиса @sectionRazor . Если на странице или в представлении определяется раздел, он должен быть преобразован для просмотра (в противном случае произойдет ошибка).

Пример @section определения в Razor представлении Pages:

@section Scripts {
     <script type="text/javascript" src="~/scripts/main.js"></script>
}

В приведенном выше коде scripts/main.js добавляется в scripts раздел на странице или представлении. Другие страницы или представления в одном приложении могут не требовать этот скрипт и не определять раздел скриптов.

Следующая разметка использует вспомогательный элемент частичного тега для отрисовки_ValidationScriptsPartial.cshtml:

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Предыдущая разметка была создана с помощью шаблонов Identity.

Разделы, определенные на странице или в представлении, доступны только непосредственно на странице макета. На них нельзя ссылаться из частичных представлений, компонентов представлений или других частей системы представлений.

Пропуск разделов

По умолчанию тело и все разделы страницы содержимого должны преобразовываться для просмотра страницей макета. Обработчик Razor представлений применяет это путем отслеживания того, был ли отрисован текст и каждый раздел.

Чтобы подсистема представлений пропустила тело или разделы, вызовите методы IgnoreBody и IgnoreSection.

Текст и каждый раздел страницы Razor должны отображаться или игнорироваться.

Импорт общих директив

Представления и страницы могут использовать Razor директивы для импорта пространств имен и внедрения зависимостей. Директивы, используемые несколькими представлениями, можно указать в общем файле _ViewImports.cshtml. Файл _ViewImports поддерживает следующие директивы:

  • @addTagHelper
  • @removeTagHelper
  • @tagHelperPrefix
  • @using
  • @model
  • @inherits
  • @inject
  • @namespace

Файл не поддерживает другие Razor функции, такие как функции и определения разделов.

Пример файла _ViewImports.cshtml:

@using WebApplication1
@using WebApplication1.Models
@using WebApplication1.Models.AccountViewModels
@using WebApplication1.Models.ManageViewModels
@using Microsoft.AspNetCore.Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

_ViewImports.cshtml Файл для приложения MVC ASP.NET Core обычно помещается в папку Pages (или Views). _ViewImports.cshtml Файл можно поместить в любую папку, в этом случае она будет применена только к страницам или представлениям в этой папке и ее вложенным папкам. Файлы _ViewImports обрабатываются начиная с корневого уровня, а затем для каждой папки вплоть до расположения самой страницы или представления. Параметры _ViewImports, заданные на корневом уровне, можно переопределить на уровне папки.

Например, предположим, что:

  • Файл корневого уровня _ViewImports.cshtml включает @model MyModel1 и @addTagHelper *, MyTagHelper1.
  • Вложенная папка _ViewImports.cshtml содержит @model MyModel2 и @addTagHelper *, MyTagHelper2.

Страницы и представления во вложенной папке будут иметь доступ к вспомогательным функциям тегов и модели MyModel2.

Если несколько _ViewImports.cshtml файлов находятся в иерархии файлов, объединенное поведение директив:

  • @addTagHelper, @removeTagHelper: выполняются все директивы по порядку;
  • @tagHelperPrefix: ближайшая к представлению директива переопределяет все остальные;
  • @model: ближайшая к представлению директива переопределяет все остальные;
  • @inherits: ближайшая к представлению директива переопределяет все остальные;
  • @using: включаются все директивы, повторяющиеся пропускаются;
  • @inject: для каждого свойства ближайшая к представлению директива переопределяет все остальные директивы с тем же именем свойства.

Выполнение кода перед каждым представлением

Код, который должен выполняться перед каждым представлением или страницей _ViewStart.cshtml , должен быть помещен в файл. По соглашению _ViewStart.cshtml файл находится в папке Pages (или Views). Операторы, перечисленные в файле _ViewStart.cshtml, выполняются перед каждым полным представлением (но не перед макетами и не перед частичными представлениями). Так же как файлы ViewImports.cshtml, файлы _ViewStart.cshtml являются иерархическими. _ViewStart.cshtml Если файл определен в папке представления или страниц, он будет выполняться после того, как он определен в корневой папке Pages (или Views) (если таковые есть).

Пример файла _ViewStart.cshtml:

@{
    Layout = "_Layout";
}

Приведенный файл предписывает всем представлениям использовать макет _Layout.cshtml.

_ViewStart.cshtmlи _ViewImports.cshtml обычно не помещаются в папку /Pages/Shared (или /Views/Shared). Версии этих файлов, которые должны действовать на уровне приложения, следует помещать непосредственно в папку /Pages (или /Views).