翻译:Janssen
1.0.请一定要抱着批评的态度来看该文章
1.1. 概要
分析如何使用微软提供的ASP.NET来对动态产生的URL地址进行网址重写。网址重写是实现一种截取网址请求并将其进行处理后重新指向到一个指定的网址的过程。作者本人在对各种实现网址重写的技术进行研究和探讨后得出的经验和方法,希望能对您有所帮助。
1.2. 内容简介
稍微花点时间看一看你做的网站里头的URL地址,你看到类似这样的地址吗http://yoursite测试数据/info/dispEmployeeInfo.aspx?EmpID=459-099&type=summary ?也许你会出于某种目的把大量的页面文件从一个目录甚至一个网站转移到其他地方,而许多访问者出于个人兴趣或者研究目的之前就已经将原有网址收藏了起来, 如果这时他从收藏夹打开该页面的时候发现这已经是坏链了。本文旨在介绍如何使用网址重写将那些“难看”的网址转换成比较有实际意义的网址,使其便于记忆。例如将http://yoursite测试数据/info/dispEmployeeInfo.aspx?EmpID=459-099&type=summary转换成如下地址:http://yoursite测试数据/ dispEmployeeInfo/459-099/summary.html 。我们甚至发现网址重写技术可以解决令人头疼的404错误,或者说它可以创建一个智能化的404错误解决方案。
如上所述,网址重写是实现一种截取网址请求并将其进行处理后重新指向到一个指定的网址的过程。在网址重写执行的期间,相应处理程序处理被请求的网址,从中提取出相关的值,然后重新指向一个新的指定地址。例如:由于一次网站目录调整,原有的 /people/ 子目录下的所有网页全部移动到/info/employees/目录,原访问者从收藏夹或者其他什么地方点击链接发出访问/people/目录下的文件的请求时,你肯定希望他还是能通过原有地址看到和原来相同的页面,但实际上看到的却是网址重写指向的新目录下的相应文件。
在老版本ASP中,使用网址重写技术的途径很少,要么写一个ISAPI过滤器,要么购买第三方厂商提供的网址重写组件,然而在微软提供的ASP.NET下你可以通过多种方法很简单地开发出自己的网址重写软件,以满足自己各种不同的需要。本文将和你一起讨论这门针对ASP.NET开发人员的实现网址重写的技术,然后举一些网址重写实际应用的例子。在我们深入探讨网址重写技术的细节之前,我们先看一下日常使用网址重写技术实现的场景。
1.3. 网址重写的一般用途
创建一个数据操作的ASP.NET程序最常见的就是一个aspx页面后面带上一些查询参数集合。例如在设计一个电子商务网站的时候,假定你设计了一项功能允许用户浏览待售的商品,为了更加方便操作,你设计了一个页面displayCategory.aspx将商品按照给定的分类显示,那么该分类下的商品显示页面上应该在页面文件对应网址后面加上了一个商品分类的查询参数,例如用户要查询待售的“装饰品”,在数据库中所有的装饰品数据对应的分类编号CategoryID的值为5,那么用户会访问如下网址:http://yoursite测试数据/displayCategory.aspx?CategoryID=5。
创建一个包含类似这样网址的网站最终有两种结果,首先从最终用户的角度来观察,http://yoursite测试数据/displayCategory.aspx?CategoryID=5 这个网址有些杂乱,可行性分析专家Jakob Neilson(主页:http://useit测试数据/) 建议选择网址显示方式时候考虑如下要求(参考网址:http://HdhCmsTestuseit测试数据/alertbox/990321.html):
● 是否简短
● 是否易于输入
● 是否将站点结构形象化
● 是否具有隐蔽性,也就是让用户通过一个虚拟的看似有意义的导航地址访问指向该地址
我想还应该在上述列表中再增加一条:是否便于记忆。http://yoursite测试数据/displayCategory.aspx?CategoryID=5 这个地址没有一个地方符合Neilson标准的任何一条,也不便于记忆。当然,对于有经验的网络开发专家来说,他们很熟悉这种键值对构成的查询参数结构体系,然而对于普通用户来说输入这些带有参数的网址实在是太麻烦了。
一种较好的方法就是使用一种比较直观且容易记忆的方式来将网址表示为:http://yoursite测试数据/products/Widgets 乍一看很容易就会推断这个网址所对应的内容极有可能会是显示装饰品(Widgets)信息,这个网址就变得更加容易记忆和传播!然后我告诉我的同事:“请查看这个网址:http://yoursite测试数据/products/widgets ”不用我说第二遍,她可能一次就把地址敲到浏览器上了(你也可以在亚马逊(Amazon测试数据)的网站上这样尝试一下)。很快就浏览器上就列出了装饰品(Widgets)的内容。这里“隐蔽性”表示:用户可以自行变更网址的结尾,例如输入:http://yoursite测试数据/products 就能看到全部分类相关的商品列表或者列出所有相关商品分类目录列表。
注:用上述简单的变更网址内容的方法来构思一下如今的比较流行的Blog网站生成的网址。例如:要查询2004年1月28日所发的帖子,只需输入 http://someblog测试数据/2004/01/28 即可,如果将网址裁减为 http://someblog测试数据/2004/01 则显示 2004年1月份的帖子 ,同样将月份裁减掉得到 http://someblog测试数据/2004 则显示出2004年全年所发的帖子。
网址重写技术除了用于将复杂的网址简单化之外,它还能用于处理因网站目录调整或者其他原因导致产生大量的无效链接和过期书签。
1.4. 当一个Web请求传送到IIS会发生什么?
在探讨如何实现网址重写这项技术之前,很有必要了解一下IIS是处理所接收的Web请求的机制。当一个Web请求到达IIS Web服务器时,IIS会根据所请求的文件后缀名来决定如何处理该请求,IIS可以处理诸如HTML页面、图片、静态内容,或者将请求转发给ISAPI应用程序,由该ISAPI应用程序处理后生成HTML静态内容返回给IIS,最后由IIS将请求结果发送回给客户端。(一个ISAPI应用程序就是一套编译好能随时在后台运行的类库,它的任务就是根据请求生成相关的内容。)
例如:如果IIS接收到一个对Info.asp的请求,它会将该请求转交给asp.dll来处理,该ISAPI应用程序调出并执行所请求的ASP页面,然后把生成的HTML代码返回给IIS,IIS最后把内容发送回请求客户端。对于ASP.NET页面,IIS则将请求转交给名为aspnet_isapi.dll的ISAPI应用程序来处理,该ISAPI应用程序调用托管的ASP.NET工作进程来处理该请求,并将生成的HTML代码返回给请求客户端。
你可以自定义IIS,将某一类扩展名映射到指定的ISAPI应用程序,图一显示了IIS管理工具中的应用程序配置对话框。
图一.已配置的文件扩展名映射
关于对IIS如何管理所接收的请求的详细探讨有些超出本文内容,想了解的更详细的话可以参考Michele Leroux Bustamante的著作 Inside IIS and ASP.NET (深入研究IIS与ASP.NET),重要的是要了解ASP.NET引擎只负责处理对扩展名已经被正确配置映射到aspnet_isapi.dll的网络请求。
1.5. 用ISAPI过滤器来分析请求
除了将请求的文件扩展名映射到相应的ISAPI应用程序外,IIS还执行一些其他工作。例如IIS还主动对发出请求的客户端用户进行授权,并判断已授权用户是否对其请求的文件拥有访问权限,在一个请求过程的全部生命期内,IIS的处理经历了几个阶段,在每一个阶段IIS都生成一个事件,而该事件可以被ISAPI过滤器实时操控的。
如同ISAPI应用程序一样,ISAPI过滤器也是一块块安装在Web服务器上的非托管代码。ISAPI应用程序用于对所接收的特定文件类型做出响应,而ISAPI过滤器含有对IIS生成的事件做出响应的代码(contain Code),甚至可以编辑进出的数据。ISAPI也含有众多应用程序,包括:
· 权限控制与授权(Authentication and Authorization)
· 日志记录与监视(Logging and Monitoring)
· HTTP内容压缩(HTTP Compression)
· 网址重写(URL Rewriting)
本文所探讨的用ASP.NET实现的网址重写技术就是基于ISAPI过滤器用于网址重写的技术内容,然而我们仍然要讨论一下究竟是使用ISAPI过滤器还是使用ASP.NET应用程序提供的技术来实现网址重写技术。
1.6. 当一个请求传入ASP.NET引擎的时候会发生什么?
ASP.NET问世之前,在IIS Web服务器上的网址重写功能需要通过ISAPI过滤器来实现,自从这个家伙问世后我们就能通过ASP.NET来实现URL重写了,因为ASP.NET的解释引擎与IIS有极大的相似之处,产生这些相似性主要是因为ASP.NET:
· 在处理接收的请求的生命期内也会产生事件;
· 允许任意数量的 HttpModule 操控产生的事件,这与IIS中的ISAPI过滤器类似;
· 将请求的资源委托给 HttpHandler 处理,这与IIS中的ISAPI应用程序类似。
和IIS一样,在一个请求的整个生命期内,ASP.NET对该请求的处理状态发出的状态改变信号引发相应的事件。例如: BeginRequest 事件在ASP.NET开始响应客户端请求之始引发; AuthenticateRequest 事件在ASP.NET确立用户身份后引发,当然还有诸如 AuthorizeRequest , ResolveRequestCache 和 EndRequest 等其它很多事件,这些都是 System.Web.HttpApplication 类下的事件,更多信息请参考技术文档中的类 HttpApplication 概要( HttpApplication Class Overview )。
如上所述,可以创建ISAPI过滤器并用于相应IIS引发的事件,同理,ASP.NET也提供了 HttpModule 用于响应ASP.NET引擎引发的事件,一个ASP.NET应用程序通过配置可以拥有多个 HttpModule 。ASP.NET引擎每处理一个请求,便初始化一个相应配置好的 HttpModule ,并允许它针对请求处理期间引发的事件生成相应的事件委托。事实上ASP.NET引擎处理每一个请求调用大量的事件委托。 FormsAuthenticationModule 就是众多内嵌 HttpModule 中的一个,它首先检查是否使用表单授权,如果是的话,它将检查用户是否已授权,如果没有授权则自动把用户重定向到指定的登录页面。
回忆在IIS中,一项请求最后被转交给一个ISAPI应用程序处理,该应用程序针对每一项请求进行处理并返回相应的数据。例如,客户端发出一个访问经典ASP页面的请求,IIS将该请求转交给asp.dll程序处理,asp.dll针对该请求执行asp页面内容,并返回HTML编码。ASP.NET也使用了类似的手法,ASP.NET引擎在将这些 HttpModule 初始化后,判断并决定调用相应的 HttpModule 来处理该请求。
所有通过ASP.NET引擎解析的请求最终被送交一个 HttpHandler 或者 HttpHandlerFactory (一个 HttpHandler 只是简单地返回一个用于处理该请求的 HttpHandler 的实例。)最终的委托呈现并响应所请求的HTML编码,并发送回IIS,IIS则将HTML返回给请求客户端。
ASP.NET包含许多 HttpHandler ,例如, PageHandlerFactory 是用于呈现ASP.NET页面内容, WebServiceHandlerFactory 用于呈现ASP.NET Web服务的SOAP数据包, TraceHandler 用于将ASP.NET请求资源的HTML标记写入trace.axd。
图二描绘了一个针对ASP.NET资源的请求所经过的处理流程。首先,IIS接收到该请求并将其转交给aspnet_isapi.dll。其次,ASP.NET引擎将一些 HttpModule 初始化。最后,最终的 HttpHandler 被调用,生成相应的标记语言,并将其返回给IIS,最终返回到请求客户端。
图二.IIS和ASP.NET对请求的处理过程
1.7. 创建并注册自定义HttpModule和HttpHandler
创建自定义 HttpModule 的工作相对较简单,它包括一个实现当前接口的托管类, HttpModule 必须实现 System.Web.IHttpModule 接口,同样 HttpHandler 和 HttpHandlerFactory 必须分别实现 System.Web.IHttpHandler 接口和 System.Web.IhttpHandlerFactory 接口。有关创建 HttpHandler 和 HttpModule 的细节已经超出本书范围,要了解更多详情请参阅Mansoor Ahmed Siddiqui的文章《 HttpHandlers and HTTP modules in ASP.NET 》(ASP.NET中的HttpHandler和HttpModule)。
一旦 HttpModule 和 HttpHandler 被创建后,必须向Web应用程序注册。如果要向整个Web服务器 HttpModule 和 HttpHandler 只需简单的写入machine.config文件;如果是由指定的Web应用程序调用则需在该程序的web.config配置文件中添加几行XML标记。
例如,要向指定的Web应用程序注册 HttpModule 和 HttpHandler ,只需向该Web应程序的web.config配置文件中configuration\System.Web节中添加下列几行:
< add type ="type" name ="name" />
</ HttpModules >
其中 type 属性为 HttpModule 的标识号和类库名称, name 属性则为该模块取一个较为友好的名称方便在Global.asax调用。
HttpHandler 和 HttpHandlerFactory 则是在web.config文件中configuration\System.Web节中添加<httpHandler>标记,例如:
< add verb ="verb" path ="path" type ="type" />
</ HttpModules >
回忆上文,ASP.NET对每一个接收到的请求指派相应的HttpHandler来处理并呈现相应内容,该指派决定于所接收请求的verb和path的内容,verb为HTTP请求的类型:GET或者POST,path则为请求的文件的路径和文件名。如果我们打算用一个 HttpHandler 来处理所有GET类型和POST类型的并且文件扩展名为.scott的内容,可以在web.config相应配置节中加入下列标记:
< httpHandlers >< add varb ="*" path =".scott" type ="type" />
</ httpHandlers >
其中type是我们定义的 HttpHandler 的类型。
注意:在注册 HttpHandler 的时候必须注意 HttpHandler 所使用的文件扩展名必须已经在IIS中做指向ASP.NET引擎的映射,在上面.scott扩展名的例子中,如果我们所使用的.scott扩展名如果没有在IIS中做指向ASP.NET引擎的映射的话,假定对foo.scott文件发出请求,该请求将导致IIS将foo.scott文件内容直接呈现给客户端,为了能够让 HttpHandler 处理该请求,必须将.scott扩展名在IIS中做指向ASP.NET引擎的映射,之后IIS才能正确地将.scott的请求转交给相应的 HttpHandler 。
有关 HttpModule 和 HttpHandler 更详细的内容请参阅MSDN中<HttpModules>节和<httpHandlers>节的文档信息。
<HttpModules>文档参考;
<httpHandlers>文档参考。
1.8. 实现网址重写
网址重写技术不但可以在IIS Web服务器一级通过ISAPI过滤器实现,而且还可以在ASP.NET一级通过 HttpModule 或者 HttpHandler 实现。本文主要关注在ASP.NET一级实现网址重写技术,所以此时不必关注在ISAPI应用程序中实现网址重写的技术细节,而且有很多第三方厂商提供的ISAPI过滤器,比如
Helicon的 ISAPI Rewrite ;
QwerkSoft的 IIS Rewrite ;Port80的 PageXChanger ;
等等。
1.9. 构建网址重写引擎
在ASP.NET中实现网址重写很简单,只需调用 System.Web.HttpContext 类的 RewritePath() 方法即可。 HttpContext 类中包含有关于特定HTTP请求的HTTP规范信息。ASP.NET引擎每接收到一个特定请求后便针对该请求创建一个特定的实例,这个类包含一些属性诸如: Request 和 Response 属性,分别提供对请求和响应的访问; Application 和 Session 属性提供对 Application 变量和 Session 变量的访问; User 属性提供对已授权用户信息的访问。
在微软.NET Framework 1.0版本中, RewritePath() 方法接收一个新路径的简单字符串,在其内部 HttpContext 类的 RewritePath(string) 方法内在地更新Request对象的路径和查询参数。除了 RewritePath(string) 方法之外,.NET Framework 1.1版还提供了另外一些重载版本,其中一个重载版本接收三个输入字符串参数,这种交替的重载形式不仅仅只是设置 Request 对象的路径和查询参数这些属性,而是设置更深层的成员变量,这些成员变量用于为 PhysicalPath 、 PathInfo 、 FilePath 属性计算 Request 对象值。
为了实现ASP.NET中的网址重写,我们需要创建一个 HttpHandler 和 HttpModule 用于:
根据请求的路径决定所需要重写的路径;
重写路径,如果需要的话可以调用RewritePath方法;
以前文所构建的那个站点为例,可以通过/info/employee.aspx?empID=EmployeeID来访问每一个雇员的信息。为了使这个网址更加地具有“隐蔽性”,我们可能会使用更加容易理解的访问方式如:/people/雇员名.aspx。这里就有了一个网址重写的案例:当接收到对/people/ScottMitchell.aspx的请求的时候,我们就得使用网址重写使得对该页面的请求被重写指向到先前使用的/info/employee?EmpID=1001地址。
1.10. 使用HttpModule来调用网址重写
在ASP.NET一级来执行网址重写,既可以使用 HttpHandler ,也可以使用 HttpModule 。当使用 HttpModule 的时候,必须决定如果该网址需要被重写的话,究竟应该在整个请求的生命周期期间的那一个点来使用。乍一看着有些武断,但是这个决定以重大而且微妙的方式影响到你的应用程序。之所以作出对网址重写点的选择是因为内嵌的ASP.NET HttpModule 使用 Request 对象的属性值来完成自己的工作(回忆一下重写路径对 Request 对象的属性值的改变),这些内嵌 HttpModule 和相应事件的密切关系列举如下:
HttpModule
事件
简介
FormsAuthenticationModule
AuthenticateRequest
判断用户是否已通过表单授权方式获取授权,如果没有的话则将用户重定向到指定的登录页面
FileAuthorizationModule
AuthorizeRequest
当使用Windows授权方式的时候,该 HttpModule 判断并确定该Microsoft Windows帐户是否对其请求的资源拥有足够的权限
UrlAuthorizationModule
AuthorizeRequest
检查并确认请求者是否对所访问的网址拥有权限。该Url授权可以在web.config文件的<authorization>和<location>元素中配置
回想一下 BeginRequest 事件在 AuthenticateRequest 事件之前引发,而 AuthenticateRequest 事件又在 AuthorizeRequest 事件之前引发。
实现网址重写的一个较为安全的场合就是把它放在在 BeginRequest 事件中执行,这意味着如果要执行网址重写的话,在众多内嵌 HttpModule 运行的时候他已经完成了。这种途径的最终用途淋漓尽致地体现在表单验证上。当用户访问受限资源的时候,如果之前使用了表单验证,他会自动被重定向到指定的登录页面,在成功登录之后,用户被重定向回先前试图访问的受限制页面。
如果把网址重写放在 BeginRequest 事件或者 AuthenticateRequest 事件中,在登录页面上执行提交后,该页面会将用户重定向到网址重写指定的页面。假定当用户在浏览器上敲入/people/ScottMitchell.aspx地址,该地址是要被重定向到/info/employee.aspx?EmpID=1001的,如果该Web应用程序设定使用表单验证,当用户开始访问/people/ScottMitchell.aspx的时候,该网址将重写指向/info/employee.aspx?EmpID=1001,接着 ForumAuthenticationModule 启动,如果需要的话将用户重定向到登录页面,用户登录后重定向到的页面将是/info/employee.aspx?EmpID=1001,这也是自从 FormAuthenticationModule 启动运行时所发出请求的页面。
同上类似,当把网址重写放在BeginRequest事件或者AuthenticateRequest事件中运行的时候, UrlAuthenticationModule 也发现了网址重写指向的网址,这意味着如果在该应用程序的web.config文件中<location>节为特定的网址配置特定的授权地址的话,你得引用重写所指向的网址。
为了解决这个微妙的问题,一个可能就是把网址重写放在 AuthorizeRequest 事件中运行,但是在使用这种方法解决URL授权和表单授权的异常时又引入了一个新的缺陷:文件授权会失效。当使用Windows验证的时候, FileAuthorizationModule 检查并验证已通过验证的用户是否拥有足够的权限访问特定的ASP.NET页面。
假定有一群用户并没有Windows级别的访问权限访问C:\inetpub\wwwroot\info\employee.aspx,当这些用户试图访问/info/employee.aspx?EmpID=1001的时候,他们会得到未授权的错误,如果我们把网址重写放到 AuthenticateRequest 事件中运行,当 FileAuthorizationModule 验证该安全性设置的时候,他仍任人为被请求的文件是/people/ScottMitchell.aspx,而这时该网址已经被重写了,因此 FileAuthorizationModule 会直接放行,让用户看到了网址重写指向的内容:/info/employee.aspx?Empid=1001。
那么什么时候在 HttpModule 调用网址重写合适呢?他决定于所使用的验证方式,当然如果不使用验证方式的话,那么无论是在 BeginRequest 事件、 AuthenticateRequest 事件还是 AuthorizeRequest 事件中调用网址重写没有多大区别,如果使用表单验证方式并且不使用Windows验证方式的话,把网址重写放入 AuthorizeRequest 事件委托中调用既可,如果使用Windows验证方式的话,把这项功能放入 BeginRequest 事件或者 AuthenticateRequest 事件调用就行了。
1.11. 使用HttpHandler来调用网址重写
除了上面所述方法外,网址重写也可以放入 HttpHandler 或者 HttpHandlerFactory 中调用。 HttpHandler 是一个负责针对特定请求生成相应内容的类,而 HttpHandlerFactory 返回一个HTTP的实例,该实例针对特定请求生成相应内容。
本节将着眼于为这些ASP.NET页面创建一个网址重写的 HttpHandlerFactory 。创建 HttpHandlerFactory 必须实现 IHTTPHandlerFactory 接口,它包括一个 GetHandler() 方法。ASP.NET引擎在初始化这些 HttpModule 后做出决定针对该请求调用相应的 HttpHandler 或者 HttpHandlerFactory ,在调用 HttpHandlerFactory 的时候,针对该Web请求以及随同的其他信息的 HttpContext 中经过的的 HttpHandlerFactory 的 GetHandler() 方法将被ASP.NET引擎调用, HttpHandlerFactory 必须返回一个能委托该请求的对象,并且该对象要能实现 IHttpHandler 接口。
要通过一个 HttpHandler 来调用网址重写,可以先创建一个 HttpHandlerFactory ,它的 GetHandler() 方法检查所请求的网址并决定是否需要调用网址重写。如果要调用网址重写的话则调用前文所述的已通过检查的 HttpContext 对象的 RewritePath() 方法。最后该 HttpHandlerFactory 返回一个由类 System.Web.UI.PageParser 的 GetCompiledInstance() 方法返回的 HttpHandler 。(这与内嵌于ASP.NET页面的 HttpHandlerFactory ( PageHandlerFactory )的工作原理相同。)
在所有 HttpModule 被初始化后, HttpHandlerFactory 就开始被实例化。把网址重写放在这些事件场所的最后一个里头调用的时候,也会碰到相同的问题:文件授权将会失效。如果非要依赖于Windows验证和文件验证的时候,你可能得使用 HttpModule 来调用网址重写了。
下一章我们着眼于如何构建一个可重用的网址重写引擎,使用下文所提的这些示例均以真实案例作为参照,在作者主页上提供下载。先用用一个简单的网址重写的例子来探讨如何实现网址重写,紧接着将利用网址重写引擎中正则表达式的强大处理能力来展示真正“隐蔽”的网址重写技术!
1.12. 使用网址重写引擎实现简单的网址重写
为了便于在Web应用程序中实现网址重写,我构建了一个网址重写引擎,该引擎提供下列功能:
可以在web.config文件中为页面开发者定义其所使用的网址重写引擎的规则;
通过使用正则表达式来使所制定的网址重写规则具有更加强大的重写能力;
能够通过简单配置即可在 HttpModule 和 HttpHandler 中使用网址重写。
本节只探讨通过 HttpModule 来实现网址重写,要了解如何通过 HttpHandler 来实现网址重写请下载本文提供的代码。
1.12.1. 设置网址重写引擎的配置信息
我们来探讨一下在web.config中网址重写规则的配置节。首先必须在web.config文件中指出是否需要在 HttpHandler 或者 HttpModule 中调用网址重写,在web.config中,下文已经包含了两个已经被注释掉的配置节:
<HttpModules>
<add type="URLRewriter.ModuleRewriter,URLRewriter" name="ModuleRewriter"/>
</HttpModules>
-->
<!--
<httpHandlers>
<add verb="*" path="*.aspx" type="URLRewriter.RewriterFactoryHandler,URLRewriter" />
</httpHandlers>
-->
被注释掉的<HttpModules>为配置使用 HttpModule 调用网址重写;注释掉的<httpHandler>为配置使用 HttpHandler 调用网址重写。
不论配置使用<HttpModules>还是<httpHandlers>调用网址重写,除此之外还须配置网址重写规则,一条重写规则包括两项字符串:请求URL中的查找模式和针对该模式的匹配成功后的替换字符串。该信息在web.config文件中用下列标签描述:
< Rules >
< RewriterRule >
< LookFor > pattern to look for </ LookFor >
< SendTo > String to replace pattern with </ SendTo >
</ RewriterRule >
< RewriterRule >
< LookFor > pattern to look for </ LookFor >
< SendTo > String to replace pattern with </ SendTo >
</ RewriterRule >
</ Rules >
</ RewriterConfig >