Friendly URLs in ASP.NET Web Forms

When ASP.NET MVC was introduced, it did away with the concept of tying the physical location of a Web Page page to the actual URL that’s seen in the browser, as was the norm earlier in ASP.NET WebForms based applications. As a result, in MVC, URLs like http://myapp.com/blog/my-new-blog-post were easier to setup instead of http://myapp.com/blog?postid. These were colloquially referred to as ‘Friendly Urls’ as in User Friendly as well as Search Engine Friendly.

This was thanks to the Routing feature implemented in ASP.NET. Note the Routing feature is built outside MVC, it’s a part of core ASP.NET and as a result easily available for WebForms as well.
Leveraging the routing framework, Microsoft recently released the FriendlyUrls package via Nuget that helps achieve FriendlyUrls in ASP.NET WebForms applications without too much of manual Route Table setup. If you have Visual Studio 2012 and upgraded to the latest Web Update 2012.2, then you are in luck and any new Web Forms project you create will pull in the required packages and you have FriendlyUrls enabled by default.

Thanks to Sumit Maitra for his help with this article

But if you are maintaining old projects in Visual Studio 2010 using .NET framework 4.0, then you have a couple of manual steps to go and you’ll be all set.

Since Visual Studio 2012 projects are setup on get-go, I’ll show how to add it to VS 2010 projects. Once the project is setup, usage of FriendlyUrls is the same in in both VS 2010 and VS 2012 so I’ll switch to 2012 and show you the rest of the goodies.

Adding Friendly URLs to an existing WebForms project

The Friendly Urls package is available for .NET Framework 4.0+ apps. So if your Web Forms app is not on .NET 4.0 yet, an upgrade is a good idea. I am not demonstrating the Upgrade process here and I assume you have a WebForms project without FriendlyUrls enabled. Let’s say our project is called FriendlyUrls and the About page for it looks like this (note the URL)

about-page-vs-2010

Step 1: Navigate to the Tools > Library Package Manager > Manage Nuget Packages dialog, search for ‘Friendly’ and install the ‘Microsoft ASP.NET Friendly URLs’ package. It will also install a couple of dependencies.

install-friendly-url-in-2010

Step 2: Once the packages are installed, open the Global.asax and setup the Url Routing by adding the following line in Application_Start event.

void Application_Start(object sender, EventArgs e)
{

RouteConfig.RegisterRoutes(RouteTable.Routes);
}


Step 3: Navigate to the App_Start\RouteConfig.cs. This is added when you installed the Friendly URLs package. Change the single line of code in the method from

routes.EnableFriendlyUrls()

to

routes.EnableFriendlyUrls(new FriendlyUrlSettings
{
AutoRedirectMode = RedirectMode.Permanent
}) ;


RedirectMode is an enum with three values – Off, Temporary and Permanent. This relates to how the routing mechanism will handle requests that have the full page URL e.g. someone types in http://myapp/About.aspx. With the default setting, RedirectMode is set to Off. Thus when the .aspx is detected, the aspx URL is actually served up. However if you set the RedirectMode to Permanent even if user types in http://myapp/About.aspx they will be redirected to http://myapp/About/ with a 301. This is important for a search engine because then it can update the URL in its database.
With the above two steps, you have enabled Friendly URLs and (optionally) setup 301 redirects for all your *.aspx URLs.

Step 4: Run the Application. Start your favorite browser’s developer tools and enable Network Capture. Now click on the ‘About’ menu item and navigate to the About page.

about-page-vs-2010-friendly-url

Voila! You have navigated to the page without the extension. The 301 redirect can be seen clearly in the Network capture frame.

So we are set with Friendly URLs and they are working well for now. Next let’s see how query parameters are treated by Friendly URLs.

Note: At this point there is no difference between .NET 4.0 projects created in VS 2010 and .NET 4.x projects created in VS 2012, so going forth I’ll be using a VS 2012 project for the sample.

Query String Parameters and Friendly URLs

Now that you have upgraded to Friendly URLs, how do the old query string parameters map to the new scheme of things on the server side?

Let’s take a scenario where we have a blogging site based on Web Forms. It has the Article.aspx Page in the folder called Blog and to access an article, you pass the article ID as a query string as seen below

blog-page-with-friendly-url

Now if we wanted to enable Friendly URLs by default http://localhost:55360/Blog/Article?id=23 would work. But that doesn’t quite give us the clean URL that we want. We want something like http://localhost:55360/Blog/Article/23 or better still it could be WordPress style URLs http://localhost:55360/Blog/Article/lorem-ipsum-title where lorem-ipsum-title is derived from the Title of the article.

How can we go about this?

First let’s look at how things might be at the backend. It would roughly involve doing the following

1. Extract ID from query String

2. Query Service layer to obtain Domain object

3. Bind Domain object to View or setup view manually

We have skipped the DB layer for brevity and are ‘getting’ some dummy data on the fly.

