WebMatrix - URLs, UrlData and Routing for SEO

There is a certain amount of debate surrounding the importance of friendly URLs in terms of Search Engine Optimisation and improving your ranking. One thing is certain - having friendly URLs helps users to identify whether the content at the end of a link to your site is likely to be relevant to them or not. This article examines how you can make use of the built-in support for Routing provided by ASP.NET Web Pages to not only create friendly URLs, but to work with parameters as well.

In a typical ASP.NET Web Forms application, incoming requests are mapped to physical files on disk. You can change this behaviour through the use of System.Web.Routing, which requires a little configuration within the application. ASP.NET Web Pages, on the other hand, provides support for routing out-of-the-box, although it is not as powerful as configuring your own through System.Web.Routing. Nevertheless, this in-built support is likely to be more than enough for managing friendly URLs for most sites.

First, what is meant by Friendly URLs? Take a look at the URLs on this site. This article can be found at http://www.mikesdotnetting.com/article/165/webmatrix-urls-urldata-and-routing. It should be clear to most people, just by reading the bold parts of the URL what they can expect to see if they requested it: an article on URLs, UrlData and Routing as pertaining to WebMatrix. A previous incarnation of this site would have presented the URL as http://www.mikesdotnetting.com/Article.aspx?ID=165. Not particularly meaningful... Over the years, a number of commentators have claimed that Google simply will not index URLs such as the one with the querystring. This is not true. My site was regularly indexed. However, you should be aware of this advice taken from Google's Webmaster Central guidelines:

If you decide to use dynamic pages (i.e., the URL contains a "?" character), be aware that not every search engine spider crawls dynamic pages as well as static pages. It helps to keep the parameters short and the number of them few.

Following that, is there any benefit to having key words appear in a friendlier URL? According to Google, the answer is Yes - it helps "a little bit". Of course, they won't say how much "a little bit" is.

OK. Now that we have got all that out of the way, how does Routing work with ASP.NET Web Pages? If you use Routing in ASP.NET Web Forms, or MVC, you can control how URLs map to resources with complete freedom. The built-in routing that comes with Web Pages offers a significant amount of freedom, but URLs must ultimately map to physical files on disk. That means you must have at least one file that matches part of the URL. Let's look at that in a bit more detail.

Note: I have written an article that covers applying the same routing system that you get in other parts of ASP.NET (namely MVC and Web Forms) in Web Pages sites. You can read it here: More Flexible Routing For ASP.NET Web Pages

A URL is composed of a number of parts. The first is the domain. In the case of my site, that's http://www.mikesdotnetting.com. After that comes a backslash (/) followed by one or more segments, each containing data. There is no limit to the number of segments that a URL can contain. WebMatrix will initially take all the segments and attempt to match them to a file path. If no file is found, WebMatrix will start considering trailing segments to be part of UrlData, and attempts to match the remainder to a physical file. This sounds a little confusing, so an example should help.

Create a site with the following structure:

The site has a folder called 'a', inside of which is another folder called 'a', inside of which is a third folder called 'a'. Inside of each folder (including the root folder), there is a file called 'a.cshtml'. Each file contains a paragraph describing its location. For example, the file in the root folder contains this markup:

<p>Page A in Root folder</p>

The file in the third level folder contains this markup:

<p>File A in Folder A in Folder A in Folder A</p>

If you select this file in WebMatrix and click the Run button, the resulting URL in your browser should look something like this (although your port number will very likely be different):

http://localhost:6324/a/a/a/a.cshtml

That's what you would expect to have to request in order to run that particular page. Now remove the ".cshtml" from the URL so that it becomes http://localhost:6324/a/a/a/a. Request this new URL, and you should get the same page - File A in Folder A in Folder A in Folder A. the Web Pages framework looks at all the segments, and attempts to turn it into a path:

In this case, it's looking for a file named 'a' three folders deep from the root, with all three folders named 'a'. If you remove the last 'a' and make another request (http://localhost:6324/a/a/a), you should get the file named 'a' two folders deep. Now change the last letter in the URL to a 'b': http://localhost:6324/a/a/b. You should get a.cshtml in the first level folder named 'a'. But what happened to the 'b' on the end of the URL? Web Pages couldn't find a file named 'b' in the second level folder, so it assumed that 'b' is UrlData, and looked to match the remaining segments to a file path. If no matches are found during the search for files, Web Pages will attempt to locate a default document instead. The two default documents which work are default.cshtml and index.cshtml in that order. However, this search is performed once, and assumes that the URL is entirely a file path, and contains no UrlData.

