介绍

不知道大家在使用 ASP.NET MVC 时有没有一些扩展要求,反正我是有很多。在使用 MVC 这几年(PS:我是从 1.0 开始学,2.0、3.0 开发至今),我深深地觉得 MVC 的扩展性真是太好了,几乎你大部分的“合理”需求,用 MVC 都能实现。好了,废话不多说了,今天我就实战演示如何扩展 ASP.NET Route,希望能帮助到你。

小写 URL

我想很多朋友和我一样,使用 ASP.NET MVC 时都想要小写的 URL。一般除非你在开发时手动把 Controller、Action 的名字建成小写,或者在 Action 方法上标记 ActionNameAttribute,否则如果不经过扩展,很难实现完全的小写 URL。把 Controller、Action 的名字建成小写不符合 C# 编码规范,当然如果你忽略这个规范,我也没有办法。另一方面,在 Action 方法上标记 ActionNameAttribute 工作量又太大,又不利于统一维护。至于为什么要小写的 URL,我想是一种习惯吧,仔细观察各大网站,你就会发现,小写的 URL 是主流。

个性 URL

实现完小写 URL 后,我还有一个特殊的 URL 规则要求,就是生成 A 元素的 URL 时,把 Action 的名字中的下划线(_)替换成减号(-),并且服务器端 ASP.NET MVC 能处理这个请求。打一个比如:

public class HomeController : Controller
{
/// <summary>
/// 添加产品信息,可以通过访问 URL:/home/add-product-information
/// </summary>
/// <returns></returns>
public ActionResult Add_Product_Information()
{
return View();
}
}

上面的例子,大家注意到了吗,我希望使用 @Url.Action("Add_Product_Information") 能生成 /home/add-Product-Information 格式的 URL,并且还能访问。很多朋友可能想到了,以为通过简单的在 Global.asax 中配置路由可以实现,经过本人的实测,是无法实现的,因为配置路由时,比如: "{controller}/{action}/{id}" 这里的 {action} 是作为一个整体,而无法再把它拆分。像这种就只能通过扩展 Route 来实现了。 作者(音乐让我说)博客地址:http://music.cnblogs.com

开始实战

1. 建立一个 LowercaseRoute,继承 System.Web.Routing.Route 类

在这个 LowercaseRoute 类里,我们需要做的是添加和 Route 类相似的构造函数,并且重写 GetVirtualPath 方法。关键代码如下:

public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
VirtualPathData virtualPath = base.GetVirtualPath(requestContext, values); // 由 MVC 默认生成的 URL,比如:/Home/Add_Product_Information
if (virtualPath != null)
{
string virtualPathValue = virtualPath.VirtualPath; // 获取比如:/Home/Add_Product_Information
int length = virtualPathValue.LastIndexOf("?");
if (length == ) return virtualPath;
if (length > )
{
string url1 = virtualPathValue.Substring(, length).ToLowerInvariant(); // 把 controller 和 action 小写
string url2 = virtualPathValue.Substring(length); // 保留 ? 后的参数
virtualPath.VirtualPath = url1 + url2; // 连接,比如:/home/add-product-information
return virtualPath;
}
virtualPath.VirtualPath = virtualPath.VirtualPath.ToLowerInvariant();
}
return virtualPath;
}

以上这个类会在当你在 View、Controller 中调用 Url.Action("Add_Product_Information")、Html.ActionLink("Add_Product_Information") 时调用。

2. 建立一个 UnderlineRoute,继承 LowercaseRoute 类

上面那个 LowercaseRoute 类仅仅是实现 URL 小写的需求,而现在这个 UnderlineRoute 才是在小写 URL 的基础上再实现个性化的 URL 需求(PS:就是本文开头介绍的,要把 Action 的名称中带有下划线的替换成减号)。作者(音乐让我说)博客地址:http://music.cnblogs.com

首先,依旧添加和 LowercaseRoute 类相似的构造函数,然后重写 GetVirtualPath 方法。完成代码如下:

public class UnderlineRoute : LowercaseRoute
{
public const string UNDERLINE_STRING = "_";
public const string SUBTRACTION_SIGN = "-"; public UnderlineRoute(string url, IRouteHandler routeHandler)
: base(url, routeHandler)
{
} public UnderlineRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
: base(url, defaults, routeHandler)
{
} public UnderlineRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
: base(url, defaults, constraints, routeHandler)
{
} public UnderlineRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
: base(url, defaults, constraints, dataTokens, routeHandler)
{
} public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
string action = (string)values["action"];
if(action != null)
{
if (action.Contains(UNDERLINE_STRING))
{
values["action"] = action.Replace(UNDERLINE_STRING, SUBTRACTION_SIGN);
}
}
// 以上的这小块代码就是生成 URL 时,把 RequestContext 中的 RouteData 中的 action 参数给替换,
// 把 action 的名字中带有下划线(_)的替换成减号(-)
return base.GetVirtualPath(requestContext, values);
} /// <summary>
/// 替换成下划线
/// </summary>
/// <param name="requestContext"></param>
public static void ReplaceUnderlineToSubtractionSignInRouteData(RequestContext requestContext)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
// 以上的这小块代码就是当处理请求时,把 RequestContext 中的 RouteData 中的 action 参数给替换回来。
// 把请求的路由中的 action 参数給替换回来,把 action 的名字中带有减号(-)的替换成下划线(_)
string actionName = (string)requestContext.RouteData.Values["action"];
if (actionName != null)
{
if (actionName.Contains(UnderlineRoute.SUBTRACTION_SIGN))
{
requestContext.RouteData.Values["action"] = actionName.Replace(UnderlineRoute.SUBTRACTION_SIGN, UnderlineRoute.UNDERLINE_STRING);
}
}
}
}

3. 建立相应的 RouteHandler。

如果您建立的项目是 MVC,则建立 UnderlineMvcRouteHandler 类,继承 MvcRouteHandler 类。(PS:WebForms 可以忽略)

如果您建立的项目是传统的 WebForm,则建立 UnderlinePageRouteHandler 类,继承 PageRouteHandler 类。(PS:MVC 可以忽略)

代码如下:

public class UnderlineMvcRouteHandler : MvcRouteHandler
{
public UnderlineMvcRouteHandler() :base()
{
} public UnderlineMvcRouteHandler(IControllerFactory controllerFactory): base(controllerFactory)
{
} protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
UnderlineRoute.ReplaceUnderlineToSubtractionSignInRouteData(requestContext); // 调用 UnderlineRoute 类处理
return base.GetHttpHandler(requestContext);
}
} public class UnderlinePageRouteHandler : PageRouteHandler
{
public UnderlinePageRouteHandler(string virtualPath) : base(virtualPath)
{
} public UnderlinePageRouteHandler(string virtualPath, bool checkPhysicalUrlAccess) : base(virtualPath, checkPhysicalUrlAccess)
{
} public override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
UnderlineRoute.ReplaceUnderlineToSubtractionSignInRouteData(requestContext); // 调用 UnderlineRoute 类处理
return base.GetHttpHandler(requestContext);
}
}

4. 扩展 RouteCollection 类的 MapRoute 方法。

在配置路由时,默认是调用 MapRoute 方法,而这个方法默认是由 MvcRouteHandler(PS: WebForm 默认是 PageRouteHandler),所以为了让我们配置的路由默认由 UnderlineMvcRouteHandler
(WebForm 默认是 UnderlinePageRouteHandler)来处理,我们再添加几个和 MapRoute 相似的方法。

对于 MVC 项目,我们添加几个名叫 MapRouteUnderline 的重载。

public static Route MapRouteUnderline(this RouteCollection routes,
string name,
string url,
object defaults,
object constraints,
string[] namespaces)
{
if (routes == null)
{
throw new ArgumentNullException("routes");
}
if (url == null)
{
throw new ArgumentNullException("url");
}
// 注意:
// 1. 这里使用的 Route 是 UnderlineRoute
// 2. 这里使用的 RouteHandler 是 UnderlineMvcRouteHandler
UnderlineRoute item = new UnderlineRoute(url, new UnderlineMvcRouteHandler())
{
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints),
DataTokens = new RouteValueDictionary(namespaces)
};
if (namespaces != null && namespaces.Length > )
{
item.DataTokens["Namespaces"] = namespaces;
}
routes.Add(name, item);
return item;
}

对于 WebForms 项目,我们添加几个名叫 MapRouteUnderline 的重载。

