第3章 视图

视图的作用与基础知识

控制器需要向视图提供一些信息, 所以它会传递一个数据转移对象,叫做模型。视图将这个模型转化为适合显示 给用户的格式。

在 ASP.NET  MVC中, 完成这一过程由两部分操作。其中一个是检查由控制器提交的模型对象,另一个是将其内容转换为HTML格式。

一个控制器相当于按某个功能划分出来的模块, 它可以对应多个视图。

如:Home 下的:

1. Index.cshtml

@{
    ViewBag.Title = "Home Page";
}

<div class="jumbotron">
    <h1>ASP.NET</h1>
    <p class="lead">ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS and JavaScript.</p>
    <p><a href="http://asp.net" class="btn btn-primary btn-lg">Learn more »</a></p>
</div>

<div class="row">
    <div class="col-md-4">
        <h2>Getting started</h2>
        <p>
            ASP.NET MVC gives you a powerful, patterns-based way to build dynamic websites that
            enables a clean separation of concerns and gives you full control over markup
            for enjoyable, agile development.
        </p>
        <p><a class="btn btn-default" href="http://go.microsoft.com/fwlink/?LinkId=301865">Learn more »</a></p>
    </div>
    <div class="col-md-4">                                                       
        <h2>Get more libraries</h2>
        <p>NuGet is a free Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects.</p>
        <p><a class="btn btn-default" href="http://go.microsoft.com/fwlink/?LinkId=301866">Learn more »</a></p>
    </div>
    <div class="col-md-4">
        <h2>Web Hosting</h2>
        <p>You can easily find a web hosting company that offers the right mix of features and price for your applications.</p>
        <p><a class="btn btn-default" href="http://go.microsoft.com/fwlink/?LinkId=301867">Learn more »</a></p>
    </div>
</div>

2. About.cshtml

@{
    ViewBag.Title = "About";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>

<p>Use this area to provide additional information.</p>

3. Contact.cshtml

@{
    ViewBag.Title = "Contact";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>

<address>
    One Microsoft Way<br />
    Redmond, WA 98052-6399<br />
    <abbr title="Phone">P:</abbr>
    425.555.0100
</address>

<address>
    <strong>Support:</strong>   <a href="mailto:Support@example.com">Support@example.com</a><br />
    <strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
</address>


HomeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcMusicStore.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";

            return View(); 
        }

        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
    }
}

视图相当于模板引擎。ViewBag 适合于传递简单数据。

控制器中的模型,传递到视图, 结合那些静态的html, 就形成了最终的显示效果。


控制器与视图的约定



控制器中的每一个方法默认对应视图中的同名视图。

如果希望Index方法渲染一个不同的视图,则应该写:

        public ActionResult Index()
        {
            return View("NoIndex");//最终会查找:NoIndex.cshtml
        }

如果希望Index渲染其它目录下的视图,则应该写:

        public ActionResult Index()
        {
            return View("~/Views/Example/Index.cshtml");//最终会查找:~/Views/Example/Index.cshtml
        }

强类型视图

Store 中方法:
        public ActionResult List() 
        {
            var albums = new List<Album>();
            for (int i = 0; i < 10; i++) 
            {
                albums.Add(new Album { Title = "Product " + i  });
            }
            ViewBag.Albums = albums;
            return View();
        }

List视图:
    <ul>
        @foreach (MvcMusicStore.Models.Album a in (ViewBag.Albums as IEnumerable<MvcMusicStore.Models.Album>)) { 
            <li>@a.Title</li>
        }
    </ul>
注意:需要将 ViewBag.Albums 转换为 IEnumerable<MvcMusicStore.Models.Album>)
为了使代码整洁, 也可以用 dynamic:
    <ul>
        @foreach (dynamic a in (ViewBag.Albums as IEnumerable<MvcMusicStore.Models.Album>)) { 
            <li>@a.Title</li>
        }
    </ul>
但是这样就没有智能感知了。