UrlData

Essentially, UrlData behaves in a very similar way to a querystring, except that each value is located by position rather than being referenced by it's name. It is a way of passing arbitrary data via a URL. UrlData is populated to a List<string>, which can be iterated through - or you can reference items according to their numerical index (eg UrlData[0]). You can test this for yourself, by adding the following code to the deepest level 'a.cshtml' file:

<p>Total number of items in UrlData: @UrlData.Count</p>
<ul>
    @for(var i = 0; i < UrlData.Count; i++){
        <li>UrlData[@i]: @UrlData[i]</li>
    }
</ul>

Now request the following URL: http://localhost:6324/a/a/a/a/b/c/d/e, and you should see that the page reports 4 items within the UrlData collection, which are iterated through and displayed according to their index in the collection:

When you reference items in the UrlData collection, you should treat them in the same way as any other user input. Just as you have manipulated the URL that you requested if you have been following along with the exercises, so a malicious user can manipulate the URL to attempt to inject something nasty into it. All the values are passed as strings, so you should use IsInt(), IsFloat() etc to test that values can be safely converted, and you should pass the resulting value as a parameter if you intend to use it as part of a database query. It is also worth pointing out that adding another backslash after the URL (http://localhost:6324/a/a/a/a/b/c/d/e/) will add another item to the UrlData collection, except that it will be empty.

Querystrings

Earlier, I said that UrlData behaves in a similar way to querystrings, but UrlData is not a querystring. Querystrings continue to work in exactly the same way as they do for any other framework: you append the URL with a question mark (?) followed by name/value pairs separated by ampersands (&). Going back to the deepest level 'a.cshtml' file, add the following code:

@foreach(string item in Request.QueryString){
    <p>QueryString item: name=@item, value=@Request[item]</p>
}

Now request the following URL:

http://localhost:6324/a/a/a/a/b/c/d/e?foo=bar&baz=foobar

The new piece of code iterates over each item in the querystring and prints its name and value to the browser:

Summary

This article should help to explain how routing, UrlData and querystrings work with ASP.NET Web Pages. There are good reasons for using the routing system to generate friendly URLs, not least for usability and a little SEO juice. One final thing worth explaining is how my current URL structure (which is driven by ASP.NET MVC) would work with Web Pages. Simply, all you would have to do is create a file called Article.cshtml in the root folder of your site. The number and article title will be treated as two items in UrlData - the first (UrlData[0]) is the identifier for the article, and the second (UrlData[1]) is the title which has been treated to a little method which replaces spaces with hyphens (or dashes) and removes other extraneous punctuation which is not allowed in URLs.

 

Date Posted: Monday, December 20, 2010 12:56 PM
Last Updated: Tuesday, November 11, 2014 8:15 AM
Posted by: Mikesdotnetting
Total Views to date: 33154

14 Comments

Monday, June 4, 2012 2:05 AM - Bob Reid

I get a 404 error when I try routing on localhost. Any ideas as to why?
Thanks,
Bob

Monday, June 4, 2012 5:00 PM - Mike

@Bob,

I suspect you've done something wrong, but I have no clue what. You should post a question to a technical forum and provide a bit more detail about what you have done and what you are trying to do.

Tuesday, July 10, 2012 5:40 PM - Darren Cook

So I cannot create different friendly urls for the same page when it is passed different query strings?

So for example, myblog?postnumber=37 changed to myblog/postTitle

Wednesday, July 11, 2012 5:26 AM - Mike

@Darren

You really need to use the Web Pages Routing helper for that: http://www.mikesdotnetting.com/Article/187/More-Flexible-Routing-For-ASP.NET-Web-Pages

Tuesday, November 27, 2012 10:14 AM - Jon-Paul

Is it possible to do this with a parameter to the home page? For example, www.mywebsite.com/hello to pass a parameter of "hello" to default.cshtml?

I want to do this without adding the /default/hello as it looks messy.

I have found your tutorials so helpful by the way and they have transformed the way that I use Webmatrix.

Thanks for any help.

Thursday, November 29, 2012 9:10 PM - Mike

@jon-paul,

No it is not possible to do that with the default routing setup.

Tuesday, March 19, 2013 11:42 PM - PVL

@Mike,

What's the performance impact of having nested files, say a/b/n/v/f while having _PageStart.cshtml in many of these folders, while compared to an MVC execution.

Is MVC, or even a universal route to an index file, manually checking if path exists and rendering corresponding files faster?

PS: I'm looking at high-performance scenarios where every instruction counts. I'm sure it really doesn't matter in normal scenarios.

Wednesday, March 20, 2013 9:22 PM - Mike

@PVL,

I have no idea. That's something you need to test for yourself in your own environment.

Wednesday, July 24, 2013 11:19 AM - vincenth69

Hi, I'm trying to get Data from my view but the UrlData is not recognized.
The adresse is localhost:8080/Home/Result/1 (Where the 1 is the number of the page -> the UrlData)
Yet, the DataUrl[0] is empty from my Result page and when I try a UrlData.Count() it gives me 0.
Do you know from where the problem could come from ?
As a result, I have my first iteration of the Reslut Page.
Thanks in advance.

Friday, July 26, 2013 11:53 AM - Mike

@vincenth69

For your scenario to work, you would need a page called Result.cshtml in a folder called Home.

Wednesday, October 16, 2013 5:27 PM - terry

Adding parameters causes all the support files to go missing because the path to them no longer works. Page contains a references to "css/styles.css"

This works: /mypage

This fails: /mypage/someparameter

It finds the page but can't find the styles in /mypage/someparameter/css/

Wednesday, October 16, 2013 9:31 PM - Mike

Terry,

Your css file behaves entirely correctly. You are providing a relative (to the url) path so it will be appended to the current url. You should provide an absolute path instead by prefixing the path with a forward slash:

/css/styles.css

The forward slash is resolved by most browsers as the root folder. In Web Pages, you can prefix the whole lot with a tilde (~) which will ensure correct resolution of the root:

~/css/styles.css

Sunday, July 6, 2014 3:26 AM - Ed

This is one of those things that make WebPages "golden" to me, and your article, though I'm only commenting now, was how I "discovered" it (1+ yrs) ago. Thank you.

Just out of curiosity, how would one replicate this behavior in (full-blown) MVC (or can/is it?), where it seems you have to define routes no matter what - I think the above is like a "dynamic optional" param route :)

