• 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: &lt;input&gt;</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" />
    <input name="myCheckbox" type="hidden" value="false" />

    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"
    type="radio" value="val" />

    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">
    val</textarea>

    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" />
    <input name="IsApproved" type="hidden" value="false" />

    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">
    <option value="">Choose</option>
    <option>A</option>
    <option>B</option>
    </select>

    Html.DropDownListFor(x => x.Gender, new SelectList(new [] {"M", "F"})) 

    <select id="Gender" name="Gender">
    <option>M</option>
    <option>F</option>
    </select>

    Multiple-select Html.ListBox("myList", new MultiSelectList(new [] {"A", "B"})) 

    <select id="myList" multiple="multiple" name="myList">
    <option>A</option>
    <option>B</option>
    </select>

    Html.ListBoxFor(x => x.Vals, new MultiSelectList(new [] {"A", "B"}))

    <select id="Vals" multiple="multiple" name="Vals">
    <option>A</option>
    <option>B</option>
    </select>

    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  

  • 相关阅读:
    PHP命名空间
    php各个版本的区别
    MySQL的读写分离---主从复制、主主复制
    redis的安装和php的redis扩展
    php的设计模式
    git命令详情
    memcache和redis的对比
    mysql性能优化
    MySQL的分区、分表、集群
    nginx的反向代理和负载均衡
  • 原文地址:https://www.cnblogs.com/duanshuiliu/p/3700670.html
一二三 - 开发者的网上家园