ASP.NET Core视图概述

原文:Views Overview
作者:Steve Smith
翻译:姚阿勇(Dr.Yao)
校对:高嵩(Jack)

ASP.NET MVC Core 的控制器可以利用 视图 返回格式化结果。

什么是视图?

在模型-视图-控制器(MVC)模式中,视图 封装用户与应用交互的表现细节。视图是带有嵌入代码的 HTML 模版,用以生成发送给客户端的内容。视图采用 Razor 语法,该语法允许以最小的代码量或复杂度与 HTML 进行编码交互。

ASP.NET Core MVC 视图默认以 .cshtml 文件保存在应用程序的 Views 文件夹里面。通常,每个控制器都会有自己的文件夹,里面是对应控制器操作的视图。

除了对应操作的视图,局部视图布局,以及其他特定视图文件可以用来帮助减少重复并允许在应用视图里重用。

使用视图的好处

视图在 MVC 应用中提供关注点分离,将用户界面层级的标记从业务逻辑中封装出来。ASP.NET MVC 视图采用Razor 语法在 HTML 标记和服务端逻辑之间进行轻松切换。通常,可以通过布局与共享指令或者局部视图对应用的用户界面中重复的外观轻松地进行复用。

创建视图

属于某个控制器的视图创建在 Views/[ControllerName] 文件夹下。在控制器之间共用的视图则放在 /Views/Shared 文件夹下。将视图文件命名为与其关联的控制器操作一样的名字,并添加 .cshtml 文件扩展名。例如,为 Home 控制器的 About 操作创建一个视图,你应该在 /Views/Home 文件夹下创建一个 About.cshtml 文件。

一个示例视图文件 ( About.cshtml ):

@{
    ViewData["Title"] = "About";
}<h2>@ViewData["Title"].</h2><h3>@ViewData["Message"]</h3><p>Use this area to provide additional information.</p>

@ 符号代表 Razor 代码。 C# 语句在大括号( {} )包裹的 Razor 代码块中运行,就像上面展示的用 “About” 给 ViewData["Title"] 元素进行的赋值操作那样。Razor 可以通过简单地用 @ 符号对值进行引用从而在 HTML 里显示它们,就像上面 <h2><h3> 元素里面展示的那样。

这个视图只关心由它负责的这部分输出。而页面布局的其余部分,以及视图中的通用外观,则在别的地方指定。了解更多关于布局与共享视图逻辑

控制器如何指定视图?

视图通常作为一个ViewResult从操作中返回。你的操作方法可以直接返回一个 ViewResult ,但是更常见的是如果你的控制器是继承自Controller的,那么可以简单地使用 View 辅助方法,如下例所示:

HomeController.cs

public IActionResult About()
{
    ViewData["Message"] = "Your application description page.";

    return View(); //手动高亮
}

这个 View 辅助方法有多个重载版本以便于帮助应用开发人员返回视图。你可以有选择性地指定一个返回的视图,还可以给视图传递一个模型对象。

当这个操作返回时,上面展示的 About.cshtml 视图将会被渲染:

视图发现

当操作返回视图的时,会进行一个叫做 视图发现 的过程。这个过程决定哪个视图文件将被采用。如果没有指定特定的视图文件,运行时首先会寻找与控制器对应的视图,然后再去 Shared 文件夹里寻找匹配的视图名称。

当操作返回 View 方法,就像 return View(); 这样,这个操作的名字则被用作视图名称。例如,假如这是从一个叫做 “ Index ” 的操作方法调用的,那么它就等价于传递了一个视图名称 “ Index ” 。也可以给这个方法传递一个明确的视图名称( return View("SomeView"); )。在这两种情况中,视图探寻都会在以下位置搜索匹配的视图文件:

  1. Views/

    <控制器名称>/<视图名称>.cshtml
  2. Views/Shared/

    <视图名称>.cshtml

我们推荐遵循约定,在可能的情况下简单地从操作中返回 View() ,这样会更加灵活,更易于重构代码。

可以提供视图文件路径,而非视图名。在这种情况下,.cshtml 扩展名必须作为文件路径的一部分明确指定。路径可以是相对于应用程序根目录的(可以选择性地以 “ / ” 或者 “ ~/ ” 开头)。例如: return View("Views/Home/About.cshtml");

局部视图以及视图组件采用了类似(但不完全一致)的发现机制。

你可以通过自定义的IViewLocationExpander来定制关于应用中的视图位于哪里的默认约定。

取决于基本文件系统,视图名称可能会区分大小写。为了跨系统的兼容性,应当总是保持控制器与操作名称同相关联的视图文件夹与文件名之间保持大小写一致。

给视图传递数据

你可以使用多种机制给视图传递数据。最健壮的方式就是在视图中指定一个 模型 类型(通常称为 视图模型 ,以区别于业务领域的模型类型 ),然后从操作中给视图传递一个该类型的实例。我们推荐你采用模型或视图模型给视图传递数据。这使得视图可以利用到强类型检查的优势。你可以通过 @model 指令为视图指定一个模型:

@model WebApplication1.ViewModels.Address // 手动高亮
<h2>Contact</h2>
<address>
    @Model.Street<br />
    @Model.City, @Model.State @Model.PostalCode<br />
    <abbr title="Phone">P:</abbr>
    425.555.0100
</address>

一旦为视图指定了模型,就可以像上面展示的那样,通过 @Model 以强类型的方式访问发送给视图的实例。为了给视图提供模型类型的实例,控制器将其作为参数传递进去:

public IActionResult Contact()
{
    ViewData["Message"] = "Your contact page.";

    var viewModel = new Address()
    {
        Name = "Microsoft",
        Street = "One Microsoft Way",
        City = "Redmond",
        State = "WA",
        PostalCode = "98052-6399"
    };
    return View(viewModel); // 手动高亮
}

对于能够作为模型提供给视图的类型没有限制。我们推荐传递具有少量行为或者没有行为的普通旧 CLR 对象(Plain Old CLR Object,POCO)视图模型,这样就可以在应用的其他地方封装业务逻辑。这种方法的一个例子就是上面示例中的 Address 视图模型:

namespace WebApplication1.ViewModels
{
    public class Address
    {
        public string Name { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string PostalCode { get; set; }
    }
}

虽然你可以使用相同的类作为你的业务模型类型和显示模型类型。然而,将它们与你的领域或持久模型区分开来可以使视图独立地变化,并且还可以提供一些安全上的收益(对于那些用户通过 模型绑定发送给应用的模型)。

弱类型数据

除了强类型视图之外,所有的视图都可以访问弱类型的数据集合。这个集合可以通过控制器和视图的 ViewData 或者 ViewBag 属性来引用。ViewBag 属性是对 ViewData 的封装用以在集合上提供动态视图。它不是一个独立的集合。

ViewData 是一个通过 string 键访问的字典对象。你可以在里面储存和查询对象,并且在提取它们的时候无需转换成特定的类型。可以利用 ViewData 从控制器传递数据给视图,以及在视图之间(还有局部视图与布局)。字符串数据可以直接储存和使用,无需进行转换。

在操作中为 ViewData 设置一些值:

public IActionResult SomeAction()
{
    ViewData["Greeting"] = "Hello";
    ViewData["Address"]  = new Address()
    {
        Name = "Steve",
        Street = "123 Main St",
        City = "Hudson",
        State = "OH",
        PostalCode = "44236"
    };
    
    return View();
}

在视图中使用数据:

    @{
        // Requires cast
        var address = ViewData["Address"] as Address; // 手动高亮
    }

    @ViewData["Greeting"] World! // 手动高亮

    <address>
        @address.Name<br />
        @address.Street<br />
        @address.City, @address.State @address.PostalCode
    </address>

ViewBag 对象为储存在 ViewData 里的对象提供动态访问。这样使用起来就更方便了,因为不需要转换。与上面的例子一样,在视图中采用了 ViewBag 而不是强类型的 Address 实例:

@ViewBag.Greeting World! // 手动高亮<address>
    @ViewBag.Address.Name<br/> // 手动高亮
    @ViewBag.Address.Street<br/> // 手动高亮
    @ViewBag.Address.City, @ViewBag.Address.State @ViewBag.Address.PostalCode // 手动高亮</address>

由于二者引用的是相同的底层 ViewData 集合,在你读取和写入值的时候,可以根据方便与否来协调混用 ViewDataViewBag

动态视图

对于没有声明模型类型但给它们传递了模型实例的视图,可以动态地引用这个实例。例如,如果将一个 Address 实例传给了一个并没有声明 @model 的视图,那么这个视图还是能够像下面展示的那样去引用这个实例的属性:

<address>
    @Model.Street<br/>
    @Model.City, @Model.State @Model.PostalCode<br/><abbr title="Phone">P:</abbr>
    425.555.0100</address>

这种特性能提供一些灵活性,但无法提供编译保护和智能提示。如果属性并不存在,页面将在运行时出错。

更多视图特性

Tag helpers便于给已有的 HTML 标记添加服务端行为,不需要视图中的自定义代码或助手代码。Tag helper 是作为 HTML 元素的属性启用的,会被不认识它们的编辑器忽略掉,使得视图标签可以被很多的工具编辑和渲染。Tag helper 有很多用途,尤其是非常便于使用表单

可以用很多内置的HTML Helpers生成自定义的 HTML 标记,更复杂的 UI 逻辑(可能有它自己的数据需求)可以在 View Components中封装。与控制器和视图一样,视图组件也提供关注点分离,并且无需操作和视图就可以处理通用 UI 元素用到的数据。

与 ASP.NET Core 的很多方面一样,视图也支持依赖注入,允许将服务注入到视图