ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase

时间:2021-11-15 22:28:15

原文地址:http://www.51csharp.com/MVC/882.html

 

引言--

  在初级篇中,我们介绍了如何利用基于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:

ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase
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
}
}
}
ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase

  这样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()方法修改一下:

ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase
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;
}
ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase

  你就会发现,无论你在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>中,

ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase
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"}
};
}
}
ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase

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

ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase
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);
}
}
}
ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase

  首先我们建议,VirtualPathData()GetRouteData()方法是成双成对出现的。一旦你制定了一条路由规则,比如分类规则/ca-categoryname,那么:

  • GetRouteData()必须处理与这条规则匹配的每一条URL,返回相同的路由值放弃与之不匹配的URL,返回null,让匹配继续。
  • VirtualPathData()必须处理与这条规则匹配的每一次路由请求,返回相同的URL;放弃与之不匹配的请求,返回null,让匹配继续。

  !!!两者相辅相成的完成了路由值和URL的相互映射,漏掉一个,就不能构成一个完成的路由规则。直接结果是出现404或生成URL地址错误。

  GetRouteData()的代码:

ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase
 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)方法。匹配终止
}
ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase

  VirtualPathData()的代码

ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase
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());
}
ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase

  至此,我们就把这条路由规则的映射处理完整了。如果你掌握了上述技术,任何的URL重写和优化需求,我相信你都能Hold住。

  如果我们的主页页面是这样<Razor视图引擎>:

ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase
@model List<JohnConnor.Models.Category>
@{
ViewBag.Title = "主页";
} <h2><a href="@Url.Action("Index", "Home")">首页</a></h2>
<p>
@foreach (var item in Model)
{
<a href="@Url.Action("ShowCategory", "Category", new { id = item.CategoeyID })">@item.CategoeyName</a>
}
</p>
ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase

  三个分类连接会得到这样的结果

<a href="/ca-nokia">Nokia</a>
<a href="/ca-iphone">iPhone</a>
<a href="/ca-anycall">Anycall</a>

  点击每一个连接都会先进入我们的处理程序,生成相应的路由值-调用CategoryController.Showcategory(string id)方法根据id显示相应的分类页面。

  ------------------------------------------------------进阶篇完---------------------------------------------------

  

  这一篇我花费了不少时间去构思如何用简单的例子讲述继承RouteBase来进行URL重写与优化。

  希望能帮助到有用的人。

ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase的更多相关文章

  1. ASP&period;NET MVC URL重写与优化&lpar;进阶篇&rpar;-继承RouteBase玩转URL

    http://www.cnblogs.com/John-Connor/archive/2012/05/03/2478821.html 引言-- 在初级篇中,我们介绍了如何利用基于ASP.NET MVC ...

  2. &lbrack;转载&rsqb;ASP&period;NET MVC URL重写与优化&lpar;进阶篇&rpar;-继承RouteBase玩转URL

    引言-- 在初级篇中,我们介绍了如何利用基于ASP.NET MVC的Web程序中的Global文件来简单的重写路由.也介绍了它本身的局限性-依赖于路由信息中的键值对: 如果键值对中没有的值,我们无法将 ...

  3. &lbrack;转&rsqb;ASP&period;NET MVC URL重写与优化&lpar;进阶篇&rpar;-继承RouteBase玩转URL

    本文转自:http://www.cnblogs.com/John-Connor/archive/2012/05/03/2478821.html 引言-- 在初级篇中,我们介绍了如何利用基于ASP.NE ...

  4. ASP&period;NET MVC URL重写与优化&lpar;初级篇&rpar;-使用Global路由表定制URL

    ASP.NET MVC URL重写与优化(初级篇)-使用Global路由表定制URL 引言--- 在现今搜索引擎制霸天下的时代,我们不得不做一些东西来讨好爬虫,进而提示网站的排名来博得一个看得过去的流 ...

  5. ASP&period;NET MVC URL重写与优化&lpar;1&rpar;-使用Global路由表定制URL

    ASP.NET MVC URL重写与优化(1)-使用Global路由表定制URL 引言--- 在现今搜索引擎制霸天下的时代,我们不得不做一些东西来讨好爬虫,进而提示网站的排名来博得一个看得过去的流量. ...

  6. ASP&period;net的url重写

    http://blog.csdn.net/windok2004/article/details/2432691 1. 有关于URL的重写,本文也只是拿来主意.相继有MS的组件“URLRewriter” ...

  7. asp&period;net 页面url重写

    不更改情况下,页面路径为index.aspx?id=1,现在输入页面路径index/1时,也能访问到页面,这一过程叫做url重写 ①:在一个类里制定路径重写规则,以下为自定义UrlRewriterFi ...

  8. &lpar;转&rpar;ASP&period;net的url重写

    1. 有关于URL的重写,本文也只是拿来主意.相继有MS的组件“URLRewriter”和在Global.asax里的“Application_BeginRequest()”编码方式,以及IIS里的I ...

  9. Asp&period;net实现URL重写

    原文:Asp.net实现URL重写 [概述] URL重写就是首先获得一个进入的URL请求然后把它重新写成网站可以处理的另一个URL的过程.重写URL是非常有用的一个功能,因为它可以让你提高搜索引擎阅读 ...