对于Controller 可以通过向重载的View 方法中传递模型实例来指定模型, 代码如下所示:
        public ActionResult List() 
        {
            var albums = new List<Album>();
            for (int i = 0; i < 10; i++) 
            {
                albums.Add(new Album { Title = "Product " + i  });
            }
            ViewBag.Albums = albums;
            return View(albums);    //原来是 View();
        }

下一步是告知哪种类型的模型正在使用 @model 声明。 注意这里需要输入模型类型的完全限定类型名(名称空间和类型名称), 如下所示:

    <ul>
        @model IEnumerable<MvcMusicStore.Models.Album>
        @foreach (MvcMusicStore.Models.Album a in Model) {  
            <li>@a.Title</li>
        }
    </ul>

如果不想输入模型的完全限定类型名, 可使用 @using 关键字声明, 如下所示:
    <ul>
        @using MvcMusicStore.Models;
        @model IEnumerable<Album>
        @foreach (Album a in Model) {  
            <li>@a.Title</li>
        }
    </ul>

对于在视图中经常使用的名称空间, 一个较好的办法是在Views 目录下的 Web.config 文件中声明。

为了查看先前的两个例子的实例应用, 使用 NuGet 将 Wrox.ProMvc5.Views.AlbumList 包安装到一个默认的 ASP.NET MVC5 项目中, 如下所示:
Install-Package Wrox.ProMvc5.Views.AlbumList


这样就把两个视图例子放进了文件夹 \Views\Albums中, 并且把相应的控制器代码放进了文件夹\Samples\AlbumList中。按 Ctrl+F5 快捷键 运行项目, 在浏览器中访问 /albums/listweaklytyped 和 /albums/liststronglytyped, 就可以看到代码的运行效果。

http://localhost/MvcMusicStore/Albums/ListWeaklyTyped

http://localhost/MvcMusicStore/Albums/ListStronglyTyped

理解ViewBag, ViewData, 和 ViewDataDictionary 

ViewData["CurrentTime"]=DateTime.Now;
ViewBag.CurrentTime = DateTime.Now;

两者是等同的, ViewBag 是ViewData 的动态封装器。但ViewBag 更受欢迎。
类似 ViewData["Key With Spaces"] 无法使用ViewBag 访问, 因为它不是一个合法的C#标识符。
如果其中任何一个参数是动态的, 那么就不会通过编译。
@Html.TextBox("name", ViewBag.Name)
正确的做法=>
@Html.TextBox("name", ViewData.["Name"]);
@Html.TextBox("name", (string)ViewBag.Name);
ViewDataDictionary 是一个特殊的字典类, 而并不是一个通用的Dictionary。原因之一在于, 它有一个额外 的Model属性, 允许向视图提供一个具体的模型对象。因为ViewData只能有一个模型对象。所以使用ViewDataDictionary向视图传递具体的类十分方便。

视图模型

视图通常需要显示各种没有直接映射到域模型的数据。例如,可能需要视图来显示单个商品的详细信息。有时在同一视图上也需要显示商品附带的其他 信息,比如当前登录系统的用户名、该用户是否有权编辑商品等。
把与视图主模型无关的数据存放在ViewBag属性中,可以很容易地实现这些数据在视图中的显示。当具有一个清晰定义的模型和一些额外的引用数据时, 这种方法尤为有用。这种技术的一种常见的应用是使用ViewBag为下拉列表提供表单选项。例如,MVC Music Store项目的Album Edit视图需要填充Genres和Albums 下拉列表, 但是这些下拉列表不适合放到Albums模型中。 为了应对这种情况, 同时不使用无关信息影响 Album 模型, 我们可以将 Genre和Album的信息保存到 ViewBag 中。

        public ActionResult Edit(int id = 0) 
        {
            Album album = db.Albums.Find(id);
            if (album == null) 
            {
                return HttpNotFound();
            }
            ViewBag.GenreId = new SelectList(
                db.Genres, "GenreId", "Name", album.GenreId);
            );
            ViewBag.ArtistId = new SelectList(
                db.Artists, "ArtistId", "Name", album.ArtistId);
            );
        }

这么做当然能够完成要求, 并且也为视图中显示数据提供了一种灵活的方法。但是这并不是一种应该经常使用的方法。 由于前面介绍过的原因, 一般应该坚持使用强类型模型对象——必须使所有数据都是强类型数据, 以便视图编写人员能够利用智能感知功能。

可能采用的一个方法是编写自定义的视图模型类。这里的视图模型可以看作仅限于向视图提供信息的模型。注意这里使用的术语“视图模型”不同于Model View ViewModel (MVVM)模型中视图模型的概念。这也是当在讨论视图模型时, 作者倾向于使用“视图特定”的原因所在。

例如, 如果需要一个购物车汇总页面, 用来显示商品列表、购物车中商品的总金额, 以及显示给用户的消息, 就可以创建 ShoppingCarViewModel 类, 如下所示:

        public class ShoppingCartViewModel 
        {
            public IEnumerable<Product> Products { get; set; }
            public decimal CartTotal { get; set; }
            public string Message { get; set; }

        }

现在可使用如下的 @model 指令, 向这个模型中强制性地输入一个视图。
@model ShoppingCartViewModel

这就不需要改变Model类的情况下带来的强类型视图的好处, 其中包括类型检查、智能感知以及免于转换无类型的ViewDataDictory对象。
查看购物车视图模型的例子, 在NuGet 中运行:

Install-Package Wrox.ProMvc5.Views.ViewModel

添加视图

虽然手动创建 视图文件, 添加到 Views 目录下, 但VS中的ASP.net MVC工具的Add View 对话框使得创建视图非常容易。
在 HomeController 控制器中添加 Edit 方法:

        public ActionResult Edit() 
        {
            return View();
        }

下一步, 在操作方法中右击, 选择 Add View 菜单项, 打开



视图名:默认为操作方法名。
模板:一旦选择一个模型类型,就可以选择一个基架模板。这些模板利用 VS 模板系统来生成基于选择模型类型的视图。 图3-6显示了这些模板, 其描述如下表:

基架描述
Create创建一个视图, 其中带有创建模型新实例的表单,并为模型类型的每一个属性生成一个标签和输入框
Delete创建一个视图,其中带有删除现有模型实例的表单,并为模型的每一个属性显示一个标签以及该属性的值
Details创建一个视图,它显示了模型类型的每一个属性的标签及其对应值
Edit创建一个视图,其中带有编辑现有模型实例的表单。并为模型类型的每一个属性生成一个标签和输入框
Empty创建一个空视图,使用@model语法指定模型类型
Empty(without model)同Empty, 但没有模型。
List创建一个带有模型实例的视图。 为模型类型的每一个属性生成一列。确保操作方法向视图传递的是 IEnumerable<YourModelType> 类型。同时为了执行创建、编辑、删除操作, 视图中还包含了指向操作的链接。

引用脚本库:这个选项用来指示要创建的视图是否应该包含指向javascrip库(如果对视图有意义的话)。 默认情况下, _Layout.cshtml 文件既不引用 jQuery Validation 库, 也不引用 Unobtrusive jQuery Validation 库, 只引用主 jQuery 库。
当创建一个包含数据条目表单的视图(如 Edit 视图或 Create 视图)时, 选择这个选项会添加对 jQueryval 捆绑的脚本的引用。如果要实现客户端验证, 那么这些库就是必需的。除了这种情况之外, 完全可以忽略这个复选框。
创建为分布视图:选择这个选项意味着要创建的视图不是一个完整的视图, 因此, Layout选项是不可用的。 生成的部分视图除了在其顶部没有 <html> 和 <head> 标签之外, 很像一个常规的视图。
使用布局页:这个选项决定了要创建的视图是否引用布局, 还是成为一个完全独立的视图。 如果选择使用默认布局, 就没有必要指定一个布局了, 因为在 _ViewStart.cshtml 文件中已经指定了布局。 这个选项是用来重写默认布局文件的。

自定义模板在 16 章中介绍。


Razor 视图引擎

前面的部分介绍了如何在控制器中指定视图以及添加视图。 然而这些内容并没有涉及到在视图中执行的语法。 ASP.net MVC提供了两种不同的引擎:较新的Razor视图引擎和较早的Web Forms 引擎。 本节中只介绍Razor 引擎, 包括Razor语法、布局和部分视图等。

Razor的概念

Razor 视图引擎是 ASP.NET MVC3 中新扩展的内容,并且也是它的默认视图引擎。




代码表达式

Razor中的核心转换字符是“at”符号。 这里有两种基本类型的转换:代码表达式和代码块。
求出表达式的值, 然后将值写入到响应中。
例如:在下面的代码段中:
<h1> Listing @items.Length items.</h1>
注意,此表达式是作为隐式表达式求解的。 这里不需要指出代码表达式的结束位置。 相比之下, Web Form视图只支持显式代码表达式, 这样上面的代码段将是:
<h1> Listing <%= stuff..Length %> items.</h1>

相比之下, Razor 有代码自动回转的功能。
但由此可能出现潜在的二义性。比如:
@{
string rootNamespace="MyApp";
}
<span>@rootNamespace.Models</span>
然而,这样反而出现了错误, 提示string 没有Models属性。在这种边界情况下, Razor诚然不能理解我们的意图,而会认为@rootNamespace.Models是表达式。
这种情况下, 可以用圆括号括起来以支持显式代码表达式:
<span>@(rootNamespace).Models</span>
这样就告知了Razor, .Models 是字面量文本, 而不是代码表达式的一部分。
但对于电子邮件地址, 如: <span>support@message.com</span>
Razor 足够智能, 而不会去处理。
如果有类似电子邮件的情形:<li>Item_@item.Length</li>  ,  会认为是 Email 原样输出。
希望输出 Item_3 , 则应改为:<li>Item_@(item.Length)</li>
对于 @ 符号, 应该用 @@ 来转义。
<p> 
You should follow 
@asp.net 
</p>
应改为:
<p> 
You should follow 
@@asp.net 
</p>

HTML编码

因为许多情况下都需要用视图显示用户输入。如博客评论或产品评论等,所以总是存在潜在的跨站脚本攻击(也称XSS,这点将在第7章详细介绍)。值得称赞的是Razor表达式用HTML自动编码的。
@{
string message = "<script>alert('haacked!');</script>";
}
<span> @message </span>
这段代码将不会弹出一个警告圣诞框,而会呈现编码的HTML:
<span> &lt;script&gt;alert('haacked!');&lt;/script&gt; </span>
如果想展现HTML标记, 就返回一个 System.Web.IHtmlString 对象的实例,Razor并不对它进行编码。
简单来说, 可以用 HtmlRaw 便捷方法:
@{
    string message = "<strong>This is bold!</strong>";
}
<span>@Html.Raw(message)</span>
这样就会显示不经过HTML编码的消息:
<span><strong>This is bold!</strong></span>
虽然这种自动的HTML编码通过对以HTML形式显示的用户输入进行编码有效缓和了XSS的脆弱性,但是对于在JavaScript中显示用户输入来说还是不够的。
例如:
<script type="text/javascript">
$(function(){
var message = 'Hello @ViewBag.Username';
$("#message").html(message).show("slow");
});
</script>
尽管在message字符串中对用户名进行了HTML编码,但是仍然具有潜在的XSS脆弱性。例如,如果用户提供以下的字符串作为用户名,HTML将被设置为一个脚本标签。
 \x3cscript\x3e%20alert(\x27pwnd\x27)%20\x3c/script\x3e
==>
&lt;script&gt;%20alert(&#39;pwnd&#39;)%20&lt;/script&gt;
当在JavaScript中将用户提供的值赋给变量时, 要使用JavaScript字符串而不仅仅是HTML编码,记住这一点是很重要的。也就是要使用@Ajax.JavaScriptStringEncode方法对用户输入进行编码。下面是使用了这个方法的相同代码,这样就可以有效地避免XSS攻击。
<script type="text/javascript">
$(function(){
var message = 'Hello @Ajax.JavaScriptStringEncode(ViewBag.Username)';
$("#message").html(message).show("slow");
});
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值