asp.net URL Rewriter 问题

好用的开源url重写组件IIRF

介绍

IIRF是一款开源的重写URL过滤器,类似于Apache的URL重写,基于VC8.0(可以用Visual Studio2005或Visual C++ 2005 Express重新编译)开发。它可以运行在IIS5.0+,支持ASP,ASP.NET,PHP等许多格式。相对比ASP.NET2.0自带的URL重写,具有更好的性能和许多我们所需要特性,重要的一点在于:它可以支持无扩展名的URL(例如:cnl bogs.com/****,你无需在创建一个默认的default.aspx文件,IIRF自动会帮你解析),让URL更加的方便我们记忆,也能进一步提高搜索的排名?IIRF能够在aspnet_isapi提前捕获我们所请求的URL进行处理,如果我们访问cnlbog s.com/a.aspx,需要获取cnlbogs.com/a.htm,步骤需要(iis-aspnet_isapi),通过IIRF,我们可以直接跳aspnet_isapi.dll,直接访问a.htm,要知道,这种方式在ASP.NET是无法实现的。

IIRF跟ASP.NET重写URL一样,它也是基于正则的方式来匹配,具有LOG记录,请求的条件判断。还是进入正题吧。

安装

IIRF安装需要我们手动操作来完成。不过。也是很方便了。

1 将IsapiRewrite4.dll, IsapiRewrite4.ini复制到c:\windows\system32\inetsrv(你也可以复制到其它适当的文件夹下面)。

IsapiRewrite4.ini是IIRF配置文件,每次该文件更改之后,IIRF会自动重新加载该文件,无需重启IIS来重新加载配置,如果您修改后INI文件后格式不正确,IIRF将会自动获取最后正确加载的配置文件。

2 打开IIS管理器,选择“默认网站”,右击“属性”,选择“ISAPI筛选器”,点击“添加”,输入筛选器名称:Ionic Rewriter,可执行文件选择上面复制到c:\windows\system32\inetsrv下面的IsapiRewrite4.dll文件,点“确定”。

3重启IISADMIN service服务。(在计算机管理----windows服务里面)

4 完成。

日志

IIRF能够将INI配置文件加载,用户的URL请求记录都会保存到指定的日志文件里。因为它具有很大的性能开销,因此建议将它日志的记录等级设为0,只有
为了方便调试的时候时候,可以设置为5,

RewriteLog <filename stub> 保存的日志路径,如 c:\temp\iirfLog.out
RewriteLogLevel {0,1,2,3,4,5} 日志的等级,默认值为0

0 –不会记录日志
1- 少许的日志
2- 比较多的日志
3- 比较详细的日志
4- 详细的日志(4),并会跟踪server variable和替换的字符串。
5- 详细的日志(5),包括日志文件更改的事件,建议方便调试的时候使用

正则

正则的语法跟.NET一样,只不过是格式不一样而已。所以我也不在详细介绍。具体有关正则的说明大家可以用GOOGLE搜索。

格式:
RewriteRule <url-pattern> <replacement-string> [<modifiers>]
url-pattern:匹配的正则表达式(必需)
replacement-string:要替换的字符串(必需)
modifiers:有关对RewriteRule的操作标记。可选选项。在下面我会说明

默认下IIRF的url-pattern,replacement-string正则的前面已经带了主机头的。

为了方便描述,直接看几个示例(以下示例基本全部来源IIRF文档)

RewriteRule ^/original/(.*).php /modified/$1.aspx

源: http://xxx/original/index.php

目标: http://xxx/modified/index.aspx

RewriteRule ^/dinoch/album/([^/]+)/([^/]+).(jpg|JPG|PNG) /chiesa/pics.aspx?d=$1&p=$2.$3

源: http://xxx/dinoch/album/30/1.jpg

目标: http://xxx/chiesa/pics.aspx?d=30&p=1.jpg

比较简单,主要还在于modifiers的功能。下面列举了它的所有值,允许组合(如[R,L])。

R = Redirect(URL跳转到<replacement-string>地址)
NF = Not found(返回404错误给用户,但该文件并未移除,还是保留在网站中)
L = Last test if match(如果已经匹配,将不在继续匹配下去)
F = Forbidden(跟NF标志相似,)
I = Do case-insensitive matching
U = Store original url in server Variable HTTP_X_REWRITE_URL(保存原始的url到HTTP_X_REWRITE_URL服务器变量中。)

[R] or [R=code]
就像跟我们在ASP.NET使用的Redirect方法一样,重新改变浏览器的方向,跳转到新的指定的URL中。
[R=code]允许我们指定特定的HTTP状态返回码。只能介于301到399。如果超出这个范围。默认会是使用302状态。
RewriteRule ^/goto.aspx?r=(.*)$ $1 [R]
源: http://xxx/goto.aspx?r=http://www.google.com/
目标: http://www.google.com

[L]
上面已经简单介绍过。不在说明

[NF]
上面已经简单介绍过。它还可以跟RewriteCond一起配合,来实现自定义的404错误请求。
特别要注意,你所要匹配的文件必须存在,替换的字符串不允许是存在文件名
RewriteRule ^/1008.aspx$ /1.aspx [NF]
1008.aspx文件需要存在,1.aspx不存在,否则无法正常达到我们的结果。
(很奇怪,我不知道是不是我搞错了。但我最终测试的结果确实是这样,文档也没详细说明过,有知道的朋友可以告诉我一下原因)

[F]
不在说明。

[I]
模糊匹配

[U]
保存原始的url到HTTP_X_REWRITE_URL服务器变量中。
在ASP.NET你可以用Request.ServerVariables["HTTP_X_REWRITE_URL"]获取原始值。

RewriteCond
RewriteCond <test-string> <pattern> [<modifier flag[,...]>]
类似于条件判断,并且允许多个条件,OR,AND。只有当RewriteCond的Server Variable 匹配所指定的正则表达,RewriteRule才会执行。比如:
RewriteCond %{REMOTE_ADDR} ^(127.0.0.1)$
RewriteRule ^/(.*).aspx$ /$1.aspx
如果我们访问网站的地址的IP来源于127.0.0.1,那么,允许 RewriteRule ^/(.*).aspx$ /$1.aspx

RewriteCond %{REMOTE_ADDR} ^(127.0.0.1)$ [OR]
RewriteCond %{REMOTE_ADDR} ^(192.168.0.10)$
RewriteRule ^/(.*).aspx$ /$1.aspx
添加了OR来多个条件判断

RewriteCond %{REMOTE_ADDR} ^(?!127.0.0.1)([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})(.*)$
RewriteRule ^/(?!redirected.htm)(.*)$ /redirected.htm

Modifier flags有二个值
I=模糊匹配
OR=逻辑判断

从IIRF的RewriteCond的功能上来说,确实很灵活,不知道跟IIS7的重写怎么样,嘻嘻。还没瞧见过呢*^_&。另外。RewriteCond的[Patterns]可以带下面几个参数
-d
Treats the TestString as a pathname and tests if it exists,
and is a directory.
TestString是一个路径名称,并且存在这个路径
-f
Treats the TestString as a pathname and tests if it exists and
is a regular file.
TestString是一个路径名称,并且是一个存在的文件
-s
Treats the TestString as a pathname and tests if it exists and
is a regular file with size greater than zero.
TestString 是一个路径名称,并且存在文件超过0字节

如文档所使用的例子
(1)RewriteCond %{HTTP_URL} (/|\.htm|\.php|\.html|/[^.]*)$ [I] )
(2)RewriteCond %{REQUEST_FILENAME} !-f
(3)RewriteCond %{REQUEST_FILENAME} !-d
(4)RewriteRule ^.*$ /index.aspx [U,L]
(1)如果URL是以htm,php,html(模糊匹配),
(2)URL不是存在文件
(3)URL不是请求的路径
(4)将所有请求跳转到index.aspx,保存原始的URL,之后不在对此进行匹配

在如
RewriteCond %{HTTP_USER_AGENT} ^Mozilla.*
RewriteRule ^/$ /homepage.max.html [L]

IIRF其它配置属性
IterationLimit {integer}从指定的integer后开始匹配RewriteRule组。如果超出RewriteRule个数,默认将会从第8个开始。
MaxMatchCount {integer} RewriteRule组的总个数。
RewriteLog <filename stub> 日志路径
RewriteLogLevel {0,1,2,3,4,5} 日志的等级

一个常见问题
在刚安装IIRF之后就测试一下ASP.NET下面的URL,发现还是跟以前ASP.NET重写URL有一样的问题:无法改写Form下面的action的路径,而我们这时使用Request.RawUrl获取原始的URL是为空的。也许你开始注意到了我 上面写过的RewriteRule的modifiers选项[U]。我们可以通过将原始的URL保存到服务器变量中。然后通过Request.ServerVariables[name]来获取。然后重写action的值就可以了。

代码1
1 Public Class FormFixerHtmlTextWriterClass FormFixerHtmlTextWriter
2 Inherits System.Web.UI.HtmlTextWriter
3 Private _url As String
4 Public Sub New()Sub New(ByVal writer As TextWriter)
5 MyBase.New(writer)
6 _url = ForumContext.Current.Context.Request.ServerVariables("HTTP_X_REWRITE_URL")
7 End Sub
8 Public Overloads Overrides Sub WriteAttribute()Sub WriteAttribute(ByVal name As String, ByVal value As String, ByVal encode As Boolean)
9 If (Not _url Is Nothing And String.Compare(name, "action", True) = 0) Then
10 value = _url
11 End If
12 MyBase.WriteAttribute(name, value, encode)
13 End Sub
14 End Class
后记:
IIRF已经分析的差不多了。从上面可以看出,功能确实很强大,而且配置也很灵活。发现自己也慢慢喜欢上它了。嘿嘿。重要的是。它是免费,开源的。

为自己小小的鼓掌一下。嘻嘻。写这篇随笔真不容易。希望能给大家一个帮助。另外,由于自己水平有限,上面也许有描述错误的地方或不清楚的地方还请指出,有则改之,无则加勉

IIRF网站: http://cheeso.members.winisp.net/IIRF.aspx

从开始着手解决url rewrite用了竟然十来个小时,虽不长却也一波三折,
记录一下,几个问题还是蛮有趣的
十几个小时前的我一直认为解决这样一个不是问题的问题应该会画去我半个小时,最多最多45分钟

rewrite policy其实很简单,

/Login => /Login.aspx
&
/(.+)/Login => /Login.aspx?SpecifyUser=$1

仅此而已的复杂度
记忆中在ASP.NET揭秘中很只画了数行代码就解决
cnblogs中也有无数链接

包括msdn都给出了很official的solution,
http://www.microsoft.com/china/msdn/library/webservices/asp.net/URLRewriting.mspx

压根就没觉得这是问题,只是要看完楼上的文章是需要一定的勇气的。
于是很简单的就直接在Application_BeginRequest中进行HttpContext.Current.RewritePath,
F5,everything is ok

问题就出在upload到IIS以后,就tmd开始404了
也对,因为目录不存在就直接被IIS intercepte掉了。

没戏,然后开始疯狂翻cnblogs老底,太多太多的重复文章,太多太多的重复内容
让我有点抓狂,然后突然发现一篇有点意思的,
一完美的关于请求的目录不存在而需要url重写的解决方案!
http://pwqzc.cnblogs.com/archive/2005/11/21/281331.aspx

你知道我当时的感觉是什么吗?
开始有点爽,完美这个词不是水水便便的,看看到底是什么方法,
哦,原来是借助custom 404
完了,用custom 404就算是完美了?!
那是没戏了

因为我不是搞网站出道的,没深入过iis,apache.但怎么都觉得这玩意难道就真没办法了
看到很多老b样在说,
何不看看.text代码呢,可以学到很多东西。
故意制造一个错误,再利用这个错误,此种设计实属另类。局限性很大。而且请求被你绕了一圈才到达正确的处理程序
------------------------------------------------------------
.text原来也无非是映射*.*到aspnet_isapi.dll
也是同样扭曲,总算是应该不替换404好太多了吧

遂疯狂的search,无意间发现了IIRF
http://cheeso.members.winisp.net/IIRF.aspx
这兄弟厉害了,就tmd的一个页面,一个free&open source类mod_rewrite ISAPI
下载,跟着看了一下readme挂到iis上,略微改了改ini
竟然就可以了...心中的不快一扫而空

为IIRF而由衷的赞..

短短的几句配置就ok了
RewriteRule ^/$ /Default.aspx
RewriteRule ^/Login$ /Login.aspx
RewriteRule ^/(.+)/Login$ /Login.aspx?SpecifyUser=$1
RewriteRule ^/Register$ /Register.aspx
RewriteRule ^/Help$ /Help.aspx
RewriteRule ^/Error$ /Error.aspx
RewriteRule ^/(.+)/TimeStream$ /personal/TimeStream.aspx?SpecifyUser=$1

一波才平一波又起,因为这个ISAPI要挂在网站下面,然后因为我们做Continuous Integration
需要通过ant在iis中创建site,并把这个ISAPI挂上去,之前创建都是小白负责我也没具体操心
今天为了把这个ISAPI挂上去,也令我颇为费神。你知道的,vbs的代码看了要死人的,我一直这样认为
最后在这里找到了
Overview of Programmatic Administration
http://www.microsoft.com/windows2000/en/server/iis/default.asp?url=/windows2000/en/server/iis/htm/asp/apro2otv.htm

应该是iis的官方文档,懵懵懂懂看了十分钟,也就搞明白他是通过metabase类似注册表的结构来保存数据的,不过这足够了

adsutil.vbs create w3svc/${site.port}/Filters "IIsFilters"
adsutil.vbs create w3svc/${site.port}/Filters/IIRF "IIsFilter"
adsutil.vbs set w3svc/${site.port}/Filters/IIRF/FilterPath "${iis.path}\IsapiRewrite4.dll"
adsutil.vbs set w3svc/${site.port}/Filters/FilterLoadOrder "IIRF"

上面的"IIsFilters","IIsFilter"是node的type,这个在create的时候要注意一下,就可以了
也算是解决了

反观在解决这两个技术问题的过程中,
不要太过于沉浸或是陶醉在一个技术里面,技术本身就是为需求为人服务的,否则就高出404这样完美的笑话了
说真的,在解决了挂载ISAPI之后,我开始对vbs有了有生以来的第一次好感

一完美的关于请求的目录不存在而需要url重写的解决方案!

我只想要达到这样的目的,和博客园一样当用户注册后将给他个如: http://www.cnblogs.com/注册名
一样的地址,以便用户能够通过此地址能够访问他的博客,而注册名这个目录其实是不存在的,本文也只解决这个问题.
从我开始想到去解决这个问题到现在解决大约5天时间,期间我查阅了基本上所有我能够找的的资料,博客园的搜索引擎甚至被我搜烂了,我有好多不明白,我不知道博客写文章是干什么的?不知道你们写的代码自己测试了没?我也不知道你们的代码是不是抄袭别人的?我认为做为一名博客,如果你引用别人代码,希望自己能够先做点测试,如果是自己写的,我希望是先测试成功了再帖出来,以免误人子弟.
同时我也在CSDN连发两帖,花了260大分,甚至有的朋友连意思都没有搞明白就回帖,当然也谢谢他们的热心,帖子地址是: http://community.csdn.net/Expert/topic/4406/4406615.xml?temp=.513714 还有个: http://community.csdn.net/Expert/topic/4401/4401936.xml?temp=.317898
大家有兴趣可以看看.
期间我碰到了两个主要问题:
1,如果和博客园一样设置iis添加>*的隐射让所有的请求不经过iis而让aspnet_isapi.dll来处理,带来性能的损失不说,当我们运行 http://localhost/RewriteTest的时候既然要我输入用户名和密码来登陆域,出现什么由于acl,访问被拒绝的401.3错误,我在iis设置权限,在RewriteTest目录添加了所有用户组的读写权限故障依然存在,其中RewriteTest是我的应用程序目录,本来我们访问这个地址是会自动转到这个目录下的default.aspx文件的,但是为什么出现这个错误呢?不知道博客园的代码是怎么实现这点的,博客园的代码左一个factory工厂右一个fctory工厂,我真想跳河啊,看不懂,所以不看他的.

为什么说完美呢?其实我认为这解决方法不完美,因为他还要设置下iis,我渴望朋友们能够给我更完美的方法,所以冠之为完美,请见晾.并且我也认为这解决方法处理类似的问题应该最为简单高效.恳请大家能够给出更好的解决方法,我相信你在我心中将会和我师傅吴旗娃一样是我心中的神!感谢师傅!我所有的代码在iis5.1,xp sp2系统,vs.net2005下测试通过.我的思路是截获404错误,然后用一个实现IHttpHandler接口的类来处理这个错误路径.我的方法如下:
一,先在vs.net2005先新建个web site站点,我命名为RewriteTest,然后添加个404.aspx文件.
二,修改iis.在iis中找到站点RewriteTest,右击选属性,在弹出的对话面板里面选,再点里面的404,选编辑属性,文件类型选url,下面输入:/RewriteTest/404.aspx 好了,点确定到头.在这里为什么我们不在web.config里面用<customErrors>来配置路径呢?因为在web.config里面设置的如果输入的是一个不存在的文件他能够截获,如果输入的是一个不存在的目录他就不能够截获拉,所以只能够在iis里面设置.
三,我们现在修改web.config文件,让路径404.aspx来让我们的一个实现了IHttpHandler接口的类来处理他,修改如下:<system.web>
<httpHandlers>
<add verb="*" type="RewriteTest.Http404" path="404.aspx"/>
</httpHandlers>
其中RewriteTest是名字空间,Http404是一个实现了IHttpHandler接口的类, 这样当我们的404.aspx截获到错误的时候会由这个Http404类来处理]
四,我们添加个类Http404,代码如下,有详细的注释:
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace RewriteTest
{
/// <summary>
/// Summary description for Http404
/// </summary>
public class Http404:IHttpHandler
{
public Http404()
{
//
// TODO: Add constructor logic here
//
}

#region IHttpHandler Members

public bool IsReusable
{
get { throw new Exception("The method or operation is not implemented."); }
}

public void ProcessRequest(HttpContext context)
{
//throw new Exception("The method or operation is not implemented.");
string errorPath = context.Request.RawUrl.ToLower();
context.Response.Write(errorPath);
}

#endregion

#region IHttpHandler Members

bool IHttpHandler.IsReusable
{
get { return false; }
}

void IHttpHandler.ProcessRequest(HttpContext context)
{
//首先我们取得请求的原始url,比如我们请求的是 http://localhost/RewriteTest/123456
//RewriteTest是这个应用程序的名字,返回的将是/rewritetest/404.aspx?404; http://localhost/rewritetest/123456
//请大家注意404后面的;
string errorString = context.Request.RawUrl.ToLower();
//context.Response.Write(errorString);
//再把得到的地址按照;分割下将得到结果是: http://localhost/rewritetest/123456
//这个时候我们再想办法得到那个不存在的虚拟目录
errorString = errorString.Split(';')[1];
//也可以写成这样:errorString = errorString.Split(new char[]{';'})[1];
//context.Response.Write(errorString);
//如果url含有点.我们就判断他带有后缀如.gif.jpg.asp.......我们就输出请求资源不存在的错误信息
if (errorString.IndexOf(".") < 0)
{
//去掉前面的 http://
errorString = errorString.Remove(0, 7);
//context.Response.Write(errorString);
//再得到域名
string hostName = context.Request.Url.Host;
errorString = errorString.Remove(0, hostName.Length);
//context.Response.Write(errorString);
//再得到请求的应用程序路径
string appPath = context.Request.ApplicationPath;
errorString = errorString.Remove(0, appPath.Length);
//context.Response.Write(errorString);
if (errorString.StartsWith("/"))
{
//如果前面有/就去掉/
errorString = errorString.Remove(0, 1);
}
if (errorString.EndsWith("/"))
{
//如果以/结尾我们也去掉他
errorString = errorString.Remove(errorString.Length - 1, 1);
}
if (errorString.IndexOf("/") < 0)
{
//如果中间含有/我们就不理他
//好了,我们现在实现url重写,就是页面跳转,在这里我很奇怪为什么不能够用
//context.RewritePath来实现,而要用Server.Transfer
context.Server.Transfer("~/page.aspx?name=" + errorString);
}
}
else
{
context.Response.Write("懒MM.你请求的资源不存在,请点<a href='default.aspx'>这里</a>返回首页!");
}
}

#endregion
}
}
五,我们再添加个page.aspx文件,后台就一句代码: protected void Page_Load(object sender, EventArgs e)
{
Response.Write(Request.QueryString["name"].ToString());
}
我们也把前台的标题改下:<title>懒MM</title>
我们现在运行下,输入: http://localhost/RewriteTest/123456
123456这个目录不存在的,大家可以看到,这个时候地址栏里面的地址依然是 http://localhost/RewriteTest/123456
但是标题变成了懒MM,内容也是page.aspx的 内容拉!

恳请能够提出更好的解决办法!谢谢、

你可能感兴趣的:(asp.net)