iFinity Blogs 

An open letter to the DotNetNuke developers of the world: Please start using the FriendlyUrlProvider API for custom page names!

by Bruce Chapman on Wednesday, April 9, 2008 11:46 AM

I spent a whole cache of hours yesterday tracking down a bug reported with the iFinity Friendly Url Provider. This bug was that the RSS feed button which is in-built into DNN modules didn’t work. The problem turned out to be the same old thing that causes the majority of incompatibility between my Friendly Url Provider and other DNN Core functions and DNN Modules – misuse or non-use of the Friendly Url Provider API. So here’s my plea to DNN developers worldwide, both core and third party: please use all the Friendly Url API features to create your Urls, don’t craft them up yourself within your code!

Generating a DNN internal site Url

Anyone who has written any DNN code will sooner or later have to generate a Url to put as the Href behind a generated link. Soon enough they will come across the DotNetNuke.Common.Globals.NavigateUrl( ) call. This takes in the page you’d like to generate the Url for, plus some other options regarding Portal Alias (the domain name), and any query string values. Great! It works like a charm, and the underlying NavigateUrl() code also figures out whether you are running friendly Urls or not. 

However, developers often want something slightly different to the /default.aspx reference that inevitably shows up at the end of the Url.

Most of the time that I have seen, the developer takes the power into their own hands and starts replacing ‘default.aspx’ with their desired value in their own code.  The code inevitably looks like this:

Dim url as string = DotNetNuke.Common.Globals.NavigateUrl(tab, path, etc..)
url = url.Replace(‘default.aspx’, ‘my-clever-hack.aspx’)

Because the Url Rewriting actually ignores the .aspx, it doesn’t matter, and the code works.

Everyone is happy, and the code is checked in. Don’t believe me? Try going to this Url and tell me if it works or not : http://www.dotnetnuke.com/tabid/125/But_I_Don't_Want_A_Download.aspx  (it’s the DotNetNuke downloads page)

You don’t need the default.aspx – you only need the /tabid/125/.aspx - everything else is superfluous.

The .aspx is only there to tell IIS it’s an asp.net request, and the tabid/125/ does all the heavy lifting in finding the right page to display.

The problem comes when the developer replaces the page name with something that needs to be in the Url to make something work.

In the case of the RSS feeds, you’ve got to request the page called ‘rss.aspx’.

In the e-commerce module ASPDOTNETSF for DNN, it contains the product information, like this:  /products/tabid/55/p-1-product-name.aspx

In both of these cases, the developers that wrote the code replaced the ‘default.aspx’ with the custom page value after calling the Friendly Url Provider.   And this stops the Friendly Url Provider knowing that there is a special case to handle.

Only then someone, in the future, replaces their standard Friendly Url Provider with a new one – and all of a sudden things don’t work anymore, because the default.aspx gets removed from the resulting url that gets generated. Anyone interested in getting Friendly Urls to work on DNN considers the /default.aspx appendix on all the pages to be the greatest problem, closely followed by the tabid/nn path. So default.aspx is always the first up against the wall when the Url revolution comes, and any code that assumes it will be there is now broken.

The Correct Way to get a Url when you want to replace default.aspx

Now, the only module I know of that works correctly is Scott McCulloch’s News Articles module, which isn’t surprising given that he wrote the DNN Friendly Url Provider.  He knows how to call the Friendly Url API correctly, which is to forget NavigateUrl, and call the FriendlyUrlProvider, like this:

DotNetNuke.Services.Url.FriendlyUrl.FriendlyUrlProvider.Instance().FriendlyUrl(tab, "~/Default.aspx?TabId=" & tab.TabID.ToString(), "My_Custom_Page_Name.aspx")

This code calls the Friendly Url Provider, and uses a little-known feature which allows you to specify a custom page name. I’ve posted on DotNetNuke.com forums before about how the Blog module could instantly make thousands of people happier if they used this call to generate the internal links for the module. All it would have to do is supply the title for the blog post as the custom page name, and the entire module would become more valuable to those who are of a SEO bent. 

In truth, the NavigateUrl( ) call should probably have one more overload added to it, which takes a custom page name and passes that through to the configured Friendly Url Provider. Developers would be much more likely to discover the overload and say ‘hey, neat, I can provide any page name I like’. If the DNN core member responsible reads this, then I’ll give you the code on how to do it.

