ASP.NET Core 中的布局Layout in ASP.NET Core

作者:Steve SmithDave BrockBy Steve Smith and Dave Brock

页面和视图经常共享视觉对象和程序元素。Pages and views frequently share visual and programmatic elements. 本文演示了以下内容的操作方法:This article demonstrates how to:

  • 使用通用布局。Use common layouts.
  • 共享指令。Share directives.
  • 在呈现页面或视图之前运行通用代码。Run common code before rendering pages or views.

本文档讨论了 ASP.NET Core MVC 的两种不同方法的布局:Razor Pages 和具有视图的控制器。This document discusses layouts for the two different approaches to ASP.NET Core MVC: Razor Pages and controllers with views. 在本主题中,差异很小:For this topic, the differences are minimal:

  • Razor Pages 位于“页面”文件夹。Razor Pages are in the Pages folder.
  • 具有视图的控制器使用视图的“视图”文件夹。Controllers with views uses a Views folder for views.

什么是布局What is a Layout

大多数 Web 应用都有一个通用布局,可在页面间切换时为用户提供一致体验。Most web apps have a common layout that provides the user with a consistent experience as they navigate from page to page. 该布局通常包括应用标头、导航或菜单元素以及页脚等常见的用户界面元素。The layout typically includes common user interface elements such as the app header, navigation or menu elements, and footer.

页面布局示例

应用中的许多页面也经常使用脚本和样式表等常用的 HTML 结构。Common HTML structures such as scripts and stylesheets are also frequently used by many pages within an app. 所有这些共享元素均可在布局文件中进行定义,应用内使用的任何视图随后均可引用此文件。All of these shared elements may be defined in a layout file, which can then be referenced by any view used within the app. 布局可减少视图中的重复代码。Layouts reduce duplicate code in views.

按照约定,ASP.NET Core 应用的默认布局名为 _Layout.cshtml。By convention, the default layout for an ASP.NET Core app is named _Layout.cshtml. 使用模板创建的新 ASP.NET Core 项目的布局文件:The layout file for new ASP.NET Core projects created with the templates:

  • Razor Pages:Pages/Shared/_Layout.cshtmlRazor Pages: Pages/Shared/_Layout.cshtml

    解决方案资源管理器中的页面文件夹

  • 具有视图的控制器:Views/Shared/_Layout.cshtmlController with views: Views/Shared/_Layout.cshtml

解决方案资源管理器中的视图文件夹

布局定义应用中的视图的最高级别模板。The layout defines a top level template for views in the app. 应用不需要布局。Apps don't require a layout. 应用可以定义多个布局,并且不同的视图指定不同的布局。Apps can define more than one layout, with different views specifying different layouts.

下面的代码演示了使用控制器和视图创建的项目模板的布局文件:The following code shows the layout file for a template created project with a controller and views:

<!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>

指定布局Specifying a Layout

Razor 视图具有 Layout 属性。Razor views have a Layout property. 单个视图通过设置此属性来指定布局:Individual views specify a layout by setting this property:

@{
    Layout = "_Layout";
}

指定的布局可以使用完整路径(例如 /Pages/Shared/_Layout.cshtml 或 /Views/Shared/_Layout.cshtml)或部分名称(示例:_Layout)。The layout specified can use a full path (for example, /Pages/Shared/_Layout.cshtml or /Views/Shared/_Layout.cshtml) or a partial name (example: _Layout). 如果提供部分名称,则 Razor 视图引擎使用其标准发现过程来搜索布局文件。When a partial name is provided, the Razor view engine searches for the layout file using its standard discovery process. 首先搜索处理程序方法(或控制器)所在的文件夹,然后搜索 Shared 文件夹。The folder where the handler method (or controller) exists is searched first, followed by the Shared folder. 此发现过程与用于发现分部视图的过程相同。This discovery process is identical to the process used to discover partial views.

默认情况下,每个布局必须调用 RenderBodyBy default, every layout must call RenderBody. 无论在何处调用 RenderBody,都会呈现视图的内容。Wherever the call to RenderBody is placed, the contents of the view will be rendered.

部分Sections

布局可以通过调用 RenderSection 来选择引用一个或多个节。A layout can optionally reference one or more sections, by calling RenderSection. 节提供一种方法来组织某些页面元素应当放置的位置。Sections provide a way to organize where certain page elements should be placed. 每次调用 RenderSection 时都可指定该部分是必需还是可选:Each call to RenderSection can specify whether that section is required or optional:

@section Scripts {
    @RenderSection("Scripts", required: false)
}

如果找不到所需的节,将引发异常。If a required section isn't found, an exception is thrown. 单个视图使用 @section Razor 语法指定要在节中呈现的内容。Individual views specify the content to be rendered within a section using the @section Razor syntax. 如果某个页面或视图定义了一个部分,则必须呈现该部分(否则将发生错误)。If a page or view defines a section, it must be rendered (or an error will occur).

Razor Pages 视图中的示例 @section 定义:An example @section definition in Razor Pages view:

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

在上面的代码中,scripts / main.js 添加到了页面或视图的 scripts 部分。In the preceding code, scripts/main.js is added to the scripts section on a page or view. 相同应用中的其他页面或视图可能不需要此脚本且不会定义脚本部分。Other pages or views in the same app might not require this script and wouldn't define a scripts section.

以下标记使用部分标记帮助程序来呈现 _ValidationScriptsPartial.cshtml:The following markup uses the Partial Tag Helper to render _ValidationScriptsPartial.cshtml:

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

前面的标记由基架标识生成。The preceding markup was generated by scaffolding Identity.

