概述

本节介绍视图以及如何使用视图渲染模型。

主要内容:

  • 什么是视图

  • 视图的概念

  • Razor 视图引擎的基本语法

  • 简单视图的渲染

  • 渲染模型

什么是视图

讨论视图前,让我们先看看下面四个 Action:

  1. public class SampleController : Controller
  2. {
  3. public IList<Product> GetAllProducts()
  4. {
  5. IList<Product> products = dbContext.GetAllProducts();
  6. return products;
  7. }
  8. public IActionResult GetAllProducts()
  9. {
  10. IList<Product> products = dbContext.GetAllProducts();
  11. return new JsonResult(products);
  12. }
  13. public IActionResult GetAllProducts()
  14. {
  15. IList<Product> products = dbContext.GetAllProducts();
  16. return new Json(products);
  17. }
  18. public IActionResult GetAllProducts()
  19. {
  20. IList<Product> products = dbContext.GetAllProducts();
  21. return new View("ProductList", products);
  22. }
  23. }

前三个 Action 与最后一个的区别在于,从数据库获取产品列表后,前三个 Action 将产品数据直接返回(数据被隐式或显式序列化为 JSON 字符串)到 Web 客户端(浏览器)。最后一个 Action 使用名为 ProductList 的视图来渲染该产品列表并生成 HTML 内容,再将 HTML 内容返回到 Web 客户端。

因此,Web 客户端从前三个 Action 接收到的内容大致如下:

  1. [
  2. {"ID":101, "Name":"Book", "Price":19.99},
  3. {"ID":102, "Name":"Bike", "Price":29.99},
  4. {"ID":103, "Name":"Fireworks", "Price":39.99}
  5. ]

最后一个 Action 发送的内容大致如下:

  1. ...
  2. <th>
  3. <td>ID</td>
  4. <td>Name</td>
  5. <td>Price</td>
  6. </th>
  7. <tr>
  8. <td>101</td>
  9. <td>Book</td>
  10. <td>19.99</td>
  11. </tr>
  12. <tr>
  13. <td>102</td>
  14. <td>Bike</td>
  15. <td>29.99</td>
  16. </tr>
  17. <tr>
  18. <td>103</td>
  19. <td>Fireworks</td>
  20. <td>39.99</td>
  21. </tr>
  22. ...

简而言之,前三个都是 Web API Action,只返回数据。只有最后一个才是 Web 程序 Action,Action 中使用视图渲染数据并生成 HTML 内容。

基本上,在 MVC 模式中,视图是具有嵌入视图逻辑代码的 HTML 模板。发送回 Web 客户端的 HTML 内容由视图生成。 ASP.NET Core 中执行渲染作业,使用视图和模型生成 HTML 和内容的组件称为视图引擎。 ASP.NET Core 的视图引擎名为 Razor。

在本节的剩余部分,我们将着重介绍如何创建视图和渲染模型。

视图和 Razor 引擎

ASP.NET Core 视图文件的扩展名是 .cshtml,使用 Razor 语法进行编写。Razor 语法是一种标记语法,用于将基于服务器(sever-based)的代码嵌入到网页中。Razor 语法包括 Razor 标记、C# 和 HTML。

在旧的 CGI 规范中,我们是将 HTML 代码嵌入到 C# 中。如今在视图(.cshtml)文件中,我们是在 HTML 代码中嵌入 C# 代码。这让开发人员能更加直观的掌控将要生成的 HTML 的外观,并避免了以前那种在业务逻辑代码(C#)中到处插 HTML 代码。

