這裏有一個疑問,諸如在文本編輯器中輸入 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。