这里有一个疑问,诸如在文本编辑器中输入 HTML 元素如此简单的任务,也须要任何帮助吗?的确,输入标签名称是很容易的事,可是确保 HTML 页面连接中的 URL 指向正确的位置、表单元素拥有适用于模型绑定的名称和值、以及当模型绑定失败时其余元素可以显示相应的错误提示消息,这些才是 HTML 的繁杂点。html
ASP.NET Web Forms 并无彻底利用 form 标签的强大功能。表单中的这些输入元素是如何被提交到服务器的呢?web
action 特性告知 Web 浏览器信息发往哪里,因此它瓜熟蒂落的包含一条 URL。这里的 URL 是相对的,但当向一个不一样的应用程序或服务器发送信息时,它也能够是绝对的。下面的 form 标签能够从任何应用程序向站点 www.bing.com 的 search 页面发送一个搜索词:浏览器
<form action="http://www.bing.com/search">
<input name="q" type="text" />
<input type="submit" value="Search" />
</form>
method 特性能够告知浏览器是使用 GET 仍是 POST 方式发送请求。上面示例中并无指定 method 特性值,而从结果能够验证,是以查询字符串方式进行请求,所以 method 默认值为 HTTP GET。服务器
POST 方式也能够把数据提交到服务器,表单值并不会显示在 URL 上,所以 GET 请求能够为结果页面创建书签。一般,应根据实际应用的语意来进行选择,GET 请求应用于读操做(R),而 POST 请求应用于写操做(CUD)。app
假设如今音乐商店要实现搜索专辑,代码可能会以下:框架
<form action="/Home/Search" method="get">
<input name="q" type="text" />
<input type="submit" value="Search" />
</form>
考虑一下,若是把应用程序部署到一个非网站根目录的目录中,或者修改了路由定义,那么手动编写的操做值可能会把用户的浏览器导航到并不存在的资源处。更好的方法是经过计算 action 的值来实现这一 URL,有一个 HTML 辅助方法能够代劳自动完成这个计算:编辑器
<!--参数序列:action、Controller、method-->
@using (Html.BeginForm("Search", "Home", FormMethod.Get))
{
<input name="q" type="text" />
<input type="submit" value="Search" />
}
经过视图的 Html 属性能够调用 HTML 辅助方法;经过 Url 属性能够调用 URL 辅助方法;经过 Ajax 属性能够调用 AJAX 辅助方法。全部这些方法的目的都是为了使视图编码变得更容易。性能
大部分的辅助方法都输出 HTML 标记,尤为是 HTML 辅助方法。例如,前面的示例 Html.BeginForm 能够构建一个强壮的表单标签。在后台,该辅助方法与路由引擎协调工做来生成合适的 URL,从而当应用程序部署位置改变时,代码更富有弹性。网站
咱们知道,using 语句括号中的对象在语句块结束后会自动释放,实际上是调用了对象的 Dispose 方法,任何实现了 IDisposeable 接口的对象都能在 using 语句块中获得释放。辅助方法在调用 BeginForm 期间生成了一个其实标签 <form>,并返回了一个实现了 IDisposeable 接口的对象(MvcForm),当执行到 using 语句结束的花括号时,隐式调用了该对象的 Dispose 方法,所以辅助方法会生成一个结束标签 </form>。using 语句使得代码看起来较为优雅,不然你须要这样书写:编码
@{
Html.BeginForm("Search", "Home", FormMethod.Get);
<input name="q" type="text" />
<input type="submit" value="Search" />
Html.EndForm();
}
本篇所介绍的许多辅助方法均可以用来输出模型值,这些输出模型值的辅助方法都会在渲染前,对值进行 HTML 编码。
@Html.TextArea("text", "hello <br /> world");
输出值是通过 HTML 编码的,默认的编码能够帮助避免跨站点攻击(XSS,Cross Site Scripting)。
<textarea cols="20" id="text" name="text" rows="2">
hello <br /> world
</textarea>
辅助方法也给出了适度的控制,下面是一个 BeginForm 的重载版本:
@using (Html.BeginForm("Search", "Home", FormMethod.Get, new { target = "_blank" }))
{
<input name="q" type="text" />
<input type="submit" value="Search" />
}
这段代码中,第 4 个参数 htmlAttributes 接受一个匿名对象,在 MVC 框架的重载版本中,几乎每个 HTML 辅助方法都包含 htmlAttributes 参数。
有时也会发如今某些重载版本中,htmlAttributes 参数的类型是 IDictionary<string,object>,辅助方法用字典条目(在对象参数的情形下,就是对象的属性名和属性值),建立辅助方法生成的元素特性。有时,这会有些问题,例如要求匿名对象必须有一个 class 的属性,在字典中有一个“class”的键值不是问题,而对象不行,由于 class 是 C# 语言的关键字,必须加上“@”符号前缀:
@using (Html.BeginForm("Search", "Home", FormMethod.Get,
new { target = "_blank", @class = "editForm" }))
{
<input name="q" type="text" />
<input type="submit" value="Search" />
}
另外一个问题是将属性设置为带有连字符的名称,例如像 data-val,带有连字符的 C# 属性名是无效的,不过,全部 HTML 辅助方法在渲染 HTML 时会将属性名中的下划线转换为连字符:
@using (Html.BeginForm("Search", "Home", FormMethod.Get,
new { target = "_blank", @class = "editForm", data_validatable = true }))
{
<input name="q" type="text" />
<input type="submit" value="Search" />
}
将会产生这样的 HTML 代码:
<form action="/Home/Search" class="editForm" data-validatable="True" method="get" target="_blank">
<input name="q" type="text" />
<input type="submit" value="Search" />
</form>
每个 Razor 视图都继承了它们基类的 Html 属性。Html 属性的类型是 System.Web.Mvc.HtmlHelper<T>,这里的 T 是一个泛型类型的参数,表明传递给视图的模型类型,默认为 dynamic。
这个属性提供了一些能够在视图中调用的实例方法,好比 EnableClientValidition(开启或关闭视图中的客户端验证)。而后,先前介绍过的 BeginForm 方法并不在其中,事实上,框架定义的大多数辅助方法都是扩展方法。
扩展方法是一种极其每秒的构建方式,这主要有 2 个缘由。
首先,在 C# 的扩展方法中只有当在它的名称空间范围内,才能调用。ASP.NET MVC 中全部的 HtmlHelper 扩展方法都在名称空间 System.Web.Mvc.Html 中。(缘于文件 Views/web.config 中使用的一个命名空间条目,若是不喜欢这些内置的扩展方法,能够删除这个命名空间,构建本身的方法)
而后,“构建本身的方法”带来了第 2 个好处,咱们能够构建本身的扩展方法来代替或加强内置的辅助方法,以后的系列会介绍如何构建自定义辅助方法。
Html.ValidationSummary(true)
ModelState.AddModelError("", "This is all wrong!");// 模型级别错误,由于不关联属性或是空值
ModelState.AddModelError("Title", "What a terrible name!"); // 属性级别错误,这里具体到 Title
用来显示 ModelState 字典中全部验证错误的无序列表,true 用来告知辅助方法排除属性级别的错误。
一些经常使用的 Html 辅助方法,且各自有一些重载的版本可对标签属性进行详细的设置:
@using (Html.BeginForm())
{
@Html.ValidationSummary(true);
<fieldset>
<legend>Edit Album</legend>
<p>
@Html.Label("GenreId")
@Html.DropDownList("GenreId", ViewBag.Genres as SelectList)
</p>
<p>
@Html.Label("Title")
@Html.TextBox("Title", Model.Title)
@Html.ValidationMessage("Title")
</p>
<input type="submit" value="Save" />
</fieldset>
}
辅助方法提供了对 HTML 细粒度控制的同时还带走了构建 UI(在合适的位置显示控件、标签、错误消息和值)等工做。它会检查 ViewData 对象以得到要显示的当前值。
看一个简单的后台代码及前台源文件,证实辅助方法会查看 ViewData 中的数据,它们也能看到对象属性。
public ActionResult Edit(int? id)
{
ViewBag.Price = 10.0;
return View();
}
@Html.TextBox("Price")
<input id="Price" name="Price" type="text" value="10" />
再看这段代码:
public ActionResult Edit(int? id)
{
ViewBag.Album = new Album { Price = 11 };
return View();
}
@Html.TextBox("Album.Price")
<input id="Album_Price" name="Album.Price" type="text" value="11" />
若是在 ViewData 中没有匹配“Album.Price”这样的键,辅助方法将尝试查找与第一个“.”以前那部分名称匹配的键。换言之,就是查找一个名为 Album 的对象,而后辅助方法估测名称中剩余的部分(Price),并找到相应的值。
注意,input 元素的 id 特性值使用了下划线代替了点,但 name 特性依然使用点。之因此这样作,是由于在 id 特性中包含点是非法的,所以,运行时用静态属性 HtmlHelper.IdAttributeDotReplacement 的值代替了点。若是没有有效的 id 特性,就没法执行带有 JavaScript 库(如jQuery)的客户端脚本。
有时,显式的提供数据是一种比较好的选择,看下面代码:
public ActionResult Edit(int? id)
{
ViewBag.Album = new Album { Title = "Do what?" };
return View();
}
@Html.TextBox("Title")
<input id="Title" name="Title" type="text" value="Edit" />
没有如预料中的输出“Do what?”,原来,这种状况下页面顶部的标题已经使用了 Title 这个键!可以使用对象.属性名避免这类问题,或者显式的提供值:
@Html.TextBox("Album.Title")
或者:
public ActionResult Edit(int? id)
{
var album = new Album { Title = "Do what?" };
return View(album);
}
@model MvcMusicStore.Models.Album
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@Html.TextBox("Title", Model.Title)
在大型应用程序中,为了更清晰的肯定在哪里使用数据,须要在一些视图数据项前添加前缀,例如,不把主页标题命名为 ViewBag.Title,而是命名为注入 ViewBag.Page_Title,这样就避免了与特定页面的明明冲突。
特别注意:如下这条非强类型辅助方法的代码是没法获取正确的属性值的!这是由于该辅助方法须要在视图数据 ViewData 中查找 key,而 Model.Title 虽然也返回字符串字面值,但本例中的 Title 值为“Caravan”,显然,视图数据中是不具有这样的 key 的!
@Html.Editor(Model.Title)
若是不适应使用字符串字面值从视图数据中提取值的话,也可使用 ASP.NET MVC 提供的强类型辅助方法。这种强类型辅助方法只需传递一个 lambda 表达式来指定要渲染的模型属性,表达式的模型类型必须和为视图指定的模型类型(@model 指令指定的模型)一致。
@model MvcMusicStore.Models.Album
@{
ViewBag.Page_Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Album</h4>
<hr />
@Html.ValidationSummary(true)
@Html.HiddenFor(model => model.AlbumId)
<div class="form-group">
@Html.LabelFor(model => model.GenreId, "GenreId", new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("GenreId", string.Empty)
@Html.ValidationMessageFor(model => model.GenreId)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.ArtistId, "ArtistId", new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("ArtistId", String.Empty)
@Html.ValidationMessageFor(model => model.ArtistId)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
</div>
......
}
这些强类型辅助方法和以前的辅助方法除了有“For”后缀以外,名称皆相同。尽管生成了一样的 HTML 标记,但强类型还有许多其余的好处,包括智能感知、编译时检查错误、轻松的代码重构!(若是在模型中更改了属性名,VS 会自动修改视图中的对应代码)。注意,这里并无显式的提供值,由于 lambda 表达式提供了足够的信息,强类型辅助方法可以正确的读取模型的 Title 属性来从视图数据中获取 Title 的值。
辅助方法也能够查看模型元数据。当询问运行时(runtime)是否有 GenreId 的可用模型元数据时,运行时会从装饰 Album 模型的 DispalyName 特性中获取信息。
@Html.Label("GenreId")
[DisplayName("Genre")]
public virtual int GenreId { get; set; }
ASP.NET MVC 中的模板辅助方法利用元数据和模板构建 HTML。其中元数据包括模型值(它的名称和类型)的信息和(经过数据注解或自定义提供器添加的)模型元数据。模板辅助方法有:
例如,使用 Html.TextBoxFor 辅助方法为某个专辑的 Title 属性生成如下 HTML 标记:
<input id="Title" name="Title" type="text" value="Caravan" />
如今换成 Html.EditorFor 辅助方法,也能够完成一样的工做:
@Html.EditorFor(model => model.Title)
<input class="text-box single-line" id="Title" name="Title" type="text" value="Caravan" />
尽管两种方法生成的是一样的 HTML 标记,可是 EditFor 方法能够经过使用数据注解来改变生成的 HTML。顾名思义,就知道它比 TextBox(指明了是 type = text) 辅助方法应用更为普遍!当使用模板辅助方法时,运行时就能够生成它以为合适的任何“编辑器(Editor)”
下面在 Title 属性上添加一个 DataType 注解,EditorFor 生成了一个 textarea 文本框,而这些变化是在没有修改视图代码下完成的:
[DisplayName("Genre")]
public int GenreId { get; set; }
<textarea class="text-box multi-line" id="Title" name="Title">Caravan</textarea>
由于,通常意义上请求一个编辑器,EditorFor 会先查看元数据,而后推断出最合适的 HTML 元素。
ASP.NET MVC 还包含许多其余的辅助方法,它们涵盖全部的输入控件:
@Html.Hidden("wizardStep", 1)
@Html.HiddenFor(m => m.WizardStep)
@Html.Password("pwd")
@Html.PasswordFor(u => u.Pwd)
<!-- 单选按钮通常会组合使用 -->
@Html.RadioButton("color", "red")
@Html.RadioButton("color", "blue", true)
@Html.RadioButton("color", "green")
@Html.RadioButtonFor(m => m.GenreId, "1") Rock
@Html.RadioButtonFor(m => m.GenreId, "2") Jazz
@Html.RadioButtonFor(m => m.GenreId, "3") Pop
@Html.CheckBox("IsCar")
@Html.CheckBoxFor(m => m.IsCar)
<!--
CheckBox 方法是惟一一个渲染两个输入元素的辅助方法:
<input id="IsCar" name="IsCar" type="checkbox" value="true" />
<input name="IsCar" type="hidden" value="false" />
主要缘由在于,HTML 规范中规定浏览器只提交“选中”的复选框的值,第二个隐藏于就保证了 IsCar
至少有一个值会被提交,即使用户没有选择这个复选框。即若选中,第一个为 checked,IsCar被提交
至服务器;若不选中,复选框虽然不会被提交至服务器,但隐藏于 IsCar 且值为 false 同样会被提交
-->
渲染辅助方法可在应用程序中生成指向其余资源的连接,也能够构建被称为部分视图的可重用 URL 片断。
ActionLink 可以渲染一个超连接,渲染的连接指向另外一个控制器操做,它也是使用路由 API 来生成 URL:
<!-- 当连接的操做所在控制器与渲染当前视图的控制器同样时,只需指定操做的名称 -->
@Html.ActionLink("Link Text", "AnotherAction")
<!-- 当须要指向不一样控制器操做的连接时,可经过第三个参数指定控制器的名称,不须要 Controller 后缀-->
@Html.ActionLink("Link Text", "AnotherAction", "AnotherController")
URL 辅助方法与 HTML 的 ActionLink、RouteLink 辅助方法类类似,但它不是以 HTML 标记的形式返回构建的 URL。
Action 辅助方法不返回锚标记,下面的代码会显示浏览商店里全部 Jazz 专辑的 URL(不是连接):
<span>
@Url.Action("Browser", "Store", new { Genre = "Jazz" }, null)
</span>
<span>
/Store/Browser?Genre=Jazz
</span>
RouteUrl 辅助方法与 RouteLink 同样,但只接收路由名称,而不接收控制器名称和操做名称。
Content 辅助方法能够把应用程序的相对路径转换成绝对路径。
Partial 辅助方法能够将部分视图渲染成字符串。一般,部分视图中包含多个在不一样视图中可重复使用的标记,这是为了 HTML 代码的重用!(你能够看成 Web 组件)。
不必为视图名指定路径和文件扩展名,由于运行时会使用全部的可用视图引擎来查找,例如:
@Html.Partial("AlbumDisplay")
RenderPartial 辅助方法与 Partial 很是类似,但它并非返回字符串,而是直接写入响应输出流。出于这个缘由,它必须被放置在代码块中,而不能放在代码表达式中。
@{Html.RenderPartial("AlbumDisplay");}
@Html.Partial("AlbumDisplay")
那到底应该使用哪个?应该选择 Partial,由于它使用起来更方便。尽管后者拥有更好的性能(由于直接写入响应流),但这须要大量使用(高的网站流量或者高数量的循环中)才能提现的出来。
相似于 Partial 和 RenderPartial 辅助方法。 Partial 辅助方法一般在单独的文件中应用视图标记来帮助视图渲染视图模型的一部分。另外一方面,Action 执行单独的控制器操做,并显示结果。Action 提供了更多的灵活性和重用性,由于控制器操做能够创建不一样的模型,能够利用单独的控制器上下文。
下面是这个方法用法的简单介绍。假设如今使用的是以下的控制器:
public class MyController : Controller
{
public ActionResult Index()
{
return View();
}
[ChildActionOnly] // 该特性用于指示操做方法只应做为子操做进行调用
public ActionResult Menu()
{
var menu = GetMenuFromSomeWhere();
return PartialView(menu);
}
}
Menu操做构建一个菜单模型,并返回一个带有菜单的部分视图:
@model Menu
<ul>
@foreach (var item in Model.MenuItem)
{
<li>@item.Text</li>
}
</ul>
在 Index.cshtml 视图中,能够调用 Menu 操做来显示菜单,这就实现了动态的菜单,菜单选项的增减将再也不须要改动任何代码:
<header>
@Html.Action("Menu")
</header>
<h1>Welcome to the Index View</h1>
ChildActionOnly 特性标记能够有效避免运行时直接经过 URL 来调用 Menu 操做,相反,只能经过 Action 或 ActionRender 来调用子操做,虽然这不是必须的,但一般在进行子操做时推荐这样作。
ASP.NET MVC 在 ControllerContext 上添加了一个新属性 IsChildAction,当经过 Action 或 ActionRender 调用操做时,它的值就为 True,经过 URL 访问时它的值就为 False。