在页面或视图中定义的部分仅在其即时布局页面中可用。Sections defined in a page or view are available only in its immediate layout page. 不能从部分、视图组件或视图系统的其他部分引用它们。They cannot be referenced from partials, view components, or other parts of the view system.

忽略节Ignoring sections

默认情况下,必须由布局页面呈现内容页中的正文和所有节。By default, the body and all sections in a content page must all be rendered by the layout page. Razor 视图引擎通过跟踪是否已呈现正文和每个节来强制执行此操作。The Razor view engine enforces this by tracking whether the body and each section have been rendered.

要让视图引擎忽略正文或节,请调用 IgnoreBodyIgnoreSection 方法。To instruct the view engine to ignore the body or sections, call the IgnoreBody and IgnoreSection methods.

必须呈现或忽略 Razor 页面中的正文和每个节。The body and every section in a Razor page must be either rendered or ignored.

导入共享指令Importing Shared Directives

视图和页面可以使用 Razor 指令来导入命名空间并使用依赖项注入Views and pages can use Razor directives to importing namespaces and use dependency injection. 由多个视图共享的指令可以在通用 _ViewImports.cshtml 文件中进行指定。Directives shared by many views may be specified in a common _ViewImports.cshtml file. _ViewImports 文件支持以下指令:The _ViewImports file supports the following directives:

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

该文件不支持函数和节定义等其他 Razor 功能。The file doesn't support other Razor features, such as functions and section definitions.

示例 _ViewImports.cshtml 文件:A sample _ViewImports.cshtml file:

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

ASP.NET Core MVC 应用的 _ViewImports.cshtml 文件通常放在“页面”(或“视图”)文件夹中。The _ViewImports.cshtml file for an ASP.NET Core MVC app is typically placed in the Pages (or Views) folder. _ViewImports.cshtml 文件可以放在任何文件夹中,在这种情况下,它只会应用于该文件夹及其子文件夹中的页面或视图。A _ViewImports.cshtml file can be placed within any folder, in which case it will only be applied to pages or views within that folder and its subfolders. 从根级别开始处理 _ViewImports 文件,然后处理在页面或视图本身的位置之前的每个文件夹。_ViewImports files are processed starting at the root level and then for each folder leading up to the location of the page or view itself. 可以在文件夹级别覆盖根级别指定的 _ViewImports 设置。_ViewImports settings specified at the root level may be overridden at the folder level.

例如,假设:For example, suppose:

  • 根级别 _ViewImports.cshtml 文件包含 @model MyModel1@addTagHelper *, MyTagHelper1The root level _ViewImports.cshtml file includes @model MyModel1 and @addTagHelper *, MyTagHelper1.
  • 子文件夹 _ViewImports.cshtml 文件包含 @model MyModel2@addTagHelper *, MyTagHelper2A subfolder _ViewImports.cshtml file includes @model MyModel2 and @addTagHelper *, MyTagHelper2.

子文件夹中的页面和视图将有权访问标记帮助程序和 MyModel2 模型。Pages and views in the subfolder will have access to both Tag Helpers and the MyModel2 model.

如果在文件层次结构中找到多个 _ViewImports.cshtml 文件,指令的合并行为是:If multiple _ViewImports.cshtml files are found in the file hierarchy, the combined behavior of the directives are:

  • @addTagHelper``@removeTagHelper:按顺序全部运行@addTagHelper, @removeTagHelper: all run, in order
  • @tagHelperPrefix:最接近视图的文件会替代任何其他文件@tagHelperPrefix: the closest one to the view overrides any others
  • @model:最接近视图的文件会替代任何其他文件@model: the closest one to the view overrides any others
  • @inherits:最接近视图的文件会替代任何其他文件@inherits: the closest one to the view overrides any others
  • @using:全部包括在内;忽略重复项@using: all are included; duplicates are ignored
  • @inject:针对每个属性,最接近视图的属性会替代具有相同属性名的任何其他属性@inject: for each property, the closest one to the view overrides any others with the same property name

在呈现每个视图之前运行代码Running Code Before Each View

需要在每个视图或页面之前运行的代码应置于 _ViewStart.cshtml 文件中。Code that needs to run before each view or page should be placed in the _ViewStart.cshtml file. 按照约定,_ViewStart.cshtml 文件位于“页面”(或“视图”)文件夹。By convention, the _ViewStart.cshtml file is located in the Pages (or Views) folder. 在呈现每个完整视图(不是布局,也不是分部视图)之前运行 _ViewStart.cshtml 中列出的语句。The statements listed in _ViewStart.cshtml are run before every full view (not layouts, and not partial views). ViewImports.cshtml 一样,_ViewStart.cshtml 也是分层结构。Like ViewImports.cshtml, _ViewStart.cshtml is hierarchical. 如果在视图或页面文件夹中定义了 _ViewStart.cshtml 文件,则它将在页面(或视图)的根目录中定义的文件之后运行)文件夹(如果有)。If a _ViewStart.cshtml file is defined in the view or pages folder, it will be run after the one defined in the root of the Pages (or Views) folder (if any).

一个示例 _ViewStart.cshtml 文件:A sample _ViewStart.cshtml file:

@{
    Layout = "_Layout";
}

上述文件指定所有视图都将使用 _Layout.cshtml 布局。The file above specifies that all views will use the _Layout.cshtml layout.

_ViewStart.cshtml 和 _ViewImports.cshtml 通常不置于 /Pages/Shared(或 /Views/Shared)文件夹中。_ViewStart.cshtml and _ViewImports.cshtml are not typically placed in the /Pages/Shared (or /Views/Shared) folder. 这些应用级别版本的文件应直接放置在 /Pages(或 /Views)文件夹中。The app-level versions of these files should be placed directly in the /Pages (or /Views) folder.