asp.net url重写

   刚毕业不久那会儿,在公司干的活是利用cms做网站,有一天,客户要求添加论坛功能。由于cms没有自带此功能,我得找现有的论坛功能,然后给整合进去,整合何谈容易。公司给留的时间不多。我找到一个论坛,整合到cms中,在本地测试没有问题。我跑到客户那儿去部署,那时候,主流用的是IIS6,这时候出了一个问题,此论坛有url重新功能。当时不知道是配置还是什么问题,导致某些页面不能正常访问。我那时的急的像热锅上的蚂蚁,公司同事出谋划策也没有解决问题。客户给你部署的时间也是有限的,一旦出问题,那客户的脸色不好看,话不好听,还担心你把他的服务器搞坏。想想晚上熬夜整合论坛,几乎一夜未睡,在现场,客户提这问题,提那问题。

       为什么会出现这样的事情呢?

       1、老板不会给你时间,让你自己开发论坛。平常一个网站两三天就可以用cms做好,如果加论坛,顶多给你多加2天时间。

       2、自己的技术水平还没有达到一个应有的阶段。

       利用cms做网站赚钱,干的是一个快餐式的买卖。一个稍微复杂的网站,顶多做5天时间。简单的就2、3天时间。这也就是你为什么看到政府的网站都是千篇一律的原因,而老板赚得金盆钵满。

       这样对程序员好吗?

       进入这样的公司,程序员是幸运,还是不幸运呢?这要看你是一个怎样的程序员。当时,老板给的工资高一些,我是被这给吸引了,没有顾及到其它。因为年轻,因为缺钱,想多挣一点钱。这些都没有错。如果你是一个比较懒的程序员,一个不为前途着想的程序员,可以在这里利用cms做网站,当然也可以不加思索,套套模板,改改样式,就一个网站速成了。短期内看领到的工资还可以。如果你是一个比较爱思考的程序员,也许是幸运的。

       我在公司里,竭尽全力地理解cms生成网站的原理。cms值得研究一下,毕竟有好多东西,那时候不知道。后来,我生成网站,并不是直接用cms,而是我做了一个程序,调用cms生成网站,这时候,我稍微改改就行了。这样我会节省一部分时间出来。这样你就多了研究cms源码的时间了。我们程序员最大的优点就是不愿意手动重复枯燥乏味的工作。于是就逼着我们自己写点代码,自动化执行,解放自身。

       我想要写一篇关于url重写的文章,以纪念早年那些青涩的岁月。

       时至今日,读了.net本质论,对url重写有了一定的认识,总结一下。 我们今天要谈的是写一个HttpMoudle模块来完成url的重写工作。具体需要完成以下几步:

      1、地址转换

          比如:   /MyUrlRewriteWebSite/wbq3311/Default.aspx   这是我的url请求的原始地址,实际在.net中要处理的地址:/MyUrlRewriteWebSite/Default.aspx?Folder=wbq3311  这是转换规则,我们得把规则配置到config中。

      2、webconfig中配置规则

1 <configSections>
2     <section name="myUrl" type="MyUrlRewrite.MyUrlRewriteConfigSection,MyUrlRewrite"/>
3 </configSections>
4 
5 <myUrl enabled="true" rebaseClientPath="true">
6     <rule source="(.*)/Default.aspx" destination="Default.aspx?Folder=$1"/>
7 </myUrl>

   ConfigSections中加了myUrl节点,是为了程序能够非常方便地读取我们的规则。规则的配置中用了正则表达式。

    3、写一个HttpMoudle模块,并配置到webConfig中

       

       从上图中,模块在初始化的时候,给应用程序注册了两个事件。一个是BeginRequest,Application管线处理的第一个事件。在这个事件里面,我们处理url映射规则,最终通过HttpContext中的RewritePath方法实现重写。请思考一个问题:为什么要在这个事件当中处理映射规则呢?我想从用户请求到HttpApplication接管的开始,就让它变成新的地址,这样后面就按照新的地址处理了。从RewritePath方法,我们明白微软在底层上是支持url重写的

       另一个事件是PreRequestHandlerExcute,它是Application执行处理程序前引发的一个事件。

1  void Application_PreRequestHandlerExecute(object sender, EventArgs e)
2     {
3         HttpContext context = HttpContext.Current;
4         if (context.CurrentHandler is Page)
5         {
6             Page page = context.CurrentHandler as Page;
7             page.PreInit += new EventHandler(Page_PreInit);
8         }
9     }

从代码中可以看出,当前的处理程序是Page对象,换一句话说,Page就是处理程序。如下图所示:

Page对象就是带有模板控件的一个处理程序。Application在这个事件里又注册了Page对象的事件 PreInit,在PreInit中,又做了些什么事情呢?稍后再看,我们看HttpMoudle完成了后的配置:

