返璞归真 asp.net mvc (6) - asp.net mvc 2.0 新特性
作者: 介绍 asp.net mvc 之 asp.net mvc 2.0 新特性:如通过 DataAnnotations 实现数据验证及客户端验证,MVC 与 Dynamic Data 的结合,对异步 Controller 的支持,对 Area 的支持,新增的一些帮助方法等 示例 1、新特性总结 Index.aspx 代码
2、异步 Controller 的 Demo Areas/AsynchronousController/Controllers/HomeController.cs <% @ Page Language = " C# " MasterPageFile = " ~/Views/Shared/Site.Master " Inherits = " System.Web.Mvc.ViewPage " %> < asp:Content ID ="Content1" ContentPlaceHolderID ="TitleContent" runat ="server" > Home Page </ asp:Content > < asp:Content ID ="Content2" ContentPlaceHolderID ="MainContent" runat ="server" > < h2 > <% : ViewData[ " Message " ] %> </ h2 > < p > < a href ="Category" target ="_blank" > Demo </ a > </ p > < p > 1、 < a href ="Category/Create" target ="_blank" > 通过 DataAnnotations 实现数据验证;对客户端验证的支持 </ a > </ p > < p > 2、 < a href ="AsynchronousController" target ="_blank" > 对异步 Controller 的支持 </ a > </ p > < p > 3、MVC 与 Dynamic Data 的结合 < br /> < a href ="Category/Edit/21" target ="_blank" > 实现 Model 级别的 Dynamic Data </ a > < br /> < a href ="Category/Edit2/21" target ="_blank" > 实现 Property 级别的 Dynamic Data </ a > < br /> 新增了对模板的支持(模板源自 Dynamic Data) </ p > < p > 4、新增的参数级的声明 DefaultValueAttribute - 用于为参数指定默认值public ActionResult View(int id, [DefaultValue(1)]int page) { } </ p > < p > 5、关于路由的新特性,参看:http://www.cnblogs.com/webabcd/archive/2010/05/20/1739750.html </ p > < p > 6、Html.Hidden() 增强 - 其可以用于保存二进制(Binary 类型或 byte[] 类型)数据 < br /> 比如: < br /> < %= Html.Hidden("Photo", Model.Photo) % > < br /> 生成的 html 为: < br /> < input type="hidden" name="Photo" value="QVNQLk5FVCBNVkMgaXMgZnVuIQ................................." / > </ p > < p > 7、在 VS 2010 中增加了 asp.net mvc 相关的代码片段 < br /> 组合键 ctrl+k ctrl+b 调出代码片段管理器后查看 </ p > < p > 8、新增了一个名为 RequireHttpsAttribute 的 Action Filter - 其作用就是用于只允许 HTTPS 请求。默认情况下,如果是 HTTP 请求,则会自动跳转到 HTTPS 请求 </ p > < p > 9、只有 HTTP POST 请求才能返回 JsonResult </ p > < p > 10、新增了 HttpPostAttribute HttpPutAttribute HttpGetAttribute HttpDeleteAttribute ,以前是类似这样写的 AcceptVerbs(HttpVerbs.Post) </ p > < p > 11、UrlParameter.Optional - 如果从url路由中无法获取某个参数的值,则从url参数中获取该参数的值。示例如下 < br /> routes.MapRoute( "Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); < br /> < a href ="Category/?pageIndex=0" target ="_blank" > 使用 UrlParameter.Optional 的一个演示 </ a > </ p > < p > 12、对 Area 的支持 - 允许在一个应用程序内存在多套 MVC,在项目中单击右键 -> Add -> Area...(详见示例程序中的 Areas 目录) </ p > < p > 13、新增的一些帮助方法 < br /> Html.EditorFor(), Html.DisplayFor(), Html.DisplayTextFor(), Html.ValidationMessageFor(), Html.TextBoxFor(), Html.TextAreaFor(), Html.DropDownListFor(), Html.CheckboxFor(), Html.RadioButtonFor(), Html.ListBoxFor(), Html.PasswordFor(), Html.HiddenFor(), Html.LabelFor() < br /> 它们主要用于实现 Scaffold,以及为 Property 或 Model 指定显示模板或编辑模板 </ p > </ asp:Content >
代码
Areas/AsynchronousController/Views/Home/Index.aspx /* * ASP.NET MVC 2.0 - 异步 Controller * 1、需要继承基类 AsyncController * 2、在 Async 为后缀的方法中写发起异步操作的逻辑;在 Completed 为后缀的方法中写异步操作完成后的逻辑;此两个方法的前缀就是这个异步 Controller 的 Action * 3、AsyncManager.OutstandingOperations.Increment() - 递增当前异步操作的计数器;AsyncManager.OutstandingOperations.Decrement(); - 递减当前异步操作的计数器。当异步操作的计数器为 0 时,则调用以 Completed 为后缀的方法 * 4、AsyncManager.Parameters[key] - 传递指定参数到以 Completed 为后缀的方法中 */ using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Net; using System.Xml; using System.ServiceModel.Syndication; namespace MVC20.Areas.AsynchronousController.Controllers{ [HandleError] public class HomeController : AsyncController { // IndexAsync() 以及 IndexCompleted() 是一对,其 Action 为 Index // 开始一个异步操作 public void IndexAsync() { // 递增计数器 AsyncManager.OutstandingOperations.Increment(); HttpWebRequest request = (HttpWebRequest)WebRequest.Create( " http://www.cnblogs.com/webabcd/rss " ); request.BeginGetResponse( new AsyncCallback(OnGetRssFeedAsyncCompleted), request); } // 完成异步操作后 public ActionResult IndexCompleted(IEnumerable < SyndicationItem > items) { ViewData[ " Message " ] = " Welcome to ASP.NET MVC! " ; ViewData[ " SyndicationItems " ] = items; return View(); } private void OnGetRssFeedAsyncCompleted(IAsyncResult result) { HttpWebRequest request = (HttpWebRequest)result.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result); using (XmlReader reader = XmlReader.Create(response.GetResponseStream())) { SyndicationFeed feed = SyndicationFeed.Load(reader); // 传递指定参数到 IndexCompleted() 方法中 AsyncManager.Parameters[ " items " ] = feed.Items; // 递减计数器,当计数器为 0 时,则调用 IndexCompleted() AsyncManager.OutstandingOperations.Decrement(); } } }}
代码
3、Area 的 Demo Areas/AsynchronousController/AsynchronousControllerAreaRegistration.cs <% @ Page Language = " C# " Inherits = " System.Web.Mvc.ViewPage<dynamic> " %> <% @ Import Namespace = " System.ServiceModel.Syndication " %> <! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" > < html xmlns ="http://www.w3.org/1999/xhtml" > < head runat ="server" > < title > Index </ title > </ head > < body > < div > < h2 > <% : ViewData[ " Message " ] %> </ h2 > < div > <!-- 调用异步 Controller 的 Demo 注意:Inherits="System.Web.Mvc.ViewPage<dynamic>",这里用了 dynamic 类型 --> <% foreach (SyndicationItem item in (IEnumerable < SyndicationItem > )ViewData[ " SyndicationItems " ]) { %> < a href ="<%= item.Id %>" > <% = item.Title.Text %> </ a >< br /> <% } %> </ div > </ div > </ body > </ html >
代码
Global.asax /* * ASP.NET MVC 2.0 - 对 Area 的支持,其允许在一个应用程序内实现多套 MVC * 1、新建 Area:右键 -> Add -> Area... * 2、继承 AreaRegistration,配置对应此 Area 的路由 * 3、在 Global 中通过 AreaRegistration.RegisterAllAreas(); 注册此 Area * 4、有了 Area,就一定要配置路由的命名空间 */ using System.Web.Mvc; namespace MVC20.Areas.AsynchronousController{ // 新建一个 Area 会自动生成这个继承自 AreaRegistration 的类 // 如果需要使用此 Area 下的 MVC, 需要在 Global 中 AreaRegistration.RegisterAllAreas(); public class AsynchronousControllerAreaRegistration : AreaRegistration { public override string AreaName { get { return " AsynchronousController " ; } } public override void RegisterArea(AreaRegistrationContext context) { // 在 Area 中配置路由的时候,要设置命名空间(即本例中的第 4 个参数) context.MapRoute( " AsynchronousController_default " , " AsynchronousController/{controller}/{action}/{id} " , new { controller = " Home " , action = " Index " , id = UrlParameter.Optional }, // Parameter defaults new string [] { " MVC20.Areas.AsynchronousController.Controllers " } ); } }}
代码
4、对 DataAnnotations 的支持,实现数据验证(包括客户端验证) Metadata.cs using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace MVC20{ public class MvcApplication : System.Web.HttpApplication { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute( " {resource}.axd/{*pathInfo} " ); // 用于本项目中使用了 Area,所以在配置路由的时候,要设置命名空间(即本例中的第 4 个参数) routes.MapRoute( " Default " , // Route name " {controller}/{action}/{id} " , // URL with parameters new { controller = " Home " , action = " Index " , id = UrlParameter.Optional }, // UrlParameter.Optional - 如果从url路由中无法获取某个参数的值,则从url参数中获取该参数的值 new string [] { " MVC20.Controllers " } ); } protected void Application_Start() { // 注册本应用程序中的所有 Area AreaRegistration.RegisterAllAreas(); RegisterRoutes(RouteTable.Routes); } }}
代码
CategoryController.cs /* * ASP.NET MVC 2.0 - 对 DataAnnotations 的支持,可以实现验证及 Scaffold */ using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.ComponentModel.DataAnnotations; namespace MVC20.Models{ [MetadataType( typeof (ProductCategory_Metadata))] public partial class ProductCategory { } public class ProductCategory_Metadata { [Required(AllowEmptyStrings = false , ErrorMessage = " {0} 为必填字段 " )] [NameValidation(ErrorMessage = " {0} 只能是数字 " )] [Display(Name = " 名称 " )] public string Name; [ScaffoldColumn( false )] public object ModifiedDate { get ; set ; } // DisplayValue=true - 默认值。在显示模板中,用一个 lable 显示指定的值;在编辑模板中,用一个 lable 显示指定的值,并用一个 hidden input 保存该值 // DisplayValue=false - 在显示模板中,不做任何显示;在编辑模板中,不做任何显示,但会用一个 hidden input 保存该值 [System.Web.Mvc.HiddenInput(DisplayValue = true )] public object rowguid { get ; set ; } } // 通过继承 RegularExpressionAttribute 实现自定义验证 Attribute // 通过继承 ValidationAttribute 实现自定义验证 Attribute 的 Demo 可以参看 http://www.cnblogs.com/webabcd/archive/2009/02/23/1396212.html public class NameValidationAttribute : RegularExpressionAttribute { public NameValidationAttribute() : base ( " ^[0-9]*$ " ) { } }}
代码
Create.aspx using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using MVC20.Models; using System.ComponentModel; namespace MVC20.Controllers{ public class CategoryController : Controller { // DefaultValueAttribute - 用于为参数指定默认值。当路由或url参数中无此参数时,则会使用此默认值 public ActionResult Index([DefaultValue( 2 )] int pageIndex) { int pageSize = 10 ; var categories = new Models.CategorySystem().GetCategory(pageIndex, pageSize); return View( " Index " , categories); } public ActionResult Edit( int id) { var category = new Models.CategorySystem().GetCategory(id); return View( " Edit " , category); } // 用于演示实现 Model 级别的 Dynamic Data [HttpPost] public ActionResult Edit( int id, FormCollection formValues) { var cs = new Models.CategorySystem(); var category = cs.GetCategory(id); TryUpdateModel < ProductCategory > (category); cs.Save(); return RedirectToAction( " Index " ); } public ActionResult Edit2( int id) { var category = new Models.CategorySystem().GetCategory(id); return View( " Edit2 " , category); } // 用于演示实现 Property 级别的 Dynamic Data [HttpPost] public ActionResult Edit2( int id, FormCollection formValues) { var cs = new Models.CategorySystem(); var category = cs.GetCategory(id); TryUpdateModel < ProductCategory > (category); cs.Save(); return RedirectToAction( " Index " ); } public ActionResult Details( int id) { var category = new Models.CategorySystem().GetCategory(id); return View( " Details " , category); } public ActionResult Create() { ProductCategory category = new ProductCategory() { }; return View(category); } // 用于演示通过 DataAnnotations 实现数据验证 [HttpPost] public ActionResult Create(ProductCategory category) { if ( ! ModelState.IsValid) { return View(category); } else { var cs = new Models.CategorySystem(); cs.AddCategory(category); cs.Save(); return View( " Details " , category); } } }}
代码
5、MVC 与 Dynamic Data 相结合的 Demo 相关的 DataAnnotations 就用上面的那个 Category/Edit.aspx <% @ Page Title = "" Language = " C# " MasterPageFile = " ~/Views/Shared/Site.Master " Inherits = " System.Web.Mvc.ViewPage<MVC20.Models.ProductCategory> " %> < asp:Content ID ="Content1" ContentPlaceHolderID ="TitleContent" runat ="server" > Create </ asp:Content > < asp:Content ID ="Content2" ContentPlaceHolderID ="MainContent" runat ="server" > <!-- 为了实现客户端验证,需要引用以下两个 JavaScript --> < script src ="http://www.cnblogs.com/Scripts/MicrosoftAjax.js" type ="text/javascript" ></ script > < script src ="http://www.cnblogs.com/Scripts/MicrosoftMvcValidation.js" type ="text/javascript" ></ script > < h2 > Create </ h2 > <!-- 启用客户端验证功能 --> <% Html.EnableClientValidation(); %> <% using (Html.BeginForm()) { %> <!-- Html.ValidationSummary Helper Method Can Display Model-Level Errors --> <% : Html.ValidationSummary( " 错误信息如下: " ) %> < fieldset > < legend > Fields </ legend > < div class ="editor-label" > <% : Html.LabelFor(model => model.ProductCategoryID) %> </ div > < div class ="editor-field" > <% : Html.TextBoxFor(model => model.ProductCategoryID) %> <!-- 验证的错误信息来自 DataAnnotations,也可以通过 ValidationMessageFor() 来指定需要显示的错误信息 --> <% : Html.ValidationMessageFor(model => model.ProductCategoryID, " * " ) %> </ div > < div class ="editor-label" > <% : Html.LabelFor(model => model.ParentProductCategoryID) %> </ div > < div class ="editor-field" > <% : Html.TextBoxFor(model => model.ParentProductCategoryID) %> <% : Html.ValidationMessageFor(model => model.ParentProductCategoryID) %> </ div > < div class ="editor-label" > <% : Html.LabelFor(model => model.Name) %> </ div > < div class ="editor-field" > <% : Html.TextBoxFor(model => model.Name) %> <% : Html.ValidationMessageFor(model => model.Name) %> </ div > < div class ="editor-label" > <% : Html.LabelFor(model => model.rowguid) %> </ div > < div class ="editor-field" > <% : Html.TextBoxFor(model => model.rowguid) %> <% : Html.ValidationMessageFor(model => model.rowguid) %> </ div > < div class ="editor-label" > <% : Html.LabelFor(model => model.ModifiedDate) %> </ div > < div class ="editor-field" > <% : Html.TextBox( " ModifiedDate " , DateTime.Now.ToString( " yyyy-MM-dd " )) %> <% : Html.ValidationMessageFor(model => model.ModifiedDate) %> </ div > < p > < input type ="submit" value ="Create" /> </ p > </ fieldset > <% } %> < div > <% : Html.ActionLink( " Back to List " , " Index " ) %> </ div > </ asp:Content >
代码
Category/Edit2.aspx <% @ Page Title = "" Language = " C# " MasterPageFile = " ~/Views/Shared/Site.Master " Inherits = " System.Web.Mvc.ViewPage<MVC20.Models.ProductCategory> " %> < asp:Content ID ="Content1" ContentPlaceHolderID ="TitleContent" runat ="server" > Edit </ asp:Content > < asp:Content ID ="Content2" ContentPlaceHolderID ="MainContent" runat ="server" > < h2 > Edit </ h2 > <% using (Html.BeginForm()) { %> <% : Html.ValidationSummary( " 错误信息如下: " ) %> <% = Html.EditorFor(category => category) %> < input type ="submit" value ="Save" /> <% } %> < div > <% : Html.ActionLink( " Back to List " , " Index " ) %> </ div > </ asp:Content >
代码
Category/Index.aspx <% @ Page Title = "" Language = " C# " MasterPageFile = " ~/Views/Shared/Site.Master " Inherits = " System.Web.Mvc.ViewPage<MVC20.Models.ProductCategory> " %> < asp:Content ID ="Content1" ContentPlaceHolderID ="TitleContent" runat ="server" > Edit2 </ asp:Content > < asp:Content ID ="Content2" ContentPlaceHolderID ="MainContent" runat ="server" > < h2 > Edit2 </ h2 > <% using (Html.BeginForm()) { %> <% : Html.ValidationSummary( " 错误信息如下: " ) %> < fieldset > < legend > Fields </ legend > <% = Html.LabelFor(c => c.ParentProductCategoryID) %> : <% = Html.EditorFor(c => c.ParentProductCategoryID) %> </ br > <% = Html.LabelFor(c => c.Name) %> : <% = Html.EditorFor(c => c.Name) %> < p > < input type ="submit" value ="Save" /> </ p > </ fieldset > <% } %> < div > <% : Html.ActionLink( " Back to List " , " Index " ) %> </ div > </ asp:Content >
代码
Shared/DisplayTemplates/MyDate.ascx <% @ Page Title = "" Language = " C# " MasterPageFile = " ~/Views/Shared/Site.Master " Inherits = " System.Web.Mvc.ViewPage<IEnumerable<MVC20.Models.ProductCategory>> " %> < asp:Content ID ="Content1" ContentPlaceHolderID ="TitleContent" runat ="server" > Index </ asp:Content > < asp:Content ID ="Content2" ContentPlaceHolderID ="MainContent" runat ="server" > < h2 > Index </ h2 > < table > < tr > < th > </ th > < th > ProductCategoryID </ th > < th > ParentProductCategoryID </ th > < th > 名称 </ th > < th > rowguid </ th > < th > ModifiedDate </ th > </ tr > <% foreach (var item in Model) { %> < tr > < td > <% : Html.ActionLink( " Edit " , " Edit " , new { id = item.ProductCategoryID }) %> | <% : Html.ActionLink( " Details " , " Details " , new { id = item.ProductCategoryID }) %> | <% : Html.ActionLink( " Delete " , " Delete " , new { id = item.ProductCategoryID }) %> </ td > < td > <% : item.ProductCategoryID %> </ td > < td > <% : item.ParentProductCategoryID %> </ td > < td > <% : Html.DisplayTextFor(c => item.Name) %> </ td > < td > <% : item.rowguid %> </ td > < td > <!-- Html.DisplayFor() - 可以指定显示模板(可以指定 Property 级模板,也可以指定 Model 级模板),本例的模板地址为 Views/Shared/DisplayTemplates/MyDate.ascx ,这种实现方式来自 Dynamic Data --> <% = Html.DisplayFor(Product => item.ModifiedDate, " MyDate " ) %> </ td > </ tr > <% } %> </ table > < p > <% = Html.RouteLink( " 上一页 " , " Default " , new { pageIndex = Convert.ToInt32(Request.QueryString[ " pageIndex " ] ?? " 2 " ) - 1 }) %> | <% = Html.RouteLink( " 下一页 " , " Default " , new { pageIndex = Convert.ToInt32(Request.QueryString[ " pageIndex " ] ?? " 2 " ) + 1 }) %> </ p > < p > <% : Html.ActionLink( " Create New " , " Create " ) %> </ p > </ asp:Content >
<% @ Control Language = " C# " Inherits = " System.Web.Mvc.ViewUserControl<dynamic> " %> <% = Html.Encode( String .Format( " {0:yyyy-MM-dd} " , Model)) %>