[转载]ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL
引言--
在初级篇中,我们介绍了如何利用基于ASP.NET MVC的Web程序中的Global文件来简单的重写路由。也介绍了它本身的局限性-依赖于路由信息中的键值对:
如果键值对中没有的值,我们无法将其利用凑出我们想要的URL表达式。
初级篇传送门:使用Global路由表定制URL
在进阶篇中,我们将介绍ASP.NET 路由相关类的基类-抽象类RouteBase,并演示如何通过继承它,让URL重写和优化变成Free Style。
一,老板的需求
假设我们是手机销售网站的一名程序猿(承接初级篇),经过第一次的URL重写之后,我们的手机分类页面的URL的改变:
- http://www.xxx.com/category/showcategory?categoryid=0001&view=list&orderby=price&page=1
=>
http://www.xxx.com/category/0001
现在老板又提出了新的需求,URL的语义化,从而更好的反应网站的结构:
- http://www.xxx.com/ca-categoryname
比如Nokia是一个分类,那么对应URL为 /ca-nokia,如果是iPhone分类,URL则对应 /ca-iphone。ca前缀的意思是分类category。
对于这个需求简单的配置Global文件是无法做到的。首先我们来介绍一下ASP.NET 路由的所有类的基类RouteBase。
二,RouteBase类简介与运行机制
1. RouteBase类位于System.Web.Routing命名空间,结构如下:
- public abstract class RouteBase
- {
- protected RouteBase();
- public abstract RouteData GetRouteData(HttpContextBase httpContext);
- public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
- }
- GetRouteData:根据Http请求信息返回一个对象-包含路由定义的值(如果该路由与当前请求匹配)或 null(如果该路由与请求不匹配)。
- GetVirtualPath:检查路由值是否与某个规则匹配,返回一个对象(包含生成的 URL 和有关路由的信息)或 null(如果路由与 values 不匹配)。
- RouteBase:初始化该类供继承的类实例使用。此构造函数只能由继承的类调用。
看完以上定义,可能大家会晕忽忽。我们来弄一个简单的例子说明这几个方法是如何运作的。
首先我们新建一个类库JohnConnor.Routing,并且继承抽象类RouteBase:(补充:需要添加引用System.Web)

- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
using System.Web.Mvc;//需要添加引用,请使用3.0以上版本- using System.Web.Routing;
- using JohnConnor.Models;
- namespace JohnConnor.Routing
- {
- public class CategoryUrlProvider:RouteBase
- {
- public override RouteData GetRouteData(System.Web.HttpContextBase httpContext)
- {
- return null;//断点1
- }
- public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
- {
- return null;//断点2
- }
- }
- }

这样CategoryUrlProvider类就包含了用来处理路由映射的方法。
首先我们需要在Web程序中添加JohnConnor.Routing类库的引用,然后我们把CategoryUrlProvider类注册到Global文件的路由表中。
- public static void RegisterRoutes(RouteCollection routes)
- {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");- routes.Add(new JohnConnor.Routing.CategoryUrlProvider());//分类规则
routes.MapRoute("Home", "", new { controller = "Home", action = "Index"});//主页
}
这里相当于添加了一条新的路由规则。重新生成一下Web程序在CategoryUrlProvider中打好断点,F5启动。
2. GetRouteData()方法
这时候相当与你在浏览器输入了http//localhost:1234/<假设本地端口号是1234>,此时程序需要判断这个URL匹配的是哪个路由值。
自上而下的匹配,首先会尝试匹配我们新增的分类路由规则,此时会命中GetRouteData()方法中的断点。
因为我们返回了null,意味着该请求与我们新增的分类路由规则不匹配,那程序将在路由表中继续自上而下的进行匹配。
直到在主页这一条规则中与其URL表达式匹配,获取了对应的路由值-调用HomeController.Index()方法。
如果你把GetRouteData()方法修改一下:

- public override RouteData GetRouteData(System.Web.HttpContextBase httpContext)
- {
- var data = new RouteData(this, new MvcRouteHandler());
- data.Values.Add("controller", "Home");
- data.Values.Add("action", "Index");
- return data;
- }

