好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

ASP.NET MVC URL重写与优化

ASP.NET MVC 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:

 

 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 >中,

View Code

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

View Code

  首先我们建议, 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视图引擎> :

View Code

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

 <  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重写与优化。

  希望能帮助到有用的人。

  需要程序源代码朋友点这里: JohnConnor.UrlRewrite.rar        

  如有任何问题,欢迎指正和讨论。

  

  

分类:  ASP.NET MVC

标签:  ASP.NET MVC

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于ASP.NET MVC URL重写与优化的详细内容...

  阅读:56次