第 3 部分,将视图添加到 ASP.NET Core MVC 应用

注意

此版本不是本文的最新版本。 有关当前版本,请参阅本文.NET 9 版本。

警告

此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 有关当前版本,请参阅本文.NET 9 版本。

重要

此信息与预发布产品相关,相应产品在商业发布之前可能会进行重大修改。 Microsoft 对此处提供的信息不提供任何明示或暗示的保证。

有关当前版本,请参阅本文.NET 9 版本。

作者:Rick Anderson

在此部分中,你将修改 HelloWorldController 类以使用 Razor 视图文件。 这顺利封装了为客户端生成 HTML 响应的过程。

视图模板是使用 Razor 创建的。 基于 Razor 的视图模板:

  • 具有 .cshtml 文件扩展名。
  • 提供一种巧妙的方法来使用 C# 创建 HTML 输出。

当前,Index 方法返回一个字符串,其中包含控制器类中的消息。 在 HelloWorldController 类中,将 Index 方法替换为以下代码:

public IActionResult Index()
{
    return View();
}

前面的代码:

  • 调用该控制器的 View 方法。
  • 使用视图模板生成 HTML 响应。

控制器方法:

  • 称为“操作方法”。 例如,上述代码中的 Index 操作方法。
  • 通常返回 IActionResult 或从 ActionResult 派生的类,而不是 string 这样的类型。

添加视图

右键单击“视图”文件夹,然后单击“添加”>>“新文件夹”,并将文件夹命名为“HelloWorld”。

右键单击“Views/HelloWorld”文件夹,然后单击“添加”>“新项”。

在“添加新项”对话框中,选择“显示所有模板”。

在“添加新项 - MvcMovie”对话框中:

  • 在右上角的搜索框中,输入“视图”
  • 选择“Razor 视图 - 空”
  • 保持“名称”框的值:Index.cshtml
  • 选择“添加”

“添加新项”对话框

Views/HelloWorld/Index.cshtmlRazor 视图文件的内容替换为以下内容:

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

导航到 https://localhost:{PORT}/HelloWorld

  • HelloWorldController 中的 Index 方法运行 return View(); 语句,指定此方法应使用视图模板文件来呈现对浏览器的响应。

  • 由于未指定视图模板文件名称,因此 MVC 默认使用默认视图文件。 如果未指定视图文件名称,则返回默认视图。 默认视图与操作方法的名称相同,在本例中为 Index。 使用视图模板 /Views/HelloWorld/Index.cshtml

  • 下图显示了视图中硬编码的字符串“Hello from our View Template!”:

    浏览器窗口

更改视图和布局页面

选择菜单链接“MvcMovie”、“Home”和“Privacy”。 每页显示相同的菜单布局。 菜单布局在 Views/Shared/_Layout.cshtml 文件中实现。

打开 Views/Shared/_Layout.cshtml 文件。

布局模板允许:

  • 在一个位置指定站点的 HTML 容器布局。
  • 在该站点的多个页面上应用 HTML 容器布局。

查找 @RenderBody() 行。 RenderBody 是显示创建的所有特定于视图的页面的占位符,已包装在布局页面中。 例如,如果选择 Privacy 链接,Views/Home/Privacy.cshtml 视图将在 RenderBody 方法中呈现。

Views/Shared/_Layout.cshtml 文件的内容替换为以下标记。 突出显示所作更改:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Movie App</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/MvcMovie.styles.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container-fluid">
                <a class="navbar-brand" asp-area="" asp-controller="Movies" asp-action="Index">Movie App</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2024 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <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>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

上述标记进行以下更改:

  • MvcMovie 更改为 Movie App 三次。
  • 定位点元素 <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">MvcMovie</a> 更改为 <a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a>

在前面的标记中,省略了 asp-area="" 定位点标记帮助程序特性和特性值,因为此应用未使用区域

说明Movies 控制器尚未实现。 此时,Movie App 链接不起作用。

保存更改并选择“Privacy”链接。 请注意,浏览器标签页上的标题现在显示的是“Privacy 策略 - 电影应用”,而不是“Privacy 策略 - MvcMovie”

Privacy 选项卡

选择 Home 链接。

请注意,标题和定位点文本显示“电影应用”。 在布局模板中进行了一次更改,网站上的所有页面都反映新的链接文本和新标题。

检查 Views/_ViewStart.cshtml 文件:

@{
    Layout = "_Layout";
}

Views/_ViewStart.cshtml 文件将 Views/Shared/_Layout.cshtml 文件引入到每个视图中。 可以使用 Layout 属性设置不同的布局视图,或将它设置为 null,这样将不会使用任何布局文件。

打开 Views/HelloWorld/Index.cshtml 视图文件。

更改标题和 <h2> 元素,如以下突出显示:

@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

标题和 <h2> 元素略有不同,因此可以清楚地看出代码的哪一部分更改了显示。

上述代码中的 ViewData["Title"] = "Movie List";ViewData 字典的 Title 属性设置为“Movie List”。 Title 属性在布局页面中的 <title> HTML 元素中使用:

<title>@ViewData["Title"] - Movie App</title>

保存更改并导航到 https://localhost:{PORT}/HelloWorld

请注意,以下内容已更改:

  • 浏览器标题。
  • 主标题。
  • 二级标题。

如果浏览器中没有任何更改,则可能是正在查看的缓存内容。 在浏览器中按 Ctrl + F5 强制加载来自服务器的响应。 浏览器标题是使用我们在 Index.cshtml 视图模板中设置的 ViewData["Title"] 以及在布局文件中添加的额外“- Movie App”创建的。

Index.cshtml 视图模板中的内容与 Views/Shared/_Layout.cshtml 视图模板合并。 单个 HTML 响应将发送到浏览器。 凭借布局模板可以轻松地对应用中所有页面进行更改。 若要了解更多信息,请参阅布局

电影列表视图

但是,“数据”的一小部分(即“Hello from our View Template!”消息)是硬编码的。 MVC 应用程序有一个“V”(视图)和一个“C”(控制器),但还没有“M”(模型)。

将数据从控制器传递给视图

控制器操作会被调用以响应传入的 URL 请求。 控制器类是编写处理传入浏览器请求的代码的地方。 控制器从数据源检索数据,并决定将哪些类型的响应发送回浏览器。 可以从控制器使用视图模板来生成并格式化对浏览器的 HTML 响应。

控制器负责提供所需的数据,使视图模板能够呈现响应。