public static Route MapPageRouteUnderline(this RouteCollection routes,
string routeName,
string routeUrl,
string physicalFile,
bool checkPhysicalUrlAccess,
RouteValueDictionary defaults,
RouteValueDictionary constraints,
RouteValueDictionary dataTokens)
{
if (routes == null)
{
throw new ArgumentNullException("routes");
}
if (routeUrl == null)
{
throw new ArgumentNullException("routeUrl");
}
// 注意:
// 1. 这里使用的 Route 是 UnderlineRoute
// 2. 这里使用的 RouteHandler 是 UnderlinePageRouteHandler
UnderlineRoute item = new UnderlineRoute(routeUrl,
defaults,
constraints,
dataTokens,
new UnderlinePageRouteHandler(physicalFile, checkPhysicalUrlAccess)
);
routes.Add(routeName, item);
return item;
}

5. 在 Global.asax 中配置路由

这一步是关键,要让我们上面自定义的 Route 和 RouteHandler 生效,就必须在配置路由时,调用我们自定义的 MapRouteUnderline 和 MapPageRouteUnderline 方法。

//作者(音乐让我说)博客地址:http://music.cnblogs.com
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); // 请注意:这里调用的是 MapPageRouteUnderline 扩展方法
routes.MapPageRouteUnderline("adminDefault",
"{controller}/{action}",
"~/admin/{action}.aspx",
true, null, new RouteValueDictionary(new { controller = "admin" })); // 请注意:这里调用的是 MapRouteUnderline 扩展方法
routes.MapRouteUnderline(
"Default", // 路由名称
"{controller}/{action}/{id}", // 带有参数的 URL
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值
);
}

截止到这里,关键代码都已经给出了,接下来就是检验我们的代码。

6. 建立测试用的 Action

在 HomeController 中建立一个名叫 Add_Product_Information 的 Action 方法。并建立 cshtml 视图,视图里面随便敲几句话就可以了。

//作者(音乐让我说)博客地址:http://music.cnblogs.com
public class HomeController : Controller
{
/// <summary>
/// 添加产品信息,可以通过访问 URL:/home/add-product-information
/// </summary>
/// <returns></returns>
public ActionResult Add_Product_Information()
{
return View();
}
}

7. 在项目的根目录建立一个名叫 admin 的文件夹。

再在 admin 文件夹下建立 hello.aspx、add_product_information.aspx 至于里面的内容,随便敲几句话就可以了。

8. 在视图中测试,生成 URL

<ul id="menu">
<li>@Html.ActionLink("主页", "Index", "Home")</li>
<li>@Html.ActionLink("关于", "About", "Home")</li>
<li>@Html.ActionLink("添加产品信息", "Add_Product_Information", "Home")</li>
<li>
@Html.ActionLink("后台 - 欢迎页", "Hello", "Admin")
</li>
<li>
@Html.ActionLink("后台 - 添加产品信息", "Add_Product_Information", "Admin")
</li>
</ul>

9. 运行

解决方案截图如下:

以上 MVC 生成的链接都小写了,并且把下划线(_)也替换成了减号(-)。运行时,也正常的呈现出来!

本文只是起一个穿针引线的作用,无法要求读者也有这样的要求,仅仅为了演示,希望和大家一起进步,如有不妥,请多多包涵,并欢迎指出!

作者(音乐让我说)博客地址:http://music.cnblogs.com

示例代码下载:点我下载

如果您觉得本文不错,麻烦点一下“推荐”,谢谢!

转载请注明,谢谢!

谢谢浏览!

谢谢浏览!

