第 5 章 ? MVC核心透析 ? 本章主要介绍MVC的一些高级特性和核心技术。 5.1 MVC Routing 在项目要使用Routing(路由),需要添加程序集引用System.Web.Routing.dll,而在ASP.NET MVC项目中则自动添加了此引用。严格来说,System.Web.Routing.dll并不属于ASP.NET MVC的一部分,微软把此项目单独列出来了,并没有开放源代码,但是未来微软也许会把Routing开源,毕竟开源是大势所趋,而且微软也在不断地拥抱开源。 MapRoute()有如下重载,它们都是扩展方法。 public class AreaRegistrationContext { // Fields private readonly HashSet _namespaces; // Methods public AreaRegistrationContext(string areaName, RouteCollection routes); public AreaRegistrationContext(string areaName, RouteCollection routes, object state); public Route MapRoute(string name, string url); public Route MapRoute(string name, string url, object defaults); public Route MapRoute(string name, string url, string[] namespaces); public Route MapRoute(string name, string url, object defaults, object constraints); public Route MapRoute(string name, string url, object defaults, string[] namespaces); public Route MapRoute(string name, string url, object defaults, object onstraints, string[] namespaces); // Properties public string AreaName { get; private set; } public ICollection Namespaces { get; } public RouteCollection Routes { get; private set; } public object State { get; private set; } } MapRoute参数介绍: ? name:规则名称,可以随意起名,但是不可以重名,否则会发生错误(路由集合中已存在名为“xx”的路由)。路由名必须是唯一的。 ? url:url获取数据的规则,这里不是正则表达式,将要识别的参数括起来即可,如“{controller}/{action};”。只需要传递name和url参数就可以建立一条Routing(路由)规则,比如“routes.MapRoute("New","{controller}/{action}");”。 ? constraints:用来限定每个参数的规则或HTTP请求的类型。constraints属性是一个RouteValueDictionary对象,也就是一个字典表,但是这个字典表的值可以有两种: ? 用于定义正则表达式的字符串,正则表达式不区分大小写。 ? 一个用于实现IRouteConstraint接口且包含Mathc方法的对象。 通过使用正则表达式可以规定参数格式化,比如Controller参数只能为6位数字: new{controller=@"\d{6}"} ? namespaces:string[]类型,指定在哪些命名空间下面查找控制器进行匹配。如果不设置此属性,默认是在bin目录下面遍历所有的dll,依次查找这些dll中继承自Controller的类进行匹配。 5.1.1 Routing——URL 作为广泛使用的Web用户接口,URL需要被重视,因为好的URL有利于SEO(Search Engine Optimization)优化。那么什么是好的URL呢? 好的URL应该满足如下条件: ? URL应为获取某种资源提供信息,不一定是物理文件路径。 ? 简短易于记忆和拼写输入。 ? 可以反映出站点结构。 ? 应该是“可拆分”,用户移除末尾,进而获得更高层次信息。 ? 持久、不应改变。 对一个网站而言,为了SEO友好,一个网址的URL层次最好不要超过三层:http://localhost/{分类}/{具体页}; 其中,域名作为第一层,分类作为第二层,最后的网页就是最后一层了。使用默认路由中的"{controller}/{action}/{id}"形式会影响网站的SEO。 下面的两种URL你更喜欢哪种呢? http://www.cnblogs.com/jiekzou/1.html http://www.cnblogs.com/jiekzou/1 如果是后者,那么服务器要怎么识别呢?ASP.NET MVC 使用路由机制完成由URL到具体调用方法的映射过程。 注:在以前传统的ASP.NET WebForm开发中,Url代表服务器磁盘上的物理文件。 5.1.2 Routing的作用 Routing的作用是: ? 匹配传入的请求(不匹配服务器物理文件),并将请求映射到“控制器”的具体操作“Action方法”和“参数”中。 ? 调用并执行对应控制器类的Action方法。 Global.asax.cs文件中的Application_Start()方法里面进行了路由注册: RouteConfig.RegisterRoutes(RouteTable.Routes); App_Start文件夹中的RouteConfig.cs定义了路由的识别规则: public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default",// 1.路由名称 url: "{controller}/{action}/{id}",// 2.带有参数的URL defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 3.参数默认值,(UrlParameter.Optional表示可选的意思) ); } 注:{controller}和{action}是特定参数名,不能改。RegisterRoutes方法中可以注册多个路由,通常越详细的路由配置放置到越前面,但是路由名称不能重复,路由按照先后顺序与传入的URL匹配,直到匹配成功为止,一旦有路由匹配成功,就不会再继续匹配,如果所有的路由都没有匹配上就会报错。 5.1.3 Routing包含字面值的URL 路由URL在段中也允许包含“字面值”,如/jiekzou/{controller}/{action}/{id}规定第一段必须以jiekzou 开头才能与该路由匹配,如/jiekzou/home/index/1URL中可以将字面量和参数混合在一期,如图5-1所示。 图5-1 不能有两个连续的URL参数,参数之间必须有字符隔开,如{controller}{action}-{id}就是错误的。 (1)路由约束 允许URL段使用正则表达式来限制路由是否匹配请求,通过constraints属性来配置。 routes.MapRoute( name: "Default",// 1.路由名称 url: "{controller}/{action}/{id}",// 2.带有参数的URL defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // 3.参数默认值,(UrlParameter.Optional表示可选的意思) constraints: new { controller = @"Home", action =@"Index|Test"} //正则 ); 在Home控制器中添加3个Action方法和对应的视图,即Index、Test、Test1,然后在URL地址栏访问,发现Test1的Action无法访问到,因为我们在路由里面进行的正则校验规则不包括名为Test1的Action。 (2)命名路由 生成指定路由名的URL超链接: @Html.RouteLink("链接文本", "blog",new {contorller="home",action ="index",id=1 } ) 5.1.4 Routing测试 在项目上右击,选择“管理NuGet程序包(N)…→联机”→搜索“RouteDebugger”,单击“安装”按钮,如图5-2所示。 图5-2 安装完成之后,就会发现在项目中自动添加了RouteDebugger引用,如图5-3所示。 图5-3 而且在Web.config中,节点里面自动添加了如下配置代码: 由于我们使用的是MVC 4,因此只需要按F5键运行程序即可看到效果。浏览器地址为http://localhost:18901/,页面效果如图5-4所示。 图5-4 对于.NET 3.5和MVC 3之前的项目,如果要使用RouteDebugger,还需要在Application_Start中注册: protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterRoutes(RouteTable.Routes); //注册RouteDebug RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes); } 要禁用RouteDebugger,只需在Web.config中修改配置文件即可(将value值修改为false): !--禁用--> 5.2 异步Ajax Ajax异步刷新应用在Web开发中经常用到;在过去WebForm中通常是使用jQuery和一般处理程序或者aspx页面来实现的;在MVC中,虽然依旧可以使用一般处理程序,但是一般还是通过在Controller中新建Action方法来实现。 5.2.1 传统Ajax实现方式 1. jQuery+一般处理程序 (1)右击项目,选择“添加→HTML页”,命名为ShowDateTime.html,引入jquery文件,添加如下代码:
(2)右击项目,选择“添加→新建项→一般处理程序”,并设置名称,这里设为“GetDateTimeHandler.ashx”。 public class GetDateTimeHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; context.Response.Write(DateTime.Now.ToString()); } public bool IsReusable { get { return false; } } } (3)运行ShowDateTime.html(右击该文件,在浏览器中查看),如图5-5所示。 图5-5 2. jQuery+Action (1)在控制器中添加Action方法ShowEmployeesList。这里用到了对象序列化,我们在新建ASP.NET MVC项目的时候,VS自动帮我们引入了System.Web.Extensions.dll,此程序集中的JavaScriptSerializer类提供了序列化和反序列化的方法。 [HttpGet] public ActionResult ShowEmployeesList() { return View(); } [HttpPost] public ActionResult GetEmployeesList() { using (NorthwindEntities db=new NorthwindEntities()) { var list = db.Employees.Select(x => new { FirstName=x.FirstName,LastName=x.LastName,City=x.City }).ToList(); System.Web.Script.Serialization.JavaScriptSerializer jss = new System.Web.Script.Serialization.JavaScriptSerializer(); return Content(jss.Serialize(list)); //return Content(JsonConvert.SerializeObject(list)); } } 同时,VS也自动添加了Newtonsoft.Json.dll这个第三方的dll程序集,功能比System.Web.Extensions.dll更加强大,性能更好。使用方式一是添加命名空间引用“using Newtonsoft.Json;”,二是调用代码“return Content(JsonConvert.SerializeObject(list));”。 (2)添加视图ShowEmployeesList: @{ ViewBag.Title = "ShowEmployeesList"; } @Scripts.Render("~/Scripts/jquery-1.8.2.min.js")
(3)运行http://localhost:2046/Home/ShowEmployeesList,结果如图5-6所示。 图5-6 5.2.2 Unobtrusive Ajax使用方式 通俗来讲,非入侵式就是将嵌入HTML中的JavaScript全部取出来,放在单独的 js 文件中,HTML标签中不要出现任何onclick、onload 等。 Unobtrusive Ajax方便程序员编写简单易于维护的Ajax代码(Code is cleaner and easier to maintain),基本特点如下: ? 网页内容和表单使用纯 HTML。 ? 无须借助 JavaScript,表单和超链接也能正常使用。 ? 页面外观完全由 CSS 控制,而不是 HTML(不要用 table 来布局)或JavaScript。 ? 任何人都能通过任何设备(考虑不支持JavaScript的设备)访问。 ASP.NET MVC 全局开启非入侵Ajax ASP.NET MVC 4已经默认开启客户端验证和非侵入式js。在Web.config中可以看到如下配置信息: 如果只想在指定页面使用此功能,可以在页面中单独添加非入侵js文件: @Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js") 或者 这两种方法都可以,推荐使用@Scripts.Render的形式,因为它支持压缩合并和服务器缓存(后面会讲)。 我们还可以在单个视图页面(View)上关闭非入侵Ajax功能,如下: @{Html.EnableUnobtrusiveJavaScript(false);} @{Html.EnableClientValidation(false);} 注意js的引用顺序: @Scripts.Render( "~/Scripts/jquery.min.js") @Scripts.Render("~/Scripts/jquery.validate.min.js") @Scripts.Render("~/Scripts/jquery.validate.unobtrusive.min.js") @Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js") 其中两个validate的js文件是用于异步验证的。 5.2.3 AjaxHelper 1. 异步链接按钮 使用异步链接按钮时,必须开启非入侵式Ajax(导入jQuery和unobtrusive相关的Ajax文件)。 (1)在View中,@Ajax.ActionLink创建Ajax超链接按钮,一般用来请求动态生成的部分HTML代码(分部视图)或者普通的字符串文本。 @Ajax.ActionLink("链接文本", "PartialViewTest", new AjaxOptions() { UpdateTargetId="divMsg",//数据显示的html容器id InsertionMode= InsertionMode.Replace, //替换容器内容 HttpMethod="Post" }) (2)在Controller中: public PartialViewResult PartialViewTest() { ViewData["Msg"] = "Hello world!"; return PartialView(); } 生成的HTML代码如下: 链接文本 2. 异步表单 (1)在Controller中添加如下Action方法: public ActionResult AjaxForm() { return View(); } public ActionResult BaseInfo(string txtName) { using (NorthwindEntities db = new NorthwindEntities()) { var result = db.Employees.Where(x => x.LastName == txtName).FirstOrDefault(); return Content("姓:"+result.FirstName+"名:"+result.LastName); } } (2)添加名为AjaxForm的View,在该View中通过@Ajax.BeginForm 创建异步表单: @{ ViewBag.Title = "AjaxForm"; } @Scripts.Render( "~/Scripts/jquery-1.8.2.min.js") @Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js") @using (Ajax.BeginForm("BaseInfo", "Home", new AjaxOptions { UpdateTargetId="msgDiv" , InsertionMode= InsertionMode.Replace, HttpMethod="post" , OnFailure= "fail", OnSuccess="success" , LoadingElementId="lodeingmsg"})) { }
运行结果如图5-7~图5-9所示。 图5-7 图5-8 图5-9 View中的代码分析如下: ? UpdateTargetId:目标元素id,获取服务器响应后,将获取的响应报文体显示到目标元素的innerHTML中。 ? InsertionMode:InsertAfter 插入目标元素原有内容之后,InsertBefore 插入目标元素原有内容之前,Replace替换目标元素原有内容。 ? LoadingElementId:异步对象readyState==4之前显示“正在加载”状态的元素id。 ? OnSuccess:请求成功之后,后续执行的js回调函数,可以根据返回的参数确定服务器端处理情况。 ? OnFailure:请求失败之后,后续执行的js回调函数。 ? HttpMethod:设置请求方式(get、post)。 ? Confirm:提交前的回调函数,指定为一个js的function。 生成的HTML代码如下:
AjaxOptions对象生成“对应”触发Ajax请求标签的属性如图5-10所示。 图5-10 5.2.4 请求Json数据 添加控制器Json,在Controller中使用 Json方法返回一个 JsonResult: NorthwindEntities db = new NorthwindEntities(); public ActionResult CustomersList() //返回类型也可写 JsonResult { var dogList = db.Customers.Select(x => new { CustomerID = x.CustomerID, ContactName = x.ContactName, Phone = x.Phone }).ToList(); return Json(dogList, JsonRequestBehavior.AllowGet); } MVC框架默认不允许使用Json响应Get请求,需要开启,开启代码为JsonRequestBehavior.Allow。在GetView中添加代码: @Ajax.ActionLink("click here", "CustomersList", new AjaxOptions() { UpdateTargetId="divMsg", InsertionMode= InsertionMode.Replace, HttpMethod="Get",OnSuccess="ajaxFinish" })
ID 姓名 电话
1. jQuery请求控制器Action 除了URL指向控制器的 Action方法不同外,其他和以前一样,具体参见jQuery帮助文档 ajax 方法: $.ajax $.post $.load $.get 在View中如果不使用@Ajax.ActionLink,那么也可以使用: $(function(){ $('#btnAjax').click(function () { $.get('Json/CustomersList', function (json) { $("#temp").tmpl(json).appendTo("#tbList"); }); }); 2. jQuery 模板插件 从云盘中获取jQuery模板插件jquery.tmpl.min.js,然后在View中导入脚本: @Scripts.Render("~/Scripts/jjquery-1.8.2.min.js") @Scripts.Render("~/Scripts/jquery.tmpl.min.js") 添加模板-占位符格式,即$(json对象属性名): 为模板装载数据并最终生成HTML,添加到表格中: function ajaxFinish(jsonObjArray) { //[{CustomerID:"1",ContactName:"zouqj",Phone:"15243641131"},....{}] $("#temp").tmpl(jsonObjArray).appendTo("#tbList"); } 运行结果如图5-11所示。 图5-11