视图模板不应:

  • 执行业务逻辑
  • 直接与数据库交互。

视图模板应仅使用由控制器提供给它的数据。 保持此“分离关注点”有助于保持代码:

  • 干净。
  • 可测试。
  • 可维护。

目前,HelloWorldController 类中的 Welcome 方法采用 nameID 参数,然后将值直接输出到浏览器。

应将控制器更改为使用视图模板,而不是使控制器将此响应呈现为字符串。 视图模板会生成动态响应,这意味着必须将适当的数据从控制器传递给视图以生成响应。 为此,可以让控制器将视图模板所需的动态数据(参数)放置在 ViewData 字典中。 然后,视图模板可以访问动态数据。

HelloWorldController.cs 中,更改 Welcome 方法以将 MessageNumTimes 值添加到 ViewData 字典。

ViewData 字典是动态对象,这意味着任何类型都可以使用。 在添加某些内容之前,ViewData 对象没有已定义的属性。 MVC 模型绑定系统自动将命名参数 namenumTimes 从查询字符串映射到方法中的参数。 完整的 HelloWorldController

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers;

public class HelloWorldController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
    public IActionResult Welcome(string name, int numTimes = 1)
    {
        ViewData["Message"] = "Hello " + name;
        ViewData["NumTimes"] = numTimes;
        return View();
    }
}

ViewData 字典对象包含将传递给视图的数据。

创建一个名为 Views/HelloWorld/Welcome.cshtml 的 Welcome 视图模板。

Welcome.cshtml 视图模板中创建一个循环,显示“Hello”NumTimes。 将 Views/HelloWorld/Welcome.cshtml 的内容替换为以下内容:

@{
    ViewData["Title"] = "Welcome";
}

<h2>Welcome</h2>

<ul>
    @for (int i = 0; i < (int)ViewData["NumTimes"]!; i++)
    {
        <li>@ViewData["Message"]</li>
    }
</ul>

保存更改并浏览到以下 URL:

https://localhost:{PORT}/HelloWorld/Welcome?name=Rick&numtimes=4

数据取自 URL,并传递给使用 MVC 模型绑定器的控制器。 控制器将数据打包到 ViewData 字典中,并将该对象传递给视图。 然后,视图将数据作为 HTML 呈现给浏览器。

Privacy 视图,显示了 Welcome 标签以及四个“Hello Rick”短语

在前面的示例中,我们使用 ViewData 字典将数据从控制器传递给视图。 稍后在本教程中,我们将使用视图模型将数据从控制器传递给视图。 传递数据的视图模型方法比 ViewData 字典方法更为优先。

在下一个教程中,将创建电影数据库。

在此部分中,你将修改 HelloWorldController 类以使用 Razor 视图文件。 这顺利封装了为客户端生成 HTML 响应的过程。

视图模板是使用 Razor 创建的。 基于 Razor 的视图模板:

  • 具有 .cshtml 文件扩展名。
  • 提供一种巧妙的方法来使用 C# 创建 HTML 输出。

当前,Index 方法返回一个字符串,其中包含控制器类中的消息。 在 HelloWorldController 类中,将 Index 方法替换为以下代码:

public IActionResult Index()
{
    return View();
}

前面的代码:

  • 调用该控制器的 View 方法。
  • 使用视图模板生成 HTML 响应。

控制器方法:

  • 称为“操作方法”。 例如,上述代码中的 Index 操作方法。
  • 通常返回 IActionResult 或从 ActionResult 派生的类,而不是 string 这样的类型。

添加视图

右键单击“视图”文件夹,然后单击“添加”>>“新文件夹”,并将文件夹命名为“HelloWorld”。

右键单击“Views/HelloWorld”文件夹,然后单击“添加”>“新项”。

在“添加新项”对话框中,选择“显示所有模板”。

在“添加新项 - MvcMovie”对话框中:

  • 在右上角的搜索框中,输入“视图”
  • 选择“Razor 视图 - 空”
  • 保持“名称”框的值:Index.cshtml
  • 选择“添加”

“添加新项”对话框

Views/HelloWorld/Index.cshtmlRazor 视图文件的内容替换为以下内容:

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

导航到 https://localhost:{PORT}/HelloWorld

  • HelloWorldController 中的 Index 方法运行 return View(); 语句,指定此方法应使用视图模板文件来呈现对浏览器的响应。

  • 由于未指定视图模板文件名称,因此 MVC 默认使用默认视图文件。 如果未指定视图文件名称,则返回默认视图。 默认视图与操作方法的名称相同,在本例中为 Index。 使用视图模板 /Views/HelloWorld/Index.cshtml

  • 下图显示了视图中硬编码的字符串“Hello from our View Template!”:

    浏览器窗口

更改视图和布局页面

选择菜单链接“MvcMovie”、“Home”和“Privacy”。 每页显示相同的菜单布局。 菜单布局在 Views/Shared/_Layout.cshtml 文件中实现。

打开 Views/Shared/_Layout.cshtml 文件。

布局模板允许:

  • 在一个位置指定站点的 HTML 容器布局。
  • 在该站点的多个页面上应用 HTML 容器布局。

查找 @RenderBody() 行。 RenderBody 是显示创建的所有特定于视图的页面的占位符,已包装在布局页面中。 例如,如果选择 Privacy 链接,Views/Home/Privacy.cshtml 视图将在 RenderBody 方法中呈现。

Views/Shared/_Layout.cshtml 文件的内容替换为以下标记。 突出显示所作更改:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Movie App</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container-fluid">
                <a class="navbar-brand" asp-area="" asp-controller="Movies" asp-action="Index">Movie App</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2023 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <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>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

上述标记进行以下更改:

  • MvcMovie 更改为 Movie App 三次。
  • 定位点元素 <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">MvcMovie</a> 更改为 <a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a>

在前面的标记中,省略了 asp-area="" 定位点标记帮助程序特性和特性值,因为此应用未使用区域

说明Movies 控制器尚未实现。 此时,Movie App 链接不起作用。

保存更改并选择“Privacy”链接。 请注意,浏览器标签页上的标题现在显示的是“Privacy 策略 - 电影应用”,而不是“Privacy 策略 - MvcMovie”

Privacy 选项卡

选择 Home 链接。

请注意,标题和定位点文本显示“电影应用”。 在布局模板中进行了一次更改,网站上的所有页面都反映新的链接文本和新标题。