protected void Page_Load(object sender, EventArgs e)
{
int id = Request.QueryString["id"] != null ? int.Parse(Request.QueryString["id"]) : -1;
if (id > 0)
{
  Model.Blog post = GetBlog(id);
  if (post != null)
  {
   title.Text = post.Title;
   postDetails.Text = post.Post;
   author.Text = post.Author;
  }
}
}


private static Model.Blog GetBlog(int id)
{
Model.Blog post = new Model.Blog
{
  Id = id,
  Author = "Author " + id.ToString(),
  Post = "Lotsa Lorem Ipsum. " + id.ToString(),
  Title = "Lorem Ipsum Title. " + id.ToString()
};
return post;
}

Updating Code to handle Friendly URL parameters

Well now that we have a rough idea of the implementation behind the scene, we know that unless we have the ID property in the query string, things are not going to work. How do we get Query String properties with Friendly URLs? Answer is, we don’t. Instead we get the Request.GetFriendlyUrlSegments() extension method that returns us a collection of FriendlyURLSegments. What is that you ask? Well, the FriendlyUrl route handler looks for the first instance of file with aspx extension that matches the URL. So if a URL has http://mysite/Foo/Bar/123 and there is a file call Foo.aspx in the root, the route handler maps to the Foo.aspx and automatically puts Bar and 123 into the FriendlyUrlSegments collection. Thus our URL http://localhost:55360/Blog/Article/23 it will map to the Article.aspx and put 23 in the FriendlyUrlSegments collection.

We update the Page_Load to get ID from FriendlyUrlSegments collection, as follows

protected void Page_Load(object sender, EventArgs e)
{
int id = Request.QueryString["id"] != null ? int.Parse(Request.QueryString["id"]) : -1;
if (id < 0)
{
  IList<string> segments = Request.GetFriendlyUrlSegments();
  if (segments.Count > 0)
  {
   int.TryParse(segments[0], out id);
  }
}

if (id > 0)
{
  Model.Blog post = GetBlog(id);
  if (post != null)
  {
   title.Text = post.Title;
   postDetails.Text = post.Post;
   author.Text = post.Author;
  }
}
}


So we are checking that if there are no Query Strings with key id, we look in the FriendlyUrlSegments collection and try to retrieve it from there. With this code in place if we now hit the URL http://localhost:55360/Blog/Article/23 we will get our Post correctly:

blog-page-with-friendly-url

Sweet!

Now if we wanted a WordPress style URL, we would have to do a bit more work wherein we would have to create a field in the db that has the last part of the URL and it maps directly to the ID. This way we could easily retrieve the Blog post using strings like “lorem-ipsum-title”.

And that’s a wrap for today.

Conclusion

We saw how to setup Friendly Urls in old WebForm project created prior to Visual Studio 2012’s latest updated (or created in Visual Studio 2010 using .NET Framework 4.0).

Once setup, we saw how we could use the FriendlyUrlSegments to retrieve parameters and use them in place of query strings to load appropriate data without disrupting the business of data layers.

Download the entire source code of this article (Github)




About The Author

Suprotim Agarwal
Suprotim Agarwal, Developer Technologies MVP (Microsoft Most Valuable Professional) is the founder and contributor for DevCurry, DotNetCurry and SQLServerCurry. He is the Chief Editor of a Developer Magazine called DNC Magazine. He has also authored two Books - 51 Recipes using jQuery with ASP.NET Controls. and The Absolutely Awesome jQuery CookBook.

Follow him on twitter @suprotimagarwal.

3 comments:

Hamlet Méndez said...

I trying to implement FriendlyUrls to my project but when I try to use the mobile feature get an error after the first time when I run the project.

The error is:

HTTP Error 404.0 - Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.

Unknown said...

I trying to implement FriendlyUrls to my project when i am using database url that maps id of query string .
how to achieve this so it shows map url without aspx ?

Unknown said...

have set these up and in general it's fine,(i.e showing mysite/mypage instead of mysite/mypage.aspx) however, i cannot get the parameters to work

i want the user to be able to navigate to mysite/hj12Ac and this retrieve from my database the page with id hj12AC and show relevant data on the page

my code is:
protected void Page_Load(object sender, EventArgs e)
{
int id = Request.QueryString["id"] != null ? int.Parse(Request.QueryString["id"]) : -1;
if (id < 0)
{
IList segments = Request.GetFriendlyUrlSegments();
if (segments.Count > 0)
{
int.TryParse(segments[0], out id);
}
}
if (id > 0)
{
//database code to retrieve the page date
//page controls are either hidden or shown based on whether there being a page ID to retrieve

}
else
{
//page controls are either hidden or shown based on there being no pageID to retrieve
}
}

however: mysite/default/hj12AC loads as a page, but loads as if there is no pageid

mysite/hj12AC returns a 404 page not found error

can anyone suggest where i am going wrong?