Razor 引擎将 .cshtml 文件中的内容将转换为视图类(该类派生自 Microsoft.AspNetCore.Mvc.Razor.RazorPage<TModel>)。Razor 语法内容(C# + HTML)将被转换为视图类的方法主体。最后,当调用实例方法 ExecuteAsync 时,将生成最终的 HTML 内容并通过 HTTP 响应将其发送回用户的 Web 浏览器。

在讲解 Razor 语法前,请先完成 Razor 基础实验,体验一下 Razor 语法。

Razor 视图文件中只有两种代码:

  1. 静态文本:代码由 HTML 标签和纯文本组成。它们不可执行,构成了视图的骨架

  2. 可执行代码:@ 后面的 C# 代码块或一段“C# like”的可执行代码。代码将在呈现视图时执行

Razor 关键字

很多 ASP.NET Core 程序员认为 Razor 中的可执行代码就是 C# 代码,这种看法有失偏颇。Razor 有自己的关键字和语法解析系统。但为了缩小学习曲线,Razor 确实借用了 C# 的许多概念和关键字。

Razor 关键字可以分为两组:

  1. 纯 Razor 关键字:model, functions, inherits, section, helper

  2. C# Razor 关键字:using, for, foreach, if, else, while, do, switch, case, default, try, catch, finally

注:ASP.NET Core 不支持 helper 关键字。

纯 Razor 关键字都有与视图相关的特殊功能。新手只需了解 model 这一个关键字。model 用于指定视图的视图模型类型。即 Microsoft.AspNetCore.Mvc.Razor.RazorPage<TModel> 中的 <TModel>

C# Razor 关键字来自 C#。它们在功能上与 C# 关键字非常相似。你只要理解了 Razor 基础实验中的 foreach,别的都是一通百通。Docs 有完整的 C# Razor 关键字示例。

C# Razor 关键字与 C# 关键字的细微差异:

  1. 在声明、指令或表达式的末尾无需跟 ; 号。例如 using 指令和 model 指令

  2. 它允许 Razor 表达式出现在语句块中。例如 foreach 语句中的 @p.ID 和 @p.Name

顺便说一句,可以将 using 指令和 model 指令合并为一个指令。例如 @model MyWebApp.Models.ProductListVM

C# 代码块

虽然不推荐,但若有必要,你可以在视图中编写 C# 代码。格式是:

  1. @{
  2. C#代码块
  3. }

注:在 C# 代码块中声明的变量的作用域是整个视图。

到目前为止,Razor 不支持在 C# 代码块中直接声明函数,但可以通过 function 关键字声明函数:

  1. @functions{
  2. string GetOffString(double rate) {
  3. var offStr = $"{(1 - rate) * 100}%";
  4. return offStr;
  5. }
  6. }

Razor 表达式

Razor 表达式是 @( … ) 中的 C# 表达式。

默认情况下,Razor 引擎将所有文本识别为纯文本或 HTML 文本。在纯文本或 HTML 文本中,都可以嵌入 Razor 表达式。在渲染视图时将计算 Razor 表达式。最终表达式计算出的结果值将取代 Razor 表达式,成为 HTML 内容的一部分。

例如,Razor 代码 <b>@(3.14*5*5*10)</b>,将被渲染为 <b>785</b>

当 C# 表达式很简单时可以省略括号。例如,若产品名称为 Rocket,则 Razor 代码 <input id="name" name="name" value="@product.name"/> 将渲染为 <input id="name" name="name" value="Rocket"/>

注:ASP.NET Core 新手常犯的两个错误:

  1. 在 Razor 表达式末尾加 ; 号

    1. Razor 引擎会将末尾 ; 号识别为纯文本
  2. 应该用 Razor 表达式(@(…))时,错误用成了 Razor C# 代码块(@{ … })

    1. Razor 表达式将计算为值,代码块不会

搜索视图

Action 中调用 View 方法触发视图渲染。除非指定视图文件,否则调用 View 时会先去本控制器对应的视图文件夹里面搜索视图,找不到再去 Shared 共享文件夹里面搜索。

例如:

  1. public class HomeController : Controller
  2. {
  3. public IActionResult Index()
  4. {
  5. return View();
  6. }
  7. public IActionResult Index()
  8. {
  9. return View("Index");
  10. }
  11. public IActionResult Index()
  12. {
  13. return View("~/Views/Home/Index.cshtml");
  14. }
  15. }
  1. 第一个 Action中,Razor 引擎将查找项目根目录下的 Views 文件夹。如果 Views 文件夹存在,Razor 引擎将尝试查找与本控制器同名的子文件夹。如果子文件夹存在,Razor 引擎再尝试查找与 Action 同名的视图文件。简言之,渲染 ~\Views\Home\Index.cshtml

  2. 第二个 Action 中,Razor 引擎以 Index 字符串为参数,先搜索项目根目录下的 Views 文件夹,再搜索 Home 子文件夹,最后搜索 Index.cshtml 文件

  3. 第三个 Action 中,Razor 引擎以相对路径为参数,直接查找 ~\Views\Home\Index.cshtml 文件

前两个与第三个的区别在于,前两个在 ~\Views\Home\ 里面找不到 Index.cshtml 时,会再去 ~\Views\Shared 中搜索 Index.cshtml 文件。第三个不会,找不到就直接报错。