检查 Views/_ViewStart.cshtml 文件:

@{
    Layout = "_Layout";
}

Views/_ViewStart.cshtml 文件将 Views/Shared/_Layout.cshtml 文件引入到每个视图中。 可以使用 Layout 属性设置不同的布局视图,或将它设置为 null,这样将不会使用任何布局文件。

打开 Views/HelloWorld/Index.cshtml 视图文件。

更改标题和 <h2> 元素,如以下突出显示:

@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

标题和 <h2> 元素略有不同,因此可以清楚地看出代码的哪一部分更改了显示。

上述代码中的 ViewData["Title"] = "Movie List";ViewData 字典的 Title 属性设置为“Movie List”。 Title 属性在布局页面中的 <title> HTML 元素中使用:

<title>@ViewData["Title"] - Movie App</title>

保存更改并导航到 https://localhost:{PORT}/HelloWorld

请注意,以下内容已更改:

  • 浏览器标题。
  • 主标题。
  • 二级标题。

如果浏览器中没有任何更改,则可能是正在查看的缓存内容。 在浏览器中按 Ctrl + F5 强制加载来自服务器的响应。 浏览器标题是使用我们在 Index.cshtml 视图模板中设置的 ViewData["Title"] 以及在布局文件中添加的额外“- Movie App”创建的。

Index.cshtml 视图模板中的内容与 Views/Shared/_Layout.cshtml 视图模板合并。 单个 HTML 响应将发送到浏览器。 凭借布局模板可以轻松地对应用中所有页面进行更改。 若要了解更多信息,请参阅布局

电影列表视图

但是,“数据”的一小部分(即“Hello from our View Template!”消息)是硬编码的。 MVC 应用程序有一个“V”(视图)和一个“C”(控制器),但还没有“M”(模型)。

将数据从控制器传递给视图

控制器操作会被调用以响应传入的 URL 请求。 控制器类是编写处理传入浏览器请求的代码的地方。 控制器从数据源检索数据,并决定将哪些类型的响应发送回浏览器。 可以从控制器使用视图模板来生成并格式化对浏览器的 HTML 响应。

控制器负责提供所需的数据,使视图模板能够呈现响应。

视图模板不应:

  • 执行业务逻辑
  • 直接与数据库交互。

视图模板应仅使用由控制器提供给它的数据。 保持此“分离关注点”有助于保持代码:

  • 干净。
  • 可测试。
  • 可维护。

目前,HelloWorldController 类中的 Welcome 方法采用 nameID 参数,然后将值直接输出到浏览器。

应将控制器更改为使用视图模板,而不是使控制器将此响应呈现为字符串。 视图模板会生成动态响应,这意味着必须将适当的数据从控制器传递给视图以生成响应。 为此,可以让控制器将视图模板所需的动态数据(参数)放置在 ViewData 字典中。 然后,视图模板可以访问动态数据。

HelloWorldController.cs 中,更改 Welcome 方法以将 MessageNumTimes 值添加到 ViewData 字典。

ViewData 字典是动态对象,这意味着任何类型都可以使用。 在添加某些内容之前,ViewData 对象没有已定义的属性。 MVC 模型绑定系统自动将命名参数 namenumTimes 从查询字符串映射到方法中的参数。 完整的 HelloWorldController

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers;

public class HelloWorldController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
    public IActionResult Welcome(string name, int numTimes = 1)
    {
        ViewData["Message"] = "Hello " + name;
        ViewData["NumTimes"] = numTimes;
        return View();
    }
}

ViewData 字典对象包含将传递给视图的数据。

创建一个名为 Views/HelloWorld/Welcome.cshtml 的 Welcome 视图模板。

Welcome.cshtml 视图模板中创建一个循环,显示“Hello”NumTimes。 将 Views/HelloWorld/Welcome.cshtml 的内容替换为以下内容:

@{
    ViewData["Title"] = "Welcome";
}

<h2>Welcome</h2>

<ul>
    @for (int i = 0; i < (int)ViewData["NumTimes"]!; i++)
    {
        <li>@ViewData["Message"]</li>
    }
</ul>

保存更改并浏览到以下 URL:

https://localhost:{PORT}/HelloWorld/Welcome?name=Rick&numtimes=4

数据取自 URL,并传递给使用 MVC 模型绑定器的控制器。 控制器将数据打包到 ViewData 字典中,并将该对象传递给视图。 然后,视图将数据作为 HTML 呈现给浏览器。

Privacy 视图,显示了 Welcome 标签以及四个“Hello Rick”短语

在前面的示例中,我们使用 ViewData 字典将数据从控制器传递给视图。 稍后在本教程中,我们将使用视图模型将数据从控制器传递给视图。 传递数据的视图模型方法比 ViewData 字典方法更为优先。

在下一个教程中,将创建电影数据库。

在此部分中,你将修改 HelloWorldController 类以使用 Razor 视图文件。 这顺利封装了为客户端生成 HTML 响应的过程。

视图模板是使用 Razor 创建的。 基于 Razor 的视图模板:

  • 具有 .cshtml 文件扩展名。
  • 提供一种巧妙的方法来使用 C# 创建 HTML 输出。

当前,Index 方法返回一个字符串,其中包含控制器类中的消息。 在 HelloWorldController 类中,将 Index 方法替换为以下代码:

public IActionResult Index()
{
    return View();
}

前面的代码:

  • 调用该控制器的 View 方法。
  • 使用视图模板生成 HTML 响应。

控制器方法:

  • 称为“操作方法”。 例如,上述代码中的 Index 操作方法。
  • 通常返回 IActionResult 或从 ActionResult 派生的类,而不是 string 这样的类型。

添加视图

右键单击“视图”文件夹,然后单击“添加”>>“新文件夹”,并将文件夹命名为“HelloWorld”。

右键单击“Views/HelloWorld”文件夹,然后单击“添加”>“新项”。

在“添加新项 - MvcMovie”对话框中:

  • 在右上角的搜索框中,输入“视图”
  • 选择“Razor 视图 - 空”
  • 保持“名称”框的值:Index.cshtml
  • 选择“添加”

“添加新项”对话框

Views/HelloWorld/Index.cshtmlRazor 视图文件的内容替换为以下内容:

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