随机推荐

  1. QQ揭秘:如何实现窗体靠边隐藏?【低调赠送:QQ高仿版GG 4&period;2 最新源码】

    QQ有个靠边隐藏的功能,使用起来很方便:在屏幕上拖动QQ的主窗体,当窗体的上边沿与屏幕的上边沿对齐时,主窗体就会duang~~地隐藏起来,当将鼠标移到屏幕上边沿的对应区域时,主窗体又会duang~~显 ...

  2. C&num;编程总结(十一)数字证书

    C#编程总结(十一)数字证书 之前已经通过文章介绍了数字证书的基础知识,包括加密和数字签名. 具体可见: 1.C#编程总结(七)数据加密——附源码 2.C#编程总结(八)数字签名 这里来讲述数字证书的 ...

  3. Android使用BLE&lpar;低功耗蓝牙,Bluetooth Low Energy&rpar;

    背景 在学习BLE的过程中,积累了一些心得的DEMO,放到Github,形成本文.感兴趣的同学可以下载到源代码. github: https://github.com/vir56k/bluetooth ...

  4. 用Intellij IDEA 创建第一个maven项目!

    1. 一直想如何复用以前项目的maven的jar包! 其实只要拿到pom.xml即可!!! 1.1 创建一个maven项目 2. 3. 创建项目名和项目路径,我给项目起的名字是mavenV1.0 4. ...

  5. mysql 1449 : The user specified as a definer &lpar;&&num;39&semi;root&&num;39&semi;&commat;&&num;39&semi;&percnt;&&num;39&semi;&rpar; does not exist 解决方法

    权限问题,授权 给 root  所有sql 权限 mysql> grant all privileges on *.* to root@"%" identified by & ...

  6. MSDN:Code First 迁移

    来自:MSDN:Code First 迁移,完全照搬! 本演练将提供对实体框架中 Code First 迁移的概述.您可以完成整个演练,也可以跳至自己感兴趣的主题.主题如下: 启用迁移 生成并运行迁移 ...

  7. Swift概览

    <pre name="code" class="objc">转自:http://letsswift.com/2014/06/swift_overvi ...

  8. 浅谈C语言中的联合体

    联合体union 当多个数据须要共享内存或者多个数据每次仅仅取其一时.能够利用联合体(union).在C Programming Language 一书中对于联合体是这么描写叙述的: 1)联合体是一个 ...

  9. Struts2 实战经验 之 入门

    Struts2以WebWork为核心,采用拦截器机制处理用户请求,这样的设计使业务逻辑控制器能够与Servlet API完全脱离开,降低了藕联性. Part 1. 安装与配置 下载struts-2.3 ...

  10. C&num; 语言规范&lowbar;版本5&period;0 &lpar;第18章 不安全代码&rpar;

    1. 不安全代码 **(注:此章对于跨多语言编程开发非常重要,如遇异常无法完成跨语言,建议使用此种方式.) 如前面几章所定义,核心 C# 语言没有将指针列入它所支持的数据类型,从而与 C 和 C++ ...