<system.web>
  <httpModules>
   <add name="RewriteModule" type="MyUrlRewrite.RewriteModule, MyUrlRewrite"/>
  </httpModules>
</system.web>

注意:Type以逗号分隔,前段是命名空间+类,后端是程序集名。II6和II7下面的配置有所不同,上面的配置是IIs6或者IIS7下的经典模式。IIS7下的集成模式的配置如下:

<system.webServer>
    <modules>
      <add name="RewriteModule" type="MyUrlRewrite.RewriteModule, MyUrlRewrite"/>
    </modules>
 </system.webServer>

4、 asp.net的回发

    .net UI设计页面和后台处理页面是同一个类,当UI页面中的form提交表单的时候,会提交到自己的后台。用户操作页面控件,回发到服务器。此时Page对象,虽然和之前的Page对象不是同一个对象,但是它们的确长的一模一样,唯一不同的是,它们的状态不同。什么状态不同呢?每次请求的cookie、控件的状态等等可能有所不同。用户操作页面上的控件,实际上是再一次向服务器发送了请求,那这次请求的地址,是上一次从服务器上传递过来的。那么这个地址必须是原始的地址,否则,我们url重写就没有什么意义了。

     我们看看PreInit事件的处理:

  // 为了保证页面在生成  form 地址的时候,正确生成 
    // 重新将原来的地址设置回去 
    void Page_PreInit(object sender, EventArgs e)
    {
        HttpContext context = HttpContext.Current;
        if (context.Items.Contains("OriginalUrl"))
        {
            string path = context.Items["OriginalUrl"] as string;
            RewriteContext con = new RewriteContext(context.Request.QueryString, path);
            context.Items["RewriteContext"] = con;
            if (path.IndexOf("?") == -1)
               path += "?";
            //context.Items["wbq3311"] = context.Request.QueryString["Folder"];
            context.RewritePath(path);
        }
    }

context.RewritePath把原始地址写回去了。那这个原始地址存在哪儿?它存在context的Item集合中,名称:OriginalUrl。Item是什么类型的集合呢?

字典类型的接口,而且实现类型是Hashtable。那我们平常用的Dictionary肯定是实现了IDictionary接口:

接口使得C#类型有多重性身份。因此说,Dictionary是一个实现了IDictionary的集合类型(实现了ICollection接口),它又是可迭代的,用foreach循环遍历,因为它实现了IEnumerable。

从上面的分析知道,Application在处理的时候,把原始的地址保存到context中的Item字典集合中,然后在页面的PreInit事件中取出,最终ReWritePath重写了原始地址,这样等下一次页面请求的时候,携带的又是原始地址。这不就是Asp.net在不同页面间保持状态的一种机制吗?所以HttpContext对象非常重要,它不光携带了很多关于用户请求响应的数据,而且可以跨越不同的处理周期传递数据。

       从上面种种分析描述,我们知道重写url并非易事。它需要理解Asp.net更多的内幕。

      1、Http请求到HTTPRuntime,再到HttpApplication接管并调用处理器处理的管线机制。

      2、HttpApplication的事件引发顺序,以及每个事件大致都做了些什么工作。

      3、页面的生命周期。(我们经常看到是Page_Load事件)

  好了,这就是.net url重写的一些感想和总结了,最后附上Application_BeginRequest的处理:

 1  void Application_BeginRequest(object sender, EventArgs e)
 2     {
 3         if (!MyUrlRewrite.Enabled)
 4             return;
 5 
 6         HttpContext context = HttpContext.Current;
 7 
 8         // 取得当前请求的路径
 9         string path = context.Request.Path;
10 
11         // 遍历所有的映射规则,进行映射处理 
12         foreach (MyUrlRewriteConfigRule rule in MyUrlRewrite.Rules)
13         {
14             Regex regex = new Regex(MyUrlRewrite.RewriteBase + rule.Source, RegexOptions.IgnoreCase);
15             Match match = regex.Match(path);
16             if (match.Success)
17             {
18                 // 映射
19                 string newPath = regex.Replace(path, rule.Destination);
20 
21                 if (context.Request.QueryString.Count != 0) 
22                 {     
23                     string sign = (path.IndexOf('?') == -1) ? "?" : "&";               
24                     newPath = newPath + sign +
25                         context.Request.QueryString.ToString();            
26                 }
27 
28                 // 为了在页面中正确生成 PostBack 地址
29                 // 保存原来的请求信息
30                 context.Items.Add("OriginalUrl", context.Request.RawUrl);
31 
32                 newPath = MyUrlRewrite.RewriteBase + newPath;
33 
34                 // 重写请求地址 
35                 context.RewritePath(newPath, MyUrlRewrite.RebaseClientPath ); 
36                 return;
37             }
38         }
39     }

 

 完整示例下载

     

       

 

 

 

       

posted @ 2018-03-20 12:45  micDavid  阅读(1704)  评论(12编辑  收藏  举报