导航到 https://localhost:{PORT}/HelloWorld

  • HelloWorldController 中的 Index 方法运行 return View(); 语句,指定此方法应使用视图模板文件来呈现对浏览器的响应。

  • 由于未指定视图模板文件名称,因此 MVC 默认使用默认视图文件。 如果未指定视图文件名称,则返回默认视图。 默认视图与操作方法的名称相同,在本例中为 Index。 使用视图模板 /Views/HelloWorld/Index.cshtml

  • 下图显示了视图中硬编码的字符串“Hello from our View Template!”:

    浏览器窗口

更改视图和布局页面

选择菜单链接“MvcMovie”、“Home”和“Privacy”。 每页显示相同的菜单布局。 菜单布局在 Views/Shared/_Layout.cshtml 文件中实现。

打开 Views/Shared/_Layout.cshtml 文件。

布局模板允许:

  • 在一个位置指定站点的 HTML 容器布局。
  • 在该站点的多个页面上应用 HTML 容器布局。

查找 @RenderBody() 行。 RenderBody 是显示创建的所有特定于视图的页面的占位符,已包装在布局页面中。 例如,如果选择 Privacy 链接,Views/Home/Privacy.cshtml 视图将在 RenderBody 方法中呈现。

Views/Shared/_Layout.cshtml 文件的内容替换为以下标记。 突出显示所作更改:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Movie App</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container-fluid">
                <a class="navbar-brand" asp-area="" asp-controller="Movies" asp-action="Index">Movie App</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2022 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <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>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

上述标记进行以下更改:

  • MvcMovie 更改为 Movie App 三次。
  • 定位点元素 <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">MvcMovie</a> 更改为 <a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a>

在前面的标记中,省略了 asp-area="" 定位点标记帮助程序特性和特性值,因为此应用未使用区域

说明Movies 控制器尚未实现。 此时,Movie App 链接不起作用。

保存更改并选择“Privacy”链接。 请注意,浏览器标签页上的标题现在显示的是“Privacy 策略 - 电影应用”,而不是“Privacy 策略 - MvcMovie”

Privacy 选项卡

选择 Home 链接。

请注意,标题和定位点文本显示“电影应用”。 在布局模板中进行了一次更改,网站上的所有页面都反映新的链接文本和新标题。

检查 Views/_ViewStart.cshtml 文件:

@{
    Layout = "_Layout";
}

Views/_ViewStart.cshtml 文件将 Views/Shared/_Layout.cshtml 文件引入到每个视图中。 可以使用 Layout 属性设置不同的布局视图,或将它设置为 null,这样将不会使用任何布局文件。

打开 Views/HelloWorld/Index.cshtml 视图文件。

更改标题和 <h2> 元素,如以下突出显示:

@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

标题和 <h2> 元素略有不同,因此可以清楚地看出代码的哪一部分更改了显示。

上述代码中的 ViewData["Title"] = "Movie List";ViewData 字典的 Title 属性设置为“Movie List”。 Title 属性在布局页面中的 <title> HTML 元素中使用:

<title>@ViewData["Title"] - Movie App</title>

保存更改并导航到 https://localhost:{PORT}/HelloWorld

请注意,以下内容已更改:

  • 浏览器标题。
  • 主标题。
  • 二级标题。

如果浏览器中没有任何更改,则可能是正在查看的缓存内容。 在浏览器中按 Ctrl + F5 强制加载来自服务器的响应。 浏览器标题是使用我们在 Index.cshtml 视图模板中设置的 ViewData["Title"] 以及在布局文件中添加的额外“- Movie App”创建的。

Index.cshtml 视图模板中的内容与 Views/Shared/_Layout.cshtml 视图模板合并。 单个 HTML 响应将发送到浏览器。 凭借布局模板可以轻松地对应用中所有页面进行更改。 若要了解更多信息,请参阅布局

电影列表视图

但是,“数据”的一小部分(即“Hello from our View Template!”消息)是硬编码的。 MVC 应用程序有一个“V”(视图)和一个“C”(控制器),但还没有“M”(模型)。

将数据从控制器传递给视图

控制器操作会被调用以响应传入的 URL 请求。 控制器类是编写处理传入浏览器请求的代码的地方。 控制器从数据源检索数据,并决定将哪些类型的响应发送回浏览器。 可以从控制器使用视图模板来生成并格式化对浏览器的 HTML 响应。

控制器负责提供所需的数据,使视图模板能够呈现响应。

视图模板不应:

  • 执行业务逻辑
  • 直接与数据库交互。

视图模板应仅使用由控制器提供给它的数据。 保持此“分离关注点”有助于保持代码:

  • 干净。
  • 可测试。
  • 可维护。

目前,HelloWorldController 类中的 Welcome 方法采用 nameID 参数,然后将值直接输出到浏览器。

应将控制器更改为使用视图模板,而不是使控制器将此响应呈现为字符串。 视图模板会生成动态响应,这意味着必须将适当的数据从控制器传递给视图以生成响应。 为此,可以让控制器将视图模板所需的动态数据(参数)放置在 ViewData 字典中。 然后,视图模板可以访问动态数据。

HelloWorldController.cs 中,更改 Welcome 方法以将 MessageNumTimes 值添加到 ViewData 字典。

ViewData 字典是动态对象,这意味着任何类型都可以使用。 在添加某些内容之前,ViewData 对象没有已定义的属性。 MVC 模型绑定系统自动将命名参数 namenumTimes 从查询字符串映射到方法中的参数。 完整的 HelloWorldController

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers;

public class HelloWorldController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
    public IActionResult Welcome(string name, int numTimes = 1)
    {
        ViewData["Message"] = "Hello " + name;
        ViewData["NumTimes"] = numTimes;
        return View();
    }
}

ViewData 字典对象包含将传递给视图的数据。

创建一个名为 Views/HelloWorld/Welcome.cshtml 的 Welcome 视图模板。

Welcome.cshtml 视图模板中创建一个循环,显示“Hello”NumTimes。 将 Views/HelloWorld/Welcome.cshtml 的内容替换为以下内容:

@{
    ViewData["Title"] = "Welcome";
}

<h2>Welcome</h2>

<ul>
    @for (int i = 0; i < (int)ViewData["NumTimes"]!; i++)
    {
        <li>@ViewData["Message"]</li>
    }
</ul>

保存更改并浏览到以下 URL:

https://localhost:{PORT}/HelloWorld/Welcome?name=Rick&numtimes=4

数据取自 URL,并传递给使用 MVC 模型绑定器的控制器。 控制器将数据打包到 ViewData 字典中,并将该对象传递给视图。 然后,视图将数据作为 HTML 呈现给浏览器。