Sunday, July 6, 2014 3:36 AM - Ed

Hi mike,

On that last comment, please disregard the 2nd part...da Googles gave me dis:

Variable segments:
http://msdn.microsoft.com/en-us/library/vstudio/cc668201(v=vs.100).aspx
Add your comment

If you have any comments to make about this article, please use this form to do so. Make sure that your comment relates specifically to the article above. More general comments can be posted through the form on the Contact page.

Please note, all comments are moderated, and some may not be published. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Not relevant to the article
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam
  • Anything in a language I don't understand including gibberish.

I do not pass email addresses on to spammers, so a valid one will assist me in responding to you personally if required.

Recent Comments

Bino 11/27/2014 7:05 PM
In response to MVC 5 with EF 6 in Visual Basic - Async and Stored Procedures with the Entity Framework
Copy +...

Manas 11/27/2014 5:30 AM
In response to Scheduled Tasks In ASP.NET With Quartz.Net
Hi Mike, Thank you for awesome article. My concern is it might impact website if we use or is...

priya 11/26/2014 6:50 PM
In response to Create PDFs in ASP.NET - getting started with iTextSharp
very nice.....its save my time...

ransems 11/24/2014 12:29 AM
In response to Adding A Controller
Love the article. I dislike that the world thinks c# articles are the way to go. Love the VB, keep...

Gautam 11/20/2014 8:01 AM
In response to I'm Writing A Book On WebMatrix
Hello Mike, I read your book, loved it! However, I have a few request/suggestions: 1) an example...

Bret Dev 11/19/2014 8:39 PM
In response to The Difference Between @Helpers and @Functions In WebMatrix
Excellent post! One concern - where can you place global @Functions code within an MVC project to Is...

Rob Farquharson 11/19/2014 4:28 PM
In response to iTextSharp - Links and Bookmarks
How can I place text at an absolute position on the page? Also, how can I rotate text?...

Andy 11/17/2014 8:08 PM
In response to MVC 5 with EF 6 in Visual Basic - Sorting, Filtering and Paging
Hello I'm testing your sorting instructions above. This is great and I was able to get it to work...

Gautam 11/17/2014 5:51 PM
In response to WebMatrix - Database Helpers for IN Clauses
Hi Mike, I am very new to programming: In the above example if I want to use a delete button the...

donramon 11/17/2014 3:22 PM
In response to Entity Framework 6 Recipe - Alphabetical Paging In ASP.NET MVC
Congratulations on your new website look and the excellent articles. Thank you!...