一步一步实战扩展 ASP.NET Route,实现小写 URL、个性化 URL的更多相关文章

  1. 一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](五)

    前言 Hi,大家好,我是Rector 时间飞逝,一个星期又过去了,今天还是星期五,Rector在图享网继续跟大家分享系列文本:一步一步创建ASP.NET MVC5程序[Repository+Autof ...

  2. 一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](二)

    前言: 在本系列第一篇<一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](一)>中,我为大家介绍了搭建空白解决方案以 ...

  3. 一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](六)

    前言 大家好,我是Rector 又是星期五,很兴奋,很高兴,很high...啦啦啦... Rector在图享网又和大家见面啦!!!上一篇<一步一步创建ASP.NET MVC5程序[Reposit ...

  4. 一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](九)

    前言 童鞋们,大家好 我是专注.NET开发者社区建设的实践者Rector. 首先,为自己间隔了两个星期五再更新本系列文章找个不充分的理由:Rector最近工作,家庭的各种事务所致,希望大家谅解. 本文 ...

  5. 一步一步搭框架(asp.netmvc+easyui+sqlserver)-03

    一步一步搭框架(asp.netmvc+easyui+sqlserver)-03 我们期望简洁的后台代码,如下: using System; using System.Collections.Gener ...

  6. 一步一步搭框架(asp.netmvc+easyui+sqlserver)-02

    一步一步搭框架(asp.netmvc+easyui+sqlserver)-02 我们期望简洁带前台代码,如下: <table id="dataGrid" class=&quo ...

  7. 一步一步搭框架(asp.netmvc+easyui+sqlserver)-01

    一步一步搭框架(asp.netmvc+easyui+sqlserver)-01 要搭建的框架是企业级开发框架,适用用企业管理信息系统的开发,如:OA.HR等 1.框架名称:sampleFrame. 2 ...

  8. (转) 一步一步学习ASP.NET 5 (四)- ASP.NET MVC 6四大特性

    转发:微软MVP 卢建晖 的文章,希望对大家有帮助.原文:http://blog.csdn.net/kinfey/article/details/44459625 编者语 : 昨晚写好的文章居然csd ...

  9. (转) 一步一步学习ASP.NET 5 (二)- 通过命令行和sublime创建项目

    转发:微软MVP 卢建晖 的文章,希望对大家有帮助. 注:昨天转发之后很多朋友指出了vNext的命名问题,原文作者已经做出了修改,后面的标题都适用 asp.net 5这个名称. 编者语 : 昨天发了第 ...

随机推荐

  1. paip.uapi 获取网络url内容html 的方法java php ahk c++ python总结.

    paip.uapi 获取网络url内容html 的方法java php ahk c++ python总结. 各种语言总结比较,脚本php.python果然是方便.简短,实用. uapi : get_w ...

  2. JSP内置对象总结

    前几天学习了javaee中的jsp(Java Server Pages),即java服务器页面,其实就是在html里面写java代码. 一.概述前言 在总结九大对象之前,有必要先搞清楚几个概念:请求, ...

  3. VS2015安装 Secondary Installer Setup Failed求解决方案

    个人同步本文博客地址http://aehyok.com/Blog/Detail/64.html 个人网站地址:aehyok.com QQ 技术群号:206058845,验证码为:aehyok 本文文章 ...

  4. 无锁编程以及CAS

    无锁编程 / lock-free / 非阻塞同步 无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Sy ...

  5. Javascript基础恶补

    1.字符集:Javascript采用Unicode字符集,支持地球上所有在用的语言. 2.区分大小写:Javascript区分大小写,HTML不区分大小写. 3.空格.换行.格式控制符:Javascr ...

  6. Spring MVC+Quartz 定时任务持久化

    请自行参考: http://sloanseaman.com/wordpress/2011/06/06/spring-and-quartz-and-persistence/ https://object ...

  7. nginx 重写 rewrite 基础及实例

    nginx rewrite 正则表达式匹配 大小写匹配 ~ 为区分大小写匹配 ~* 为不区分大小写匹配 !~和!~*分别为区分大小写不匹配及不区分大小写不匹配 文件及目录匹配 -f和!-f用来判断是否 ...

  8. 通过生产者消费者模式例子讲解Java基类方法wait、notify、notifyAll

    wait(),notify()和notifyAll()都是Java基类java.lang.Object的方法. 通俗解释wait():在当前线程等待其它线程唤醒.notify(): 唤醒一个线程正在等 ...

  9. Windows 7 中设置VPN(PPTP连接方式)

    第一步:打开控制面板-网络和共享中心(如图) 第二步:点击-设置新的连接或网络(如图) 第三步:选择-连接到工作区-下一步(如图) 第四步:如果已经存在其他连接,则在这一步选择”否,创建新连接“:如果 ...

  10. http协议读书笔记2-连接管理

    一.http是如何使用tcp连接的? http连接本质就是tcp连接和一些使用连接的规则.所有的http通讯都是由tcp/ip来承载的.tcp/ip是全球计算机及网络设备都在使用的一种常用的分组交换的 ...