Privacy 视图,显示了 Welcome 标签以及四个“Hello Rick”短语

在前面的示例中,我们使用 ViewData 字典将数据从控制器传递给视图。 稍后在本教程中,我们将使用视图模型将数据从控制器传递给视图。 传递数据的视图模型方法比 ViewData 字典方法更为优先。

在下一个教程中,将创建电影数据库。

在此部分中,你将修改 HelloWorldController 类以使用 Razor 视图文件。 这顺利封装了为客户端生成 HTML 响应的过程。

视图模板是使用 Razor 创建的。 基于 Razor 的视图模板:

  • 具有 .cshtml 文件扩展名。
  • 提供一种巧妙的方法来使用 C# 创建 HTML 输出。

当前,Index 方法返回一个字符串,其中包含控制器类中的消息。 在 HelloWorldController 类中,将 Index 方法替换为以下代码:

public IActionResult Index()
{
    return View();
}

前面的代码:

  • 调用该控制器的 View 方法。
  • 使用视图模板生成 HTML 响应。

控制器方法:

  • 称为“操作方法”。 例如,上述代码中的 Index 操作方法。
  • 通常返回 IActionResult 或从 ActionResult 派生的类,而不是 string 这样的类型。

添加视图

右键单击“视图”文件夹,然后单击“添加”>>“新文件夹”,并将文件夹命名为“HelloWorld”。

右键单击“Views/HelloWorld”文件夹,然后单击“添加”>“新项”。

在“添加新项 - MvcMovie”对话框中:

  • 在右上角的搜索框中,输入“视图”
  • 选择“Razor 视图 - 空”
  • 保持“名称”框的值:Index.cshtml
  • 选择“添加”

“添加新项”对话框

Views/HelloWorld/Index.cshtmlRazor 视图文件的内容替换为以下内容:

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

导航到 https://localhost:{PORT}/HelloWorld

  • HelloWorldController 中的 Index 方法运行 return View(); 语句,指定此方法应使用视图模板文件来呈现对浏览器的响应。

  • 由于未指定视图模板文件名称,因此 MVC 默认使用默认视图文件。 如果未指定视图文件名称,则返回默认视图。 默认视图与操作方法的名称相同,在本例中为 Index。 使用视图模板 /Views/HelloWorld/Index.cshtml

  • 下图显示了视图中硬编码的字符串“Hello from our View Template!”:

    浏览器窗口

更改视图和布局页面

选择菜单链接“MvcMovie”、“Home”和“Privacy”。 每页显示相同的菜单布局。 菜单布局在 Views/Shared/_Layout.cshtml 文件中实现。

打开 Views/Shared/_Layout.cshtml 文件。

布局模板允许:

  • 在一个位置指定站点的 HTML 容器布局。
  • 在该站点的多个页面上应用 HTML 容器布局。

查找 @RenderBody() 行。 RenderBody 是显示创建的所有特定于视图的页面的占位符,已包装在布局页面中。 例如,如果选择 Privacy 链接,Views/Home/Privacy.cshtml 视图将在 RenderBody 方法中呈现。

Views/Shared/_Layout.cshtml 文件的内容替换为以下标记。 突出显示所作更改:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Movie App</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container-fluid">
                <a class="navbar-brand" asp-area="" asp-controller="Movies" asp-action="Index">Movie App</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2021 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <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>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

上述标记进行以下更改:

  • MvcMovie 更改为 Movie App 三次。
  • 定位点元素 <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">MvcMovie</a> 更改为 <a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a>

在前面的标记中,省略了 asp-area="" 定位点标记帮助程序特性和特性值,因为此应用未使用区域

说明Movies 控制器尚未实现。 此时,Movie App 链接不起作用。

保存更改并选择“Privacy”链接。 请注意,浏览器标签页上的标题现在显示的是“Privacy 策略 - 电影应用”,而不是“Privacy 策略 - MvcMovie”

Privacy 选项卡

选择 Home 链接。

请注意,标题和定位点文本显示“电影应用”。 在布局模板中进行了一次更改,网站上的所有页面都反映新的链接文本和新标题。

检查 Views/_ViewStart.cshtml 文件:

@{
    Layout = "_Layout";
}

Views/_ViewStart.cshtml 文件将 Views/Shared/_Layout.cshtml 文件引入到每个视图中。 可以使用 Layout 属性设置不同的布局视图,或将它设置为 null,这样将不会使用任何布局文件。

打开 Views/HelloWorld/Index.cshtml 视图文件。

更改标题和 <h2> 元素,如以下突出显示:

@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

标题和 <h2> 元素略有不同,因此可以清楚地看出代码的哪一部分更改了显示。

上述代码中的 ViewData["Title"] = "Movie List";ViewData 字典的 Title 属性设置为“Movie List”。 Title 属性在布局页面中的 <title> HTML 元素中使用:

<title>@ViewData["Title"] - Movie App</title>

保存更改并导航到 https://localhost:{PORT}/HelloWorld

请注意,以下内容已更改:

  • 浏览器标题。
  • 主标题。
  • 二级标题。

如果浏览器中没有任何更改,则可能是正在查看的缓存内容。 在浏览器中按 Ctrl + F5 强制加载来自服务器的响应。 浏览器标题是使用我们在 Index.cshtml 视图模板中设置的 ViewData["Title"] 以及在布局文件中添加的额外“- Movie App”创建的。

Index.cshtml 视图模板中的内容与 Views/Shared/_Layout.cshtml 视图模板合并。 单个 HTML 响应将发送到浏览器。 凭借布局模板可以轻松地对应用中所有页面进行更改。 若要了解更多信息,请参阅布局

电影列表视图

但是,“数据”的一小部分(即“Hello from our View Template!”消息)是硬编码的。 MVC 应用程序有一个“V”(视图)和一个“C”(控制器),但还没有“M”(模型)。

将数据从控制器传递给视图

控制器操作会被调用以响应传入的 URL 请求。 控制器类是编写处理传入浏览器请求的代码的地方。 控制器从数据源检索数据,并决定将哪些类型的响应发送回浏览器。 可以从控制器使用视图模板来生成并格式化对浏览器的 HTML 响应。

控制器负责提供所需的数据,使视图模板能够呈现响应。

视图模板不应:

  • 执行业务逻辑
  • 直接与数据库交互。