At least the Blog module doesn’t modify the url after the Friendly Url Provider call, though. The other modules which do modify the Url after calling the Friendly Url Provider leave developers (like me) of customized Friendly Url Providers out in the cold when it comes to interoperability.    Developers take note: you need to tell the Friendly Url Provider that a special case is being used, so that it can handle it.

In the case of RSS feeds, just by modifying the core code to call down to the Friendly Url Provider, it’s easy to intercept the call and say ‘oh, that’s for an RSS feed – well, in that case, here’s your RSS url’. Without it, you are stuck with /default.aspx on every page you want an RSS feed. For websites with friendly Urls, that’s a disaster case.

In the case of ASPDOTNETSF, well, that’s even more difficult. The module won’t work at all with any Friendly Url Provider because the whole thing is built on the assumption that DNN urls look like this : domain.com/pagename/tabid/xx/default.aspx. Change that, and the whole thing doesn’t work.

Well, I shouldn’t say ‘won’t work at all with any Friendly Url Provider’ because I have developed a way to make it work with my Url Master for DotNetNuke module. It’s not just by having ‘unfriendly’ urls on the product pages, either, I’m talking about full, human friendly Urls  for the ASPDOTNETSF product and category pages.  But it would have been a lot easier if they had used the Friendly Url API in the first place!


Since writing this, Chris Hammond from Engage has informed me that the Engage:Publish module does indeed use the right way of generating a custom Url.  I should have known this already from another thread regarding the iFinity Friendly Url Provider - I was able to sort out a problem he was having precisely because the Engage:Publish module behaved in the right way!  So add another one to the list.

Blogs Parent Separator Crafty Code
Bruce Chapman

The craft of writing code. The outcomes from being crafty with code. Crafty Code is tales from the coding bench.

18 comment(s) so far...

Anonymous 4/9/2008

Wow, this post is HUGE in IE7, you have to scroll for a mile to the right to read it!

Bruce Chapman 4/9/2008

@chris<br><br>Malformed Html tag destroyed the layout. But all fixed now, thanks for letting me know. It should be nice in any browser :)

Anonymous 4/9/2008

Nice post. Didn't realize that the FriendlyURL provider did that. I'll have to review my code to make sure I'm using it.

Bruce Chapman 4/9/2008

n3bula1<br><br>Hey, join the revolution, and modify your code today! Friendly Urls for all DotNetNuke code :) Seriously, it's a 5 minute change and makes all the difference with the quality of Urls.

Anonymous 4/17/2008

Great article. I suggest that use also highlight also the correct code, not only the bad example.<br><br>

Anonymous 4/17/2008

Bruce,<br><br>Great information.<br><br>Also, a recommendation, if you have the code for the additional overload for the NavigateUrl object, you should post it to support.dotnetnuke.com, supposedly that is the best way to "submit" code for inclusion in the core if you are a non core team member...

Bruce Chapman 4/17/2008

@stefan<br>The suggested code was there, it just has a formatting problem :DotNetNuke.Services.Url.FriendlyUrl.FriendlyUrlProvider.Instance().FriendlyUrl(tab, _ "~/Default.aspx?TabId=" & tab.TabID.ToString(), "My_Custom_Page_Name.aspx")<br><br>@mitchel<br>I'll log in two requests - one for the navigateUrl overload, one for the way the RSS feed aggregator is created.

Bruce Chapman 4/17/2008

The gemini request to add in an extra 'navigateUrl' is here : vote for it if you like it :)<br><br>http://support.dotnetnuke.com/issue/ViewIssue.aspx?id=7400&PROJID=23<br>

Smart-Thinker 6/30/2008