你就会发现,无论你在http//localhost:1234/后面输入任何相对URL,都会被定向到HomeController.Index()方法。
因为返回的是路由值而不是null,表示已经找到匹配项,就不会再往下匹配了。<这条规则覆盖了后面所有的规则>
当然,请不要这样写。。。
由此可以推断出GetRouteData()方法在路由映射中担任的角色:处理请求中的URL,返回相应的路由值,不处理或不匹配则返回null。
3. VirtualPathData()方法
如果你在Razor页面有这样一段通过指定路由值来获取URL的代码
- <a href="@Url.Action("Index", "Home")">首页</a>
当视图引擎渲染页面到这句代码时,HomeController.Index()方法会被解析为一个RouteValueDictionary类型的不分大小写的键值对<假设键值对对象为values>:
- values["controller"]="Home";
- values["action"]="Index";
这个键值对表示了一个路由值。
同样是在路由表中自上而下的匹配这个路由值,尝试第一条分类规则时,就会命中VirtualPathData()方法中的断点。
我们返回一个null,表示不匹配,则程序进行下一个规则的匹配。
直到找到主页规则的路由值与之匹配时,构造出相应的相对URL"",并返回该URL。
显示为:
- <a href="http://localhost:1234/">首页</a>
如果我们也改写一下VirtualPathData()方法:
- public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
- {
- return new VirtualPathData(this, "This-is-a-Test-URL");
- }
结果是你通过上述方法构造的URL不论请求来自哪里,全部都会显示成http://localhost:1234/This-is-a-Test-URL
因为我们返回的是一个相对路径,而不是null,表示已经找到匹配项,则匹配不会往下继续。<同上这条规则覆盖了后面所有的规则>
再一次提示,请不要这样写。。。
由此可看出,VirtualPathData()在路由映射中的活:处理请求与路由键值对,生成相应URL,不处理或不匹配则返回null。
4.方法重写的规则
在上文中,我一再的用红色字体提示,请不要这样写。因为每一个URL的重写类,建议仅仅处理尽可能少的路由映射。
比如CategoryUrlProvider仅处理CategoryController.Show(string categoeyid)这一个Action方法的映射。凡是不是这个方法相关的映射,都返回null。
继续去匹配别的规则。
三,开始动手把~
为了最快的说明问题,我们简化了网站的内容。以下内容有助于理解后面的程序,如果时间充裕,还是自己构建一个网站来尝试以下。
首先我们在JohnConnor.Routing类库中创建Category.cs来保存分类模型,并把所有的分类显示的保存在List<Category>中,

- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- namespace JohnConnor.Models
- {
- //分类模型
- public class Category
- {
- public string CategoeyID { get; set; }
- public string CategoeyName { get; set; }
- }
- public static class CategoryManager
- {
- //这里只显示创建了三个分类作为示例,实际中AllCategories可以从数据源读取。
- public static readonly List<Category> AllCategories = new List<Category>
- {
- new Category(){ CategoeyID="001", CategoeyName="Nokia"},
- new Category(){ CategoeyID="002", CategoeyName="iPhone"},
- new Category(){ CategoeyID="003", CategoeyName="Anycall"}
- };
- }
- }

假设我们网站的CategoryController是这样的。

- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- using JohnConnor.Models;
- namespace JohnConnor.Web.Controllers
- {
- public class CategoryController : Controller
- {
- public ActionResult ShowCategory(string id)
- {
- var category = CategoryManager.AllCategories.Find(c => c.CategoeyID == id);
- return View(category);
- }
- }
- }

首先我们建议,VirtualPathData()和GetRouteData()方法是成双成对出现的。一旦你制定了一条路由规则,比如分类规则/ca-categoryname,那么:
- GetRouteData()必须处理与这条规则匹配的每一条URL,返回相同的路由值;放弃与之不匹配的URL,返回null,让匹配继续。
- VirtualPathData()必须处理与这条规则匹配的每一次路由请求,返回相同的URL;放弃与之不匹配的请求,返回null,让匹配继续。
!!!两者相辅相成的完成了路由值和URL的相互映射,漏掉一个,就不能构成一个完成的路由规则。直接结果是出现404或生成URL地址错误。
GetRouteData()的代码:

- public override RouteData GetRouteData(System.Web.HttpContextBase httpContext)
- {
- var virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath + httpContext.Request.PathInfo;//获取相对路径
virtualPath = virtualPath.Substring(2).Trim('/');//此时URL会是~/ca-categoryname,截取后面的ca-categoryname- if (!virtualPath.StartsWith("ca-"))//判断是否是我们需要处理的URL,不是则返回null,匹配将会继续进行。
- return null;
- var categoryname = virtualPath.Split('-').Last();//截取ca-前缀后的分类名称
- //尝试根据分类名称获取相应分类,忽略大小写
- var category = CategoryManager.AllCategories.Find(c => c.CategoeyName.Equals(categoryname,StringComparison.OrdinalIgnoreCase));
- if (category == null)//如果分类是null,可能不是我们要处理的URL,返回null,让匹配继续进行
- return null;
- //至此可以肯定是我们要处理的URL了
- var data = new RouteData(this, new MvcRouteHandler());//声明一个RouteData,添加相应的路由值
- data.Values.Add("controller", "Category");
- data.Values.Add("action", "ShowCategory");
- data.Values.Add("id", category.CategoeyID);
- return data;//返回这个路由值将调用CategoryController.ShowCategory(category.CategoeyID)方法。匹配终止
- }

VirtualPathData()的代码

- public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
- {
- //判断请求是否来源于CategoryController.Showcategory(string id),不是则返回null,让匹配继续
- var categoryId = values["id"] as string;
- if (categoryId == null)//路由信息中缺少参数id,不是我们要处理的请求,返回null
- return null;
- //请求不是CategoryController发起的,不是我们要处理的请求,返回null
- if (!values.ContainsKey("controller") || !values["controller"].ToString().Equals("category",StringComparison.OrdinalIgnoreCase))
- return null;
- //请求不是CategoryController.Showcategory(string id)发起的,不是我们要处理的请求,返回null
- if (!values.ContainsKey("action") || !values["action"].ToString().Equals("showcategory", StringComparison.OrdinalIgnoreCase))
- return null;
- //至此,我们可以确定请求是CategoryController.Showcategory(string id)发起的,生成相应的URL并返回
- var category = CategoryManager.AllCategories.Find(c => c.CategoeyID == categoryId);
- if (category == null)
- throw new ArgumentNullException("category");//找不到分类抛出异常
- var path = "ca-" + category.CategoeyName.Trim();//生成URL
- return new VirtualPathData(this, path.ToLowerInvariant());
- }