视图模板应仅使用由控制器提供给它的数据。 保持此“分离关注点”有助于保持代码:

  • 干净。
  • 可测试。
  • 可维护。

目前,HelloWorldController 类中的 Welcome 方法采用 nameID 参数,然后将值直接输出到浏览器。

应将控制器更改为使用视图模板,而不是使控制器将此响应呈现为字符串。 视图模板会生成动态响应,这意味着必须将适当的数据从控制器传递给视图以生成响应。 为此,可以让控制器将视图模板所需的动态数据(参数)放置在 ViewData 字典中。 然后,视图模板可以访问动态数据。

HelloWorldController.cs 中,更改 Welcome 方法以将 MessageNumTimes 值添加到 ViewData 字典。

ViewData 字典是动态对象,这意味着任何类型都可以使用。 在添加某些内容之前,ViewData 对象没有已定义的属性。 MVC 模型绑定系统自动将命名参数 namenumTimes 从查询字符串映射到方法中的参数。 完整的 HelloWorldController

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers
{
    public class HelloWorldController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Welcome(string name, int numTimes = 1)
        {
            ViewData["Message"] = "Hello " + name;
            ViewData["NumTimes"] = numTimes;

            return View();
        }
    }
}

ViewData 字典对象包含将传递给视图的数据。

创建一个名为 Views/HelloWorld/Welcome.cshtml 的 Welcome 视图模板。

Welcome.cshtml 视图模板中创建一个循环,显示“Hello”NumTimes。 将 Views/HelloWorld/Welcome.cshtml 的内容替换为以下内容:

@{
    ViewData["Title"] = "Welcome";
}

<h2>Welcome</h2>

<ul>
    @for (int i = 0; i < (int)ViewData["NumTimes"]!; i++)
    {
        <li>@ViewData["Message"]</li>
    }
</ul>

保存更改并浏览到以下 URL:

https://localhost:{PORT}/HelloWorld/Welcome?name=Rick&numtimes=4

数据取自 URL,并传递给使用 MVC 模型绑定器的控制器。 控制器将数据打包到 ViewData 字典中,并将该对象传递给视图。 然后,视图将数据作为 HTML 呈现给浏览器。

Privacy 视图,显示了 Welcome 标签以及四个“Hello Rick”短语

在前面的示例中,我们使用 ViewData 字典将数据从控制器传递给视图。 稍后在本教程中,我们将使用视图模型将数据从控制器传递给视图。 传递数据的视图模型方法比 ViewData 字典方法更为优先。

在下一个教程中,将创建电影数据库。

在此部分中,你将修改 HelloWorldController 类以使用 Razor 视图文件。 这顺利封装了为客户端生成 HTML 响应的过程。

视图模板是使用 Razor 创建的。 基于 Razor 的视图模板:

  • 具有 .cshtml 文件扩展名。
  • 提供一种巧妙的方法来使用 C# 创建 HTML 输出。

当前,Index 方法返回一个字符串,其中包含控制器类中的消息。 在 HelloWorldController 类中,将 Index 方法替换为以下代码:

public IActionResult Index()
{
    return View();
}

前面的代码:

  • 调用该控制器的 View 方法。
  • 使用视图模板生成 HTML 响应。

控制器方法:

  • 称为“操作方法”。 例如,上述代码中的 Index 操作方法。
  • 通常返回 IActionResult 或从 ActionResult 派生的类,而不是 string 这样的类型。

添加视图

右键单击“视图”文件夹,然后单击“添加”>>“新文件夹”,并将文件夹命名为“HelloWorld”。

右键单击“Views/HelloWorld”文件夹,然后单击“添加”>“新项”。

在“添加新项 - MvcMovie”对话框中:

  • 在右上角的搜索框中,输入“视图”
  • 选择“Razor 视图 - 空”
  • 保持“名称”框的值:Index.cshtml
  • 选择“添加”

“添加新项”对话框

Views/HelloWorld/Index.cshtmlRazor 视图文件的内容替换为以下内容:

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

导航到 https://localhost:{PORT}/HelloWorld

  • HelloWorldController 中的 Index 方法运行 return View(); 语句,指定此方法应使用视图模板文件来呈现对浏览器的响应。

  • 由于未指定视图模板文件名称,因此 MVC 默认使用默认视图文件。 如果未指定视图文件名称,则返回默认视图。 默认视图与操作方法的名称相同,在本例中为 Index。 使用视图模板 /Views/HelloWorld/Index.cshtml

  • 下图显示了视图中硬编码的字符串“Hello from our View Template!”:

    浏览器窗口

更改视图和布局页面

选择菜单链接“MvcMovie”、“Home”和“Privacy”。 每页显示相同的菜单布局。 菜单布局在 Views/Shared/_Layout.cshtml 文件中实现。

打开 Views/Shared/_Layout.cshtml 文件。

布局模板允许:

  • 在一个位置指定站点的 HTML 容器布局。
  • 在该站点的多个页面上应用 HTML 容器布局。

查找 @RenderBody() 行。 RenderBody 是显示创建的所有特定于视图的页面的占位符,已包装在布局页面中。 例如,如果选择 Privacy 链接,Views/Home/Privacy.cshtml 视图将在 RenderBody 方法中呈现。

Views/Shared/_Layout.cshtml 文件的内容替换为以下标记。 突出显示所作更改:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Movie App</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2020 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

上述标记进行以下更改:

  • MvcMovie 更改为 Movie App 三次。
  • 定位点元素 <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">MvcMovie</a> 更改为 <a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a>

在前面的标记中,省略了 asp-area="" 定位点标记帮助程序特性和特性值,因为此应用未使用区域

说明Movies 控制器尚未实现。 此时,Movie App 链接不起作用。

保存更改并选择“Privacy”链接。 请注意,浏览器标签页上的标题现在显示的是“Privacy 策略 - 电影应用”,而不是“Privacy 策略 - MvcMovie”

Privacy 选项卡

选择 Home 链接。

请注意,标题和定位点文本显示“电影应用”。 在布局模板中进行了一次更改,网站上的所有页面都反映新的链接文本和新标题。

检查 Views/_ViewStart.cshtml 文件:

@{
    Layout = "_Layout";
}

Views/_ViewStart.cshtml 文件将 Views/Shared/_Layout.cshtml 文件引入到每个视图中。 可以使用 Layout 属性设置不同的布局视图,或将它设置为 null,这样将不会使用任何布局文件。