Hi Bruce,<br><br>Great article!<br><br>I would like to update all my modules to use this method but I am concerned about the affect on SEO. For example, on PokerDIY, I have 400 or so poker leagues (for example - http://www.pokerdiy.com/Leagues/PokerLeague/tabid/514/LID/241/Default.aspx). I would much prefer this to be "http://www.pokerdiy.com/Poker-League-241/Rods-Poker-League" or similar, but as soon as I change my NavigateUrl() methods then every league will cause duplicate content.<br><br>Can URL Masters be configured to handle a generic URL rewrite like this or would I need to use PageBlaster to do it?

Anonymous 6/30/2008

@rod<br><br>If you're going to change the way your module works, you do need to think about the knock-on effect with search engines, as you have rightly pointed out. The best way to go is to implement a 301 redirect for your 'old' Urls.<br><br>You can do this inside your module (detect when the 'old' version of the Url was issued by looking at the 'UrlRewrite:OriginalUrl' item in the context.items collection) and issuing a 301 redirect to the 'new' url. <br><br>Or, as you have pointed out, you can use an external module like the Url Master module to implement the 301 redirect for you. Not sure if Page Blaster can handle this for you or not.<br><br>I'll send you a PM regarding the redirects.

mittal soni 8/13/2008

Hi Bruce<br>I am a new bee to Dotnetnuke. I have this problem. Can you please help me to figure out exct place to change this code for FriendlyUrl for RSS feeds

Anonymous 5/19/2009

hi<br>if i write DotNetNuke.Common.Global. i cann't found NavigateURL ?<br>plz explan if i need to add any dll to my protal ?

Anonymous 5/19/2009

sorry<br>i should write <br>DotNetNuke.Common.Globals.<br>not<br>DotNetNuke.Common.Global.<br>:)

Anonymous 5/25/2009

I'm having a problem when using Globals.FriendlyUrl on my localhost. When I use it by running my site through IIS on my localmachine it returns a string such as: <a href="<br>http://localhost/eol/MyPortal/TabId/172/Default.aspx" rel="nofollow">localhost/eol/MyPortal/TabId/172/Default.aspx</a> <br>However if I run the site through VWD using the builtin Webserver I get the value:<br>/eol/MyPortal/TabId/172/Default.aspx <br><br>This is kinda annoying. Anyone else having this issue or am I doing something weird?<br><br>ps this is using the line: Globals.FriendlyUrl(PortalSettings.ActiveTab, Globals.NavigateURL(202))

Bruce Chapman 5/25/2009

@Mark : the behaviour you are seeing is indeed weird, and quite annoying. I'm guessing in your case the reason is the difference between the cassini built in webserver and 'proper' IIS in the way that the Url path information is constructed in the request context. I found this out the hard way while trying to develop a unit testing framework for DNN modules - there's a couple of blog posts about that somewhere.<br><br>The end result is that you have to learn to live with it and write your code to accept either result. Or start rewriting portions of the underlying code. I've rewritten the Friendly Url PRovider to always return the full domain name and path, just to eliminate confusion.

Anonymous 8/23/2010

Hi Bruce,<br><br>Great blog, thanks for the steer. <br><br>Actually, I am currently investigating why the RSS button is not working on the Announcements module for a website which happens to use Url Master. Is the problem with RSS you have described general to DNN core modules, or do you know if it applies to the Announcements module? Is there a workaround?

Bruce Chapman 8/23/2010

@paul - the dnn module RSS feed works by doing a find/replace on the /default.aspx in the string with /rss.aspx. It happens in all modules, but the announcements module is the one everyone uses the RSS feed with.<br><br>See <a href=" http://www.ifinity.com.au/Products/Knowledge_Base/Url_Master/topic/Using+the+DNN+Module+RSS+Feed" rel="nofollow">www.ifinity.com.au/Products/Knowledge_Base/Url_Master/topic/Using+the+DNN+Module+RSS+Feed</a> for how to setup the module to work with the RSS feed.

Anonymous 10/10/2011

I've seen this post way to late obviously. As of today, Nuntio Articles supports this as well. <a href="http://www.snowcovered.com/snowcovered2/Default.aspx?tabid=242&PackageID=23216" rel="nofollow">www.snowcovered.com/snowcovered2/Default.aspx?tabid=242&PackageID=23216</a>

Bruce Chapman
Hi, I'm Bruce Chapman, and this is my blog. You'll find lots of information here - my thoughts about business and the internet, technical information, things I'm working on and the odd strange post or two.
Connect with Bruce Chapman on Google+

Share this page
Get more!
Subscribe to the Mailing List
Email Address:
First Name:
Last Name:
You will be sent a confirmation upon subscription

Follow me on Twitter
Stack Exchange
profile for Bruce Chapman at Stack Overflow, Q&A for professional and enthusiast programmers
Klout Profile