传统的URL的形式如同:
http://www.dotnetnuke.com/default.aspx?tabid=510&Name=Pm2004
页面通过parameter=value的形式给页面传递参数,页面根据参数加载不同的内容。但是,DNN因为以下原因放弃了传统的形式:
(1) 这种方式不利于阅读和人们的理解。不够人性化。
(2) 很多搜索引擎会忽略,搜索引擎会避免参数。不利于网络搜索。
目标:
(1) 有利于搜索引擎发现
(2) 有利于人们阅读
(3) 适合child portal
(4) 不影响其它应用
(5) 性能
DNN Friendly URL基本思想
DNN的解决方案类似于翻译员,将一种语言翻译成另外的一种语言。最终的本质还是带有参数的形式,只是在表现上,在URL的字符串中出现的是Friendly的。这里用到的一个术语是URL Schema的规则集。通过这组规则完成原生URL和Friendly URL之间的相互翻译。
如:
原始的URL是:
http://www.dotnetnuke.com/default.aspx?tabid=703
被翻译为:
http://www.dotnetnuke.com/tabid/703/default.aspx
URL Schema将一种表达方式转化为另一种表达方式,这种结果是确定的,而且是唯一的。这是我们谈到的一种情况,就是请求URL的转换方式。HTTP还包含一种响应模式,我们的机制同时要满足这样的要求,例如用户可能希望看到新闻组的网页在浏览器中显示的是:
http://www.dotnetnuke.com/About/News.aspx,
而不是:
http://www.dotnetnuke.com/About/News/tabid/703/Default.aspx
相关组件
URL Schema
在SiteUrls.config文件中定义了相应的规则:
<RewriterConfig>
<Rules>
<RewriterRule>
<LookFor>.*/TabId/(\d+)(.*)/Logoff.aspx</LookFor>
<SendTo>~/Admin/Security/Logoff.aspx?tabid=$1</SendTo>
</RewriterRule>
<RewriterRule>
<LookFor>.*/TabId/(\d+)(.*)/rss.aspx</LookFor>
<SendTo>~/rss.aspx?TabId=$1</SendTo>
</RewriterRule>
<RewriterRule>
<LookFor>[^?]*/TabId/(\d+)(.*)</LookFor>
<SendTo>~/Default.aspx?TabId=$1</SendTo>
</RewriterRule>
</Rules>
</RewriterConfig>
结合对源码的分析,这里的规则是全匹配,而不是局部的匹配。同时利用正则表达式
HTTP Module
在Web.Config中定义了:
<add name="UrlRewrite" type="DotNetNuke.HttpModules.UrlRewriteModule,
DotNetNuke.HttpModules.UrlRewrite" />
</httpModules>
在HTTP请求中,IIS允许开发者在HTTP请求的管道中插入若干Module来处理加工/处理该请求。Module像在管道中的一个截面,流经这个截面的信息都被Module处理一下。
在URLRewriteModule中 Init 只是定义了一个事件而已
AddHandler application.BeginRequest, AddressOf Me.OnBeginRequest
在OnBeginRequest中处理的过程比较复杂,这个方法中包含了一些基本的URL转换规则定义,另外的一部分在SiteUrls.Config中以URL Schema的形式进行了定义。
基本的步骤是:
- 检查请求URL的合法性,并将其转换为绝对请求地址。
- 读入SiteUrls.Config文件中的规则。
- 轮询这些规则,如果匹配则进行转换。
- 在转换TabID,Portal Alias等基本规则。
- 其他的一些处理。
Friendly Url的实战
需求
在网站的开发中,我们经常需要跳转,从一个页面转入另外的页面,虽然我们理解了DNN Friendly Url的基本思想,但是在实战中,我们拼凑一个这样的URL还是很费力的,为此DNN提供了几个辅助类和相应的方法。下面我们介绍这些方法和类,并且给出一些应用。
DNNFriendlyUrlProvider
沿袭ASP.NET2/DNN的一贯思想,使用Provider的机制来灵活定制程序的运行方式,Friendly Url也是采用这种方式,在Web.Config中定义了:
<providers>
<clear />
<add name="DNNFriendlyUrl" type="DotNetNuke.Services.Url.FriendlyUrl.DNNFriendlyUrlProvider, DotNetNuke.HttpModules.UrlRewrite" includePageName="true" regexMatch="[^a-zA-Z0-9 _-]" />
</providers>
</friendlyUrl>
它实现了
public override string FriendlyUrl(TabInfo tab, string path, string pageName, PortalSettings settings)
public override string FriendlyUrl(TabInfo tab, string path, string pageName)
public override string FriendlyUrl(TabInfo tab, string path)
等方法。
我们考查一下下面这个函数
string path,
string pageName,
PortalSettings settings)
其中
Path就是路径,可能是~开头的相对路径,也可能是绝对路径,可能是/也可能是\。如果有参数(&?形式的参数)就包含在这个字符串中。
PageName是页面文件,比如default.aspx,这个页面会替换掉Path中的页面。
Setting主要是为了取出Portal的根路径或者Alias路径
通过这些参数能够组合,格式化成一个Friendly Url。注意最后形成的Friendly URL中可能也含有参数,目前regexMatch="[^a-zA-Z0-9 _-]这些参数才可以被Friendly化。
EditURL与NavigateURL
翻阅DNN的代码,经常会碰到使用EditURL函数,比如,最常见的我们在可以编辑的Item前面有一支笔,点击后就跳到编辑的页面:
<asp:HyperLink NavigateUrl='<%# EditURL("ItemID",DataBinder.Eval(Container.DataItem,"ItemID")) %>' Visible="<%# IsEditable %>" runat="server" ID="Hyperlink1"><asp:Image ID=Hyperlink1Image Runat=server ImageUrl="~/images/edit.gif" AlternateText="Edit" Visible="<%#IsEditable%>" resourcekey="Edit"/></asp:hyperlink>
EditURL主要是为了跳转到Edit页面而定义的,当然它也可以跳转到其他Control页面。它在模块的基类PortalModuleBase中被定义。它被重载了5次,可以什么参数都不带,也可以Public Function EditUrl(ByVal KeyName As String, ByVal KeyValue As String, ByVal ControlKey As String, ByVal ParamArray AdditionalParameters As String()) As String
它处在模块的基类中,很方便使用,也是出镜率很高的函数之一。
它调用Global.vb中NavigateURL函数,这是一个Static函数。NavigateURL调用Global.vb中的FriendlyUrl函数,该函数按照Provider的机制加载相应的DNNFriendlyUrlProvider
DotNetNuke.Services.Url.FriendlyUrl.FriendlyUrlProvider.Instance().FriendlyUrl(tab, path, pageName, portalAlias)
应用
在DNN 的开发过程中,经常需要跳转页面,下面就不同的场合下给出解决的对策(以下描述的主要是目标URL串如何生成):
(1) 在模块定义中定义了两个或多个Control,需要从一个Control页面跳到另一个页面。
同一个模块,不同Control之间的跳动,而且不涉及参数的传递,使用EditUrl(ControlKey)
(2) 上述的情况,在很多情况下我们需要传入一些参数,特别是ItemID,在接收的模块中会提取此参数进行判断。比如在Blog模块中:
定义了View_Entry Control,可能需要从View Blog中跳转,就可以用EditUrl(“ItemID”,”
同一个模块,不同Control之间的跳动,而且涉及某一个参数的传递,可以使用
EditUrl(KeyName, KeyValue, ControlKey),如果是Edit Control可以省略为
EditUrl(KeyName, KeyValue)
(3) 在有的时候我们可能需要传递更多的参数,我们使用EditUrl(KeyName, ByVal KeyValue, ControlKey, ByVal ParamArray AdditionalParameters As String())
最后的参数是可以被罗列的,比如
EditUrl(“CaterogyCode”,”
它的含义是,跳转到ViewDetail Control页面里,其中CaterogyCode=123,StoreID=431等等。
(4) 如果我们希望在不同的Tab中进行跳转,比如我跳转到首页,或者已知的某个TabID的页面可以NavigateURL(ByVal TabID As Integer)
以下代码帮助跳转到"产品"页:
TabInfo objTab = ctlTab.GetTabByName("产品");
this.Response.Redirect(Globals.NavigateURL(objTab.TabID), true);
(5)
Public Function NavigateURL(ByVal TabID As Integer, ByVal IsSuperTab As Boolean, ByVal settings As PortalSettings, ByVal ControlKey As String, ByVal ParamArray AdditionalParameters As String()) As String
函数,借助它能够带上ControlKey以及N多的额外参数。
总结上面两个函数:
在同一个模块不同Control之间跳转,可以使用EditUrl。
在不同Tab之间跳转使用NavigateUrl。
他们都可以传递若干参数。
这两个函数都是没有办法改变最终的aspx文件名的,但是Globals.vb中定义的FriendlyUrl可以实现Path的自定义,更换*.aspx等等。具有更大的灵活性,但是使用不多见。