打开 Views/HelloWorld/Index.cshtml 视图文件。

更改标题和 <h2> 元素,如以下突出显示:

@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

标题和 <h2> 元素略有不同,因此可以清楚地看出代码的哪一部分更改了显示。

上述代码中的 ViewData["Title"] = "Movie List";ViewData 字典的 Title 属性设置为“Movie List”。 Title 属性在布局页面中的 <title> HTML 元素中使用:

<title>@ViewData["Title"] - Movie App</title>

保存更改并导航到 https://localhost:{PORT}/HelloWorld

请注意,以下内容已更改:

  • 浏览器标题。
  • 主标题。
  • 二级标题。

如果浏览器中没有任何更改,则可能是正在查看的缓存内容。 在浏览器中按 Ctrl + F5 强制加载来自服务器的响应。 浏览器标题是使用我们在 Index.cshtml 视图模板中设置的 ViewData["Title"] 以及在布局文件中添加的额外“- Movie App”创建的。

Index.cshtml 视图模板中的内容与 Views/Shared/_Layout.cshtml 视图模板合并。 单个 HTML 响应将发送到浏览器。 凭借布局模板可以轻松地对应用中所有页面进行更改。 若要了解更多信息,请参阅布局

电影列表视图

但是,“数据”的一小部分(即“Hello from our View Template!”消息)是硬编码的。 MVC 应用程序有一个“V”(视图)和一个“C”(控制器),但还没有“M”(模型)。

将数据从控制器传递给视图

控制器操作会被调用以响应传入的 URL 请求。 控制器类是编写处理传入浏览器请求的代码的地方。 控制器从数据源检索数据,并决定将哪些类型的响应发送回浏览器。 可以从控制器使用视图模板来生成并格式化对浏览器的 HTML 响应。

控制器负责提供所需的数据,使视图模板能够呈现响应。

视图模板不应:

  • 执行业务逻辑
  • 直接与数据库交互。

视图模板应仅使用由控制器提供给它的数据。 保持此“分离关注点”有助于保持代码:

  • 干净。
  • 可测试。
  • 可维护。

目前,HelloWorldController 类中的 Welcome 方法采用 nameID 参数,然后将值直接输出到浏览器。

应将控制器更改为使用视图模板,而不是使控制器将此响应呈现为字符串。 视图模板会生成动态响应,这意味着必须将适当的数据从控制器传递给视图以生成响应。 为此,可以让控制器将视图模板所需的动态数据(参数)放置在 ViewData 字典中。 然后,视图模板可以访问动态数据。

HelloWorldController.cs 中,更改 Welcome 方法以将 MessageNumTimes 值添加到 ViewData 字典。

ViewData 字典是动态对象,这意味着任何类型都可以使用。 在添加某些内容之前,ViewData 对象没有已定义的属性。 MVC 模型绑定系统自动将命名参数 namenumTimes 从查询字符串映射到方法中的参数。 完整的 HelloWorldController

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers
{
    public class HelloWorldController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Welcome(string name, int numTimes = 1)
        {
            ViewData["Message"] = "Hello " + name;
            ViewData["NumTimes"] = numTimes;

            return View();
        }
    }
}

ViewData 字典对象包含将传递给视图的数据。

创建一个名为 Views/HelloWorld/Welcome.cshtml 的 Welcome 视图模板。

Welcome.cshtml 视图模板中创建一个循环,显示“Hello”NumTimes。 将 Views/HelloWorld/Welcome.cshtml 的内容替换为以下内容:

@{
    ViewData["Title"] = "Welcome";
}

<h2>Welcome</h2>

<ul>
    @for (int i = 0; i < (int)ViewData["NumTimes"]; i++)
    {
        <li>@ViewData["Message"]</li>
    }
</ul>

保存更改并浏览到以下 URL:

https://localhost:{PORT}/HelloWorld/Welcome?name=Rick&numtimes=4

数据取自 URL,并传递给使用 MVC 模型绑定器的控制器。 控制器将数据打包到 ViewData 字典中,并将该对象传递给视图。 然后,视图将数据作为 HTML 呈现给浏览器。

Privacy 视图,显示了 Welcome 标签以及四个“Hello Rick”短语

在前面的示例中,我们使用 ViewData 字典将数据从控制器传递给视图。 稍后在本教程中,我们将使用视图模型将数据从控制器传递给视图。 传递数据的视图模型方法比 ViewData 字典方法更为优先。

在下一个教程中,将创建电影数据库。

在此部分中,你将修改 HelloWorldController 类以使用 Razor 视图文件。 这顺利封装了为客户端生成 HTML 响应的过程。

视图模板是使用 Razor 创建的。 基于 Razor 的视图模板:

  • 具有 .cshtml 文件扩展名。
  • 提供一种巧妙的方法来使用 C# 创建 HTML 输出。

当前,Index 方法返回一个字符串,其中包含控制器类中的消息。 在 HelloWorldController 类中,将 Index 方法替换为以下代码:

public IActionResult Index()
{
    return View();
}

前面的代码:

  • 调用该控制器的 View 方法。
  • 使用视图模板生成 HTML 响应。

控制器方法:

  • 称为“操作方法”。 例如,上述代码中的 Index 操作方法。
  • 通常返回 IActionResult 或从 ActionResult 派生的类,而不是 string 这样的类型。

添加视图

右键单击“视图”文件夹,然后单击“添加”>>“新文件夹”,并将文件夹命名为“HelloWorld”。

右键单击“Views/HelloWorld”文件夹,然后单击“添加”>“新项”。

在“添加新项 - MvcMovie”对话框中:

  • 在右上角的搜索框中,输入“视图”
  • 选择“Razor 视图 - 空”
  • 保持“名称”框的值:Index.cshtml
  • 选择“添加”

“添加新项”对话框

Views/HelloWorld/Index.cshtmlRazor 视图文件的内容替换为以下内容:

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

导航到 https://localhost:{PORT}/HelloWorld

  • HelloWorldController 中的 Index 方法运行 return View(); 语句,指定此方法应使用视图模板文件来呈现对浏览器的响应。

  • 由于未指定视图模板文件名称,因此 MVC 默认使用默认视图文件。 如果未指定视图文件名称,则返回默认视图。 默认视图与操作方法的名称相同,在本例中为 Index。 使用视图模板 /Views/HelloWorld/Index.cshtml

  • 下图显示了视图中硬编码的字符串“Hello from our View Template!”:

    浏览器窗口

更改视图和布局页面

选择菜单链接“MvcMovie”、“Home”和“Privacy”。 每页显示相同的菜单布局。 菜单布局在 Views/Shared/_Layout.cshtml 文件中实现。

打开 Views/Shared/_Layout.cshtml 文件。

布局模板允许:

  • 在一个位置指定站点的 HTML 容器布局。
  • 在该站点的多个页面上应用 HTML 容器布局。

查找 @RenderBody() 行。 RenderBody 是显示创建的所有特定于视图的页面的占位符,已包装在布局页面中。 例如,如果选择 Privacy 链接,Views/Home/Privacy.cshtml 视图将在 RenderBody 方法中呈现。

Views/Shared/_Layout.cshtml 文件的内容替换为以下标记。 突出显示所作更改:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Movie App</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2020 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @RenderSection("Scripts", required: false)
</body>
</html>

上述标记进行以下更改:

  • MvcMovie 更改为 Movie App 三次。
  • 定位点元素 <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">MvcMovie</a> 更改为 <a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a>

在前面的标记中,省略了 asp-area="" 定位点标记帮助程序特性和特性值,因为此应用未使用区域

说明Movies 控制器尚未实现。 此时,Movie App 链接不起作用。

保存更改并选择“Privacy”链接。 请注意,浏览器标签页上的标题现在显示的是“Privacy 策略 - 电影应用”,而不是“Privacy 策略 - MvcMovie”

Privacy 选项卡

选择 Home 链接。

请注意,标题和定位点文本显示“电影应用”。 在布局模板中进行了一次更改,网站上的所有页面都反映新的链接文本和新标题。

检查 Views/_ViewStart.cshtml 文件:

@{
    Layout = "_Layout";
}

Views/_ViewStart.cshtml 文件将 Views/Shared/_Layout.cshtml 文件引入到每个视图中。 可以使用 Layout 属性设置不同的布局视图,或将它设置为 null,这样将不会使用任何布局文件。

打开 Views/HelloWorld/Index.cshtml 视图文件。

更改标题和 <h2> 元素,如以下突出显示:

@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

标题和 <h2> 元素略有不同,因此可以清楚地看出代码的哪一部分更改了显示。

上述代码中的 ViewData["Title"] = "Movie List";ViewData 字典的 Title 属性设置为“Movie List”。 Title 属性在布局页面中的 <title> HTML 元素中使用:

<title>@ViewData["Title"] - Movie App</title>

保存更改并导航到 https://localhost:{PORT}/HelloWorld

请注意,以下内容已更改:

  • 浏览器标题。
  • 主标题。
  • 二级标题。

如果浏览器中没有任何更改,则可能是正在查看的缓存内容。 在浏览器中按 Ctrl + F5 强制加载来自服务器的响应。 浏览器标题是使用我们在 Index.cshtml 视图模板中设置的 ViewData["Title"] 以及在布局文件中添加的额外“- Movie App”创建的。

Index.cshtml 视图模板中的内容与 Views/Shared/_Layout.cshtml 视图模板合并。 单个 HTML 响应将发送到浏览器。 凭借布局模板可以轻松地对应用中所有页面进行更改。 若要了解更多信息,请参阅布局

电影列表视图

但是,“数据”的一小部分(即“Hello from our View Template!”消息)是硬编码的。 MVC 应用程序有一个“V”(视图)和一个“C”(控制器),但还没有“M”(模型)。

将数据从控制器传递给视图

控制器操作会被调用以响应传入的 URL 请求。 控制器类是编写处理传入浏览器请求的代码的地方。 控制器从数据源检索数据,并决定将哪些类型的响应发送回浏览器。 可以从控制器使用视图模板来生成并格式化对浏览器的 HTML 响应。

控制器负责提供所需的数据,使视图模板能够呈现响应。

视图模板不应:

  • 执行业务逻辑
  • 直接与数据库交互。

视图模板应仅使用由控制器提供给它的数据。 保持此“分离关注点”有助于保持代码:

  • 干净。
  • 可测试。
  • 可维护。

目前,HelloWorldController 类中的 Welcome 方法采用 nameID 参数,然后将值直接输出到浏览器。

应将控制器更改为使用视图模板,而不是使控制器将此响应呈现为字符串。 视图模板会生成动态响应,这意味着必须将适当的数据从控制器传递给视图以生成响应。 为此,可以让控制器将视图模板所需的动态数据(参数)放置在 ViewData 字典中。 然后,视图模板可以访问动态数据。

HelloWorldController.cs 中,更改 Welcome 方法以将 MessageNumTimes 值添加到 ViewData 字典。

ViewData 字典是动态对象,这意味着任何类型都可以使用。 在添加某些内容之前,ViewData 对象没有已定义的属性。 MVC 模型绑定系统自动将命名参数 namenumTimes 从查询字符串映射到方法中的参数。 完整的 HelloWorldController

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers
{
    public class HelloWorldController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Welcome(string name, int numTimes = 1)
        {
            ViewData["Message"] = "Hello " + name;
            ViewData["NumTimes"] = numTimes;

            return View();
        }
    }
}

ViewData 字典对象包含将传递给视图的数据。

创建一个名为 Views/HelloWorld/Welcome.cshtml 的 Welcome 视图模板。

Welcome.cshtml 视图模板中创建一个循环,显示“Hello”NumTimes。 将 Views/HelloWorld/Welcome.cshtml 的内容替换为以下内容:

@{
    ViewData["Title"] = "Welcome";
}

<h2>Welcome</h2>

<ul>
    @for (int i = 0; i < (int)ViewData["NumTimes"]; i++)
    {
        <li>@ViewData["Message"]</li>
    }
</ul>

保存更改并浏览到以下 URL:

https://localhost:{PORT}/HelloWorld/Welcome?name=Rick&numtimes=4

数据取自 URL,并传递给使用 MVC 模型绑定器的控制器。 控制器将数据打包到 ViewData 字典中,并将该对象传递给视图。 然后,视图将数据作为 HTML 呈现给浏览器。

Privacy 视图,显示了 Welcome 标签以及四个“Hello Rick”短语

在前面的示例中,我们使用 ViewData 字典将数据从控制器传递给视图。 稍后在本教程中,我们将使用视图模型将数据从控制器传递给视图。 传递数据的视图模型方法比 ViewData 字典方法更为优先。

在下一个教程中,将创建电影数据库。