至此,我们就把这条路由规则的映射处理完整了。如果你掌握了上述技术,任何的URL重写和优化需求,我相信你都能Hold住。
如果我们的主页页面是这样<Razor视图引擎>:
三个分类连接会得到这样的结果
- <a href="/ca-nokia">Nokia</a>
- <a href="/ca-iphone">iPhone</a>
- <a href="/ca-anycall">Anycall</a>
点击每一个连接都会先进入我们的处理程序,生成相应的路由值-调用CategoryController.Showcategory(string id)方法根据id显示相应的分类页面。
原文地址:http://www.cnblogs.com/John-Connor/archive/2012/05/03/2478821.html
[转载]ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL的更多相关文章
- ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL
http://www.cnblogs.com/John-Connor/archive/2012/05/03/2478821.html 引言-- 在初级篇中,我们介绍了如何利用基于ASP.NET MVC ...
- [转]ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL
本文转自:http://www.cnblogs.com/John-Connor/archive/2012/05/03/2478821.html 引言-- 在初级篇中,我们介绍了如何利用基于ASP.NE ...
- ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase
原文地址:http://www.51csharp.com/MVC/882.html ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL 引言-- 在初级篇中,我们 ...
- ASP.NET MVC URL重写与优化(初级篇)-使用Global路由表定制URL
ASP.NET MVC URL重写与优化(初级篇)-使用Global路由表定制URL 引言--- 在现今搜索引擎制霸天下的时代,我们不得不做一些东西来讨好爬虫,进而提示网站的排名来博得一个看得过去的流 ...
- ASP.NET MVC URL重写与优化(1)-使用Global路由表定制URL
ASP.NET MVC URL重写与优化(1)-使用Global路由表定制URL 引言--- 在现今搜索引擎制霸天下的时代,我们不得不做一些东西来讨好爬虫,进而提示网站的排名来博得一个看得过去的流量. ...
- [转载] ASP.NET MVC (一)——深入理解ASP.NET MVC
个人认为写得比较透彻得Asp.net mvc 文章,所以转载过来,原文链接在最后: ASP.NET vs MVC vs WebForms 许多ASP.NET开发人员开始接触MVC认为MVC与ASP.N ...
- 转载——Asp.Net MVC+EF+三层架构的完整搭建过程
转载http://www.cnblogs.com/zzqvq/p/5816091.html Asp.Net MVC+EF+三层架构的完整搭建过程 架构图: 使用的数据库: 一张公司的员工信息表,测试数 ...
- 转载ASP.NET MVC 和ASP.NET Web Form简单区别
转载原地址 http://www.cnblogs.com/lei2007/p/3315431.html 概论: Asp.net 微软 提供web开发框架或者技术.分Web Form和ASP.NET ...
- 转载 ASP.NET MVC中使用ASP.NET Identity
转载原地址: http://blog.jobbole.com/90695/ 在之前的文章中,我为大家介绍了OWIN和Katana,有了对它们的基本了解后,才能更好的去学习ASP.NET Identit ...
随机推荐
- g++ 编译和链接(转)
传统意义上的编译程序分两步走 —— 编译和链接: 1.编译(compile):指用编译器(compiler)将源代码(source code)生成二进制目标文件(object file),在Windo ...
- android代码设置、打开WLAN wifi热点及热点的连接
其实创建热点很简单,先获取到wifi的服务,再配置热点名称.密码等等,然后再通过反射打开它就OK了. 下面我们看看创建热点的代码实现: 这一段是开启WLAN热点,并可以指定好它的热点名和密码 支行后, ...
- Sql Server Profiler跟踪死锁
Sql Server Profiler一个很重要的应用场景就是监视分析死锁. 下面通过一个例子来学习Profiler是如何监视死锁的. 1. 创建测试数据模拟死锁, 创建表Table_A和Table_ ...
- jQuery概述,代码举例及最新版下载
jQuery是一个快速的,小巧的,具有强大功能的JavaScript库. 它的基本功能包括: 1)访问和操作DOM元素 2)控制页面样式(可以兼容各种浏览器) 3)对页面事件的处理 4)大量插件在页面 ...
- mysql绿色版在windows系统中的启动
mysql绿色版在windows系统中的启动 1.下载mysql免安装版 例如:mysql-5.7.11-winx64 2.修改配置文件,my-default.ini名称改为:my.ini,文件里面的 ...
- SpringInAction读书笔记--第1章Spring之旅
1.简化Java开发 Spring是一个开源框架,它的根本使命在于简化java开发.为了降低java开发的复杂性,Spring采取了以下4种关键策略: 基于POJO的轻量级和最小侵入性编程 ...
- Z-Stack内部API 小结
Z-Stack是TI推出的全功能ZigBee协议栈,通过了ZigBee联盟的兼容性平台测试,包含如下几个组件. 1. HAL,硬件抽象层 2. OSAL,操作系统抽象层 3. ZigBee Stack ...
- 一个疑惑的的问题-ntvdm.exe进程
今天测试反馈了一个问题,在启动我们程序某个模块的程序时,会立即出现一个ntvdm.exe进程,此进程会占用大量的系统资源,导致系统卡住. 当第一眼看到这个现象时,以为是电脑中毒了,所以立即在网上查. ...
- httpd 配置用户访问认证
需求:单用户访问网站的某个目录,需要使用帐号密码来登录才能访问. 一.编辑虚拟主机的配置文件,添加目录级访问限制 <Directory "/var/www/html/demo" ...
- php session学习笔记(实例代码)
http 无状态协议 一个服务器向客户端发送消息的时候有三条信息 一是状态二是头信息三是内容 会话控制 让一个用户访问每个页面,服务器都知道是哪个用户访问 cookie cookie是通过头信息发送 ...