ASP.NET MVC 4 (六) 帮助函数
帮助函数封装一些代码,方便我们在应用程序中重用,MVC内建很多帮助函数,可以很方便的生成HTML标记。首先列出后面示例中用到的数据模型类定义:
namespace HelperMethods.Models { public partial class Person {
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public Address HomeAddress { get; set; }
public bool IsApproved { get; set; }
public Role Role { get; set; }
} public class Address {
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
[Display(Name = "ZIP CODE")]
public string PostalCode { get; set; }
public string Country { get; set; }
} public enum Role {
Admin,
User,
Guest
}
}
控制器的定义:
namespace HelperMethods.Controllers {
public class HomeController : Controller { public ActionResult Index() { ViewBag.Fruits = new string[] { "Apple", "Orange", "Pear" };
ViewBag.Cities = new string[] { "New York", "London", "Paris" }; string message = "This is an HTML element: <input>"; return View((object)message);
} public ActionResult CreatePerson() {
return View(new Person { Role = Role.Guest});
} [HttpPost]
public ActionResult CreatePerson(Person person) {
return View("DisplayPerson", person);
}
}
}
内联帮助函数
我们可以直接在视图中定义内联的帮助函数,使用@helper标记内联函数定义:
@model string
@{
Layout = null;
}
@helper ListArrayItems(string[] items)
{
foreach (string str in items)
{
<b>@str </b>
}
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
Here are the fruits: @ListArrayItems(ViewBag.Fruits)
</div>
<div>
Here are the cities: @ListArrayItems(ViewBag.Cities)
</div>
<div>
Here is the message:
<p>@Model</p>
</div>
</body>
</html>
这里定义了一个内联帮助函数ListArrayItems,后续我们可以重用它显示数值内容,内联帮助函数没有返回值。在调用ListArrayItems时我们也没有对参数做类型Cast,MVC会自动实现类型转换。
外部帮助函数
内联帮助函数虽然很方便,但是只能在视图中定义和使用,如果内联函数很复杂会让视图很难读得清楚,对此我们可以定义外部帮助函数,外部帮助函数实际上是对HtmlHelper类的方法扩展,上面的内联帮助函数改写成外部帮助函数是这样的:
namespace HelperMethods.Infrastructure {
public static class CustomHelpers { public static MvcHtmlString ListArrayItems(this HtmlHelper html, string[] list) { TagBuilder tag = new TagBuilder("ul");
foreach(string str in list) {
TagBuilder itemTag = new TagBuilder("li");
itemTag.SetInnerText(str);
tag.InnerHtml += itemTag.ToString();
} return new MvcHtmlString(tag.ToString());
} }
}
HtmlHelper暴露一些属性比如RouteCollection、ViewBag、ViewContext方便我们获取当前请求相关的数据。外部帮助函数最后返回一个MvcHtmlString对象,它的内容直接写到输出响应。我们可以这样在视图中使用自定义的外部帮助函数:
@model string
@using HelperMethods.Infrastructure
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
Here are the fruits: @Html.ListArrayItems((string[])ViewBag.Fruits)
</div>
<div>
Here are the cities: @Html.ListArrayItems((string[])ViewBag.Cities)
</div>
<div>
Here is the message:
<p>@Model</p>
</div>
</body>
</html>
我们需要引入定义外部帮助函数的命名空间,也可以定义在/Views/Web.config中供所有的视图使用。使用@html来调用外部帮助函数,它返回一个HtmlHelper类的实例,在调用外部帮助函数时也要做参数类型转化。
帮助函数中的字符串编码
在使用帮助函数时我们需要注意HTML编码的问题,先来看看下面一个问题,如果我们在控制器action中输出一些带HTML标记的字符串到视图:
public ActionResult Index() {
string message = "This is an HTML element: <input>";
return View((object)message);
}
在视图中直接输出这个字符串:
@model string @{
Layout = null;
} <!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<p>This is the content from the view:</p>
<div>
Here is the message:
<p>@Model</p>
</div>
</body>
</html>
最后Razor输出的结果是:
...
<div>
Here is the message:
<p>This is an HTML element: <input></p>
</div>
...
<input>中的“<>”标签被编码为相应的字符而非HTML标签,这是出于安全的考虑,防止动态嵌入HTML标记甚至Javascript脚本。如果是从帮助函数中输出HTML标签又是怎样的结果呢?看下面的例子:
public static MvcHtmlString DisplayMessage(this HtmlHelper html, string msg) {
string result = String.Format("This is the message: <p>{0}</p>", msg);
return new MvcHtmlString(result);
}
视图文件中调用这个帮助函数输出HTML标记:
<p>This is the content from the helper method:</p>
<div style="border: thin solid black; padding: 10px">
@Html.DisplayMessage("This is an HTML element: <input>")
</div>
这次得到的就是HTML的<input>编辑框,这是因为我们的HTML帮助函数返回的MvcHtmlString被信任为安全的,对输出的结果不再做HTML编码。如果帮助函数返回的不是MvcHtmlString,只是普通的String,比如:
public staticstring DisplayMessage(this HtmlHelper html, string msg) {
return String.Format("This is the message: <p>{0}</p>","This is an HTML element: <input>"); }
这时候字符串又会被HTML编码,不会输出input标签。但这样的结果是<p>标记也会被HTML编码,这不是我们想要的结果,正确的做法是:
public static MvcHtmlStringDisplayMessage(this HtmlHelper html) {
string encodedMessage = html.Encode("This is an HTML element: <input>");
string result = String.Format("This is the message: <p>{0}</p>", encodedMessage);
return new MvcHtmlString(result);
}
我们使用html.Encode()对动态内容编码,结果仍然返回MvcHtmlString对象,这样就避免了动态内容嵌入带来的安全漏洞,也使真正要显示的HTML标记被正确的渲染。
内建的Form帮助函数
我们可以直接使用HTML的Form标记创建一个表单来编辑要提交的数据:
@model HelperMethods.Models.Person
@{
ViewBag.Title = "CreatePerson";
}
<h2>CreatePerson</h2>
<form action="/Home/CreatePerson" method="post">
<div class="dataElem">
<label>PersonId</label>
<input name="personId" value="@Model.PersonId" />
</div>
<div class="dataElem">
<label>First Name</label>
<input name="FirstName" value="@Model.FirstName" />
</div>
<div class="dataElem">
<label>Last Name</label>
<input name="lastName" value="@Model.LastName" />
</div>
<input type="submit" value="Submit" />
</form>
这样的问题是必须硬编码表单提交的URL,实际上我们可以使用内建的BeginForm帮助函数创建一个表单:
@Html.BeginForm()
<div class="dataElem">
<label>PersonId</label>
<input name="personId" value="@Model.PersonId" />
</div>
<div class="dataElem">
<label>First Name</label>
<input name="FirstName" value="@Model.FirstName" />
</div>
<div class="dataElem">
<label>Last Name</label>
<input name="lastName" value="@Model.LastName" />
</div>
<input type="submit" value="Submit" />
@{Html.EndForm();}
BeginForm开始一个表单,EndForm结束表单,实际上更常用的写法是是使用Razor代码块:
@using (Html.BeginForm())
{
<div class="dataElem">
<label>PersonId</label>
<input name="personId" value="@Model.PersonId" />
</div>
<div class="dataElem">
<label>First Name</label>
<input name="FirstName" value="@Model.FirstName" />
</div>
<div class="dataElem">
<label>Last Name</label>
<input name="lastName" value="@Model.LastName" />
</div>
<input type="submit" value="Submit" />
}
@using创建一个自闭合的表单,不再需要调用EndForm。不带参数的BeginForm将表单提交到当前控制器的当前action方法,除此之外BeginForm还有很多重载形式,下面给出一些使用的例子:
@using (Html.BeginForm("CreatePerson", "Home",new { id = "MyIdValue" }, FormMethod.Post,new { @class = "personClass", data_formType="person"})) {
...
}
这里CreatePerson指定了表单要提交到的控制器,Home为要提交到的action,new { id = "MyIdValue" }为额外的路径映射参数,FormMethod.Post指定使用HTTP post方法提交数据,new { @class = "personClass", data_formType="person"}指定Form标记的属性,得到的表单HTML结果是这样的:
...
<form action="/Home/CreatePerson/MyIdValue" class="personClass" data-formType="person"
method="post">
...
我们还可以指定表单使用的路径映射生成提交到的链接,比如我们注册了这样一条路径映射:
routes.MapRoute( name: "FormRoute", url: "app/forms/{controller}/{action}" );
我们使用BeginRouteForm创建表单并指定要使用的路径映射记录:
@using(Html.BeginRouteForm("FormRoute", new {}, FormMethod.Post, new { @class = "personClass", data_formType="person"})) {
...
}
最后得到的结果是:
...
<form action="/app/forms/Home/CreatePerson"class="personClass" data-formType="person" method="post">
...
内建输入帮助函数
实际上不需要使用诸如<input>的HTML标记来创建数据的编辑框,MVC内建提供了大量的输入帮助函数:
HTML元素 | 示例 | 输出结果 |
Checkbox | Html.CheckBox("myCheckbox", false) |
<input id="myCheckbox" name="myCheckbox" type="checkbox" value="true" /> |
Hidden | Html.Hidden("myHidden", "val") | <input id="myHidden" name="myHidden" type="hidden" value="val" /> |
Radio | Html.RadioButton("myRadiobutton", "val", true) |
<input checked="checked" id="myRadiobutton" name="myRadiobutton" |
Password | Html.Password("myPassword", "val") | Html.Password("myPassword", "val") |
Text area |
Html.TextArea("myTextarea", "val", 5, 20, null) |
<textarea cols="20" id="myTextarea" name="myTextarea" rows="5"> |
Text box | Html.TextBox("myTextbox", "val") | <input id="myTextbox" name="myTextbox" type="text" value="val" /> |
每一种输入标记函数都有多种重载形式,可以提供额外的参数指定标签的特性。需要注意的是CheckBox,它会生成两条input标记,其中一个类型为hidden,这是因为浏览器在checkbox未选中时不会提交数据,这条hidden的标记使得MVC即使未在复选框未选中时也能获得相应的数据。
使用输入帮助函数前面的表单可以这样写:
@model HelperMethods.Models.Person
@{
ViewBag.Title = "CreatePerson";
}
<h2>CreatePerson</h2>
@using (Html.BeginRouteForm("FormRoute", new { }, FormMethod.Post,new { @class = "personClass", data_formType = "person" }))
{
<div class="dataElem">
<label>PersonId</label>
@Html.TextBox("personId", @Model.PersonId)
</div>
<div class="dataElem">
<label>First Name</label>
@Html.TextBox("firstName", @Model.FirstName)
</div>
<div class="dataElem">
<label>Last Name</label>
@Html.TextBox("lastName", @Model.LastName)
</div>
<input type="submit" value="Submit" />
}
我们为每个输入字段指定了名称,如果指定的名称和视图模型的某个属性名称不一致,MVC将不能成功的绑定和创建模型对象,我们可以使用这种替代的方式:
我们不需要再给出字段值,MVC自动根据传入的字符串从View data、viewbag及view model中查找对应的值,比如调用@Html.TextBox("DataValue"),MVC视图从ViewBag.DataValue、@Model.DataValue获取数值,使用第一个找到的结果。更复杂点的@Html.TextBox("DataValue.First.Name"),MVC搜索路径也更多:
• ViewBag.DataValue.First.Name
• ViewBag.DataValue["First"].Name
• ViewBag.DataValue["First.Name"]
• ViewBag.DataValue["First"]["Name"]
同样,在找到第一个可用值后搜索停止,这势必造成性能上的一些损失,但是使得我们创建编辑表单更为简单。
强类型输入帮助函数
上节中每一种基本输入帮助函数都有对应的强类型形式,强类型输入帮助函数只能用在强类型视图中。
HTML元素 | 示例 | 输出结果 |
Checkbox | Html.CheckBoxFor(x => x.IsApproved) |
<input id="IsApproved" name="IsApproved" type="checkbox" value="true" /> |
Hidden | Html.HiddenFor(x => x.FirstName) | <input id="FirstName" name="FirstName" type="hidden" value="" /> |
Radio | Html.RadioButtonFor(x => x.IsApproved, "val") | input id="IsApproved" name="IsApproved" type="radio" value="val" /> |
Password | Html.PasswordFor(x => x.Password) | <input id="Password" name="Password" type="password" /> |
Text area | Html.TextAreaFor(x => x.Bio, 5, 20, new{}) | <textarea cols="20" id="Bio" name="Bio" rows="5">Bio value</textarea> |
Text box | Html.TextBoxFor(x => x.FirstName) | <input id="FirstName" name="FirstName" type="text" value="" /> |
强类型输入帮助函数使用Lambda表达式从视图模型中抽取字段或者属性,所生成的HTML结果和基本输入没有什么不同,只是帮助我们编程时减少输入的错误。上面的例子使用强类型输入函数是这样的:
@model HelperMethods.Models.Person
@{
ViewBag.Title = "CreatePerson";
}
<h2>CreatePerson</h2>
@using (Html.BeginRouteForm("FormRoute", new { }, FormMethod.Post,
new { @class = "personClass", data_formType = "person" }))
{
<div class="dataElem">
<label>PersonId</label>
@Html.TextBoxFor(m => m.PersonId)
</div>
<div class="dataElem">
<label>First Name</label>
@Html.TextBoxFor(m => m.FirstName)
</div>
<div class="dataElem">
<label>Last Name</label>
@Html.TextBoxFor(m => m.LastName)
</div>
<input type="submit" value="Submit" />
}
选择元素帮助函数
MVC提供以下帮助函数用以生成HTML的select元素:
HTML元素 | 示例 | 输出结果 |
Drop-down list | Html.DropDownList("myList", new SelectList(new [] {"A", "B"}), "Choose") |
<select id="myList" name="myList"> |
Html.DropDownListFor(x => x.Gender, new SelectList(new [] {"M", "F"})) |
<select id="Gender" name="Gender"> |
|
Multiple-select | Html.ListBox("myList", new MultiSelectList(new [] {"A", "B"})) |
<select id="myList" multiple="multiple" name="myList"> |
Html.ListBoxFor(x => x.Vals, new MultiSelectList(new [] {"A", "B"})) |
<select id="Vals" multiple="multiple" name="Vals"> |
Select帮助函数使用SelectList或者MultilSelectList参数,两者的区别在于后者创建的允许多选的select。Select帮助函数所用的数值为IEnumerbale类型,比如我们要创建一个枚举类型的选择编辑框:
<div class="dataElem">
<label>Role</label>
@Html.DropDownListFor(m => m.Role, new SelectList(Enum.GetNames(typeof(HelperMethods.Models.Role))))
</div>
HelperMethods.Models.Role是一个C#的枚举类型,输出的结果是:
<div class="dataElem">
<label>Role</label>
<select data-val="true" data-val-required="The Role field is required." id="Role" name="Role">
<option selected="selected">Admin</option>
<option>User</option>
<option>Guest</option>
</select>
</div>
data-val和data-val-required两个特性是和数据验证相关的,后文中再来详细分析。
以上为对《Apress Pro ASP.NET MVC 4》第四版相关内容的总结,不详之处参见原版 http://www.apress.com/9781430242369。
ASP.NET MVC 4 (六) 帮助函数的更多相关文章
- Pro ASP.NET MVC –第六章 MVC的基本工具
在本章,我们将介绍每个MVC程序员"武器库"的三个重要工具:依赖注入容器.单元测试框架和mock工具.在本书,对于三个工具分别都只用了一种方式实现,但每个工具都还有其他的实现方式. ...
- ASP.NET MVC 第六回 过滤器Filter
在Asp.netMvc中当你有以下及类似以下需求时你可以使用Filter功能 判断登录与否或用户权限 决策输出缓存 防盗链 防蜘蛛 本地化与国际化设置 实现动态Action Filter是一种声明式编 ...
- asp.net MVC helper 和自定义函数@functions小结
asp.net Razor 视图具有.cshtml后缀,可以轻松的实现c#代码和html标签的切换,大大提升了我们的开发效率.但是Razor语法还是有一些棉花糖值得我们了解一下,可以更加强劲的提升我们 ...
- asp.net MVC 帮助助手和函数( @helper @functions)
asp.net Razor 视图具有.cshtml后缀,可以轻松的实现c#代码和html标签的切换,大大提升了我们的开发效率.但是Razor语法还是有一些棉花糖值得我们了解一下,可以更加强劲的提升我们 ...
- [转]asp.net MVC helper 和自定义函数@functions小结
本文转自:http://www.cnblogs.com/jiagoushi/p/3904995.html asp.net Razor 视图具有.cshtml后缀,可以轻松的实现c#代码和html标签的 ...
- ASP.NET MVC教程六:两个配置文件详解
前言 在新建完一个MVC项目之后,你会发现整个整个项目结构中存在有两个web.config文件,如下图所示: 这两个配置文件,一个位于项目的根目录下面,一个位于Views文件夹下面,这两个配置文件有什 ...
- ASP.NET MVC 4 (七) 模板帮助函数
和普通HTML帮助函数不同,模板帮助函数不需要指定所用的HTML类型,MVC会推断选择合适的HTML元素,这让我们有更多的灵活性. 使用模板帮助函数 我们使用<ASP.NET MVC 4 (六) ...
- ASP.NET MVC API 路由生成规则
我们都知道调用ASP.NET MVC的某些API函数(诸如:Url.Action.RedirectToAction等)可以生成URL,ASP.NET MVC会根据调用API函数时传入的参数去匹配系统定 ...
- ASP.NET MVC 4 部署到 Windows Azure 如何轉換時區設定
由於公司慢慢地開始將新的專案都移往 Windows Azure 雲端平台做網站代管,漸漸地也開始遇到一些小問題,這些問題在還沒上雲端之前通常不會發生,像我們這次遇到的問題就跟顯示時間有關.由於 Win ...
随机推荐
- JNI编程实现(Linux)
JNI是Java Native Interface的缩写,是Java平台的本地调用,从Java1.1就成为了Java标准的一部分,它允许Java代码和其它语言的代码进行互相调用,只要调用约定支持即可, ...
- 集合(2)—Collection之List的使用方法
声明集合变量 List list = new ArrayList(); 或者 : public LIst list: public 构造函数(){ this.list = new ArrayList( ...
- Android:Unable to find explicit activity class
写了两个Activity,确定java代码和xml配置文件没问题之后,运行工程,报错: E/AndroidRuntime(10513): FATAL EXCEPTION: main E/Android ...
- MySQL架构与业务总结图
MySQL架构与业务总结图如下:
- 【SQL 代码】SQL复制数据表及表结构
select * into 目标表名 from 源表名 from 源表名 以上两句都是将'源表'的数据插入到'目标表',但两句又有区别的: 第一句(select into from)要求目标表不存在, ...
- 【Zookeeper】源码分析之服务器(二)之ZooKeeperServer
一.前言 前面阐述了服务器的总体框架,下面来分析服务器的所有父类ZooKeeperServer. 二.ZooKeeperServer源码分析 2.1 类的继承关系 public class ZooKe ...
- 《Unix&Linux大学教程》学习笔记七:进程与作业控制
1:进程:一个内存中的程序+程序所需数据+管理程序的各种状态信息. 2:进程由内核进行管理,内核使用调度器,给予进程一个时间片来运行,然后切换到下一个进程. 3:进程分叉 fork :创建一个子进程 ...
- linux实现共享内存同步的四种方法
https://blog.csdn.net/sunxiaopengsun/article/details/79869115 本文主要对实现共享内存同步的四种方法进行了介绍. 共享内存是一种最为高效的进 ...
- VS中项目的循环引用的问题
这个道理很简单,要编译A,首先要编译A引用的项目B,要编译项目B,必须首先编译B引用的项目A. 那么你说应该先编译哪个项目. 如果你非要循环引用,你不要让A引用项目B,而是直接引用项目B生成的b.dl ...
- 我的Nginx配置文件
#user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #erro ...