Colin Cochrane

Software Developer based in Victoria, BC specializing in C#, PowerShell, Web Development and DevOps.

Using the ASP.NET Web.Sitemap to Manage Meta Data

kick it on DotNetKicks.com

In an ASP.NET application the web.sitemap is very convenient tool for managing the site's structure, especially when used with an asp:Menu control.  One aspect of the web.sitemap that is often overlooked is the ability to add custom attributes to the <siteMapNode> elements, which provides very useful leverage for managing a site's meta-data.  I think the best way to explain would be through a simple example.

Consider a small site with three pages: /default.aspx, /products.aspx, and /services.aspx. The web.sitemap for this site would look like this:

<?xml version="1.0" encoding="utf-8" ?>

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >

<siteMapNode url="~/" title="Home">

     <siteMapNode url="~/products.aspx" title="Products" />
     <siteMapNode url="~/services.aspx" title="Services" />

</siteMapNode>

 

Now let's add some custom attributes where we can set the Page Title (because the title attribute is where the asp:Menu control looks for the name of a menu item and it's probably best to leave that as is), the Meta Description, and the Meta Keywords elements. 

 

<?xml version="1.0" encoding="utf-8" ?>

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >

<siteMapNode url="~/" title="Home" pageTitle="Homepage" metaDescription="This is my homepage!" metaKeywords="homepage, keywords, etc">

     <siteMapNode url="~/products.aspx" title="Products" pageTitle="Our Products" metaDescription="These are our fine products" metaKeywords="products, widgets"/>
     <siteMapNode url="~/services.aspx" title="Services" pageTitle="Our Services" metaDescription="Services we offer" metaKeywords="services, widget cleaning"/>

</siteMapNode>

 

Now with that in place all we need is a way to access these new attributes and use them to set the elements on the pages.  This can be accomplished by adding a module to your project, we'll call it "MetaDataFunctions" for this example.  In this module you add the following procedure.

 

Public Sub GenerateMetaTags(ByVal TargetPage As Page)
    Dim head As HtmlHead = TargetPage.Master.Page.Header
    Dim meta As
New HtmlMeta

    If SiteMap.CurrentNode IsNot Nothing Then
      meta.Name = "keywords"
      meta.Content = SiteMap.CurrentNode("metaKeywords")

      head.Controls.Add(meta)

      meta =
New HtmlMeta
      meta.Name = "description"
      meta.Content = SiteMap.CurrentNode("metaDescription")
      head.Controls.Add(meta)

      TargetPage.Title = SiteMap.CurrentNode.Description
   
Else

      meta.Name = "keywords"
      meta.Content = "default keywords"

      head.Controls.Add(meta)

      meta =
New HtmlMeta
      meta.Name = "description"
      meta.Content = "default description"

      head.Controls.Add(meta)

      TargetPage.Title = "default page title"
    End If
  End Sub

 

Then all you have to do is call this procedure on the Page_Load event like so...

 

Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load

    GenerateMetaTags(Me)    

End Sub

 

..and you'll be up and running.  Now you have a convenient, central location where you can see and manage your site's meta-data.

SEO Best Practices - Dynamic Pages in ASP.NET

kick it on DotNetKicks.com

One of the greatest time-savers in web development is the use of dynamic pages to serve up database driven content.  The most common examples of which are content management systems and product information pages.  More times than not these pages hinge on a querystring parameter such as /page.aspx?id=12345 to determine which record needs to be retrieved from the database and output to the page.  What is surprising is how many sites don't adequatly validate that crucial parameter.

Any parameter that can be tampered with by a user, such as a querystring, must be validated as a matter of basic security.  That being said, this validation must also adequately deal with a situation when that parameter is not valid.  Whether the parameter is for a non-existant record, or whether the parameter contains letters where it should only be numbers, the end-result is the same: the expected page does not exist.  As simple as this sounds there are countless applications out there that seem to completely ignore any sort of error handling, and are content to have Server Error in "/" Application be the extent of their error handling.  Somewhere in the development cycle the developers of these application decided that the default ASP.NET error page would be the best thing to show to the site's visitors, and that a 500 SERVER ERROR was the ideal response to send to any search engine spiders that might have the misfortune of coming across a link with a bad parameter in it.

With a dynamic page that depends on querystring parameters to generate its content, the following basic measures should be taken:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load    
'Ensure that the requested URI actually has any querystring keys
If Request.Querystring.HasKeys() Then

'Ensure that the requested URI has the expected parameter, and that the parameter isn't empty
If Request.Querystring("id") IsNot Nothing Then

'Perform any additional type validation to ensure that the string value can be cast to the required type.

Else

Response.StatusCode = 404

Response.Redirect("/404.aspx",True)

End If

Else

Response.StatusCode = 404

Response.Redirect("/404.aspx",True)

End Sub


This is a basic example, but demonstrates how to perform simple validation against the querystring that will properly redirect anyone that reaches the page with a bad querystring in the request URL.  A similar approach should be taken when attempting to retrieve the data in the case that the record is not found.

Another useful trick is to define the default error redirect in the web.config file (<customErrors mode="RemoteOnly" defaultRedirect="/error.aspx">), and use that page to respond to the error appropriately by using the Server.GetLastError() method to get the most recent server exception and handling that exception as required.

There are many other ways to manage server responses when there is an error in your ASP.NET application.  What is most important is knowing that you need to handle these errors properly, up to and including an appropriate response to the request.   

Missing Link Data in Google Webmaster Tools

Late last night I noticed that the link data for my sites was missing in Google Webmaster Tools.  I assumed this was the result of some routine maintenance on Google's end and thought nothing of it.  Apparantly this may not be the case, as I came in to work this morning and discovered the the link data was still missing, and was also missing for every single one of our client's sites (both external links and internal links).

I'm not going to throw out wild theories about what Google is up to, but it will certainly be interesting to see if anything comes of this latest development.  If I had to hazard a guess, I would say the most likely explanation is an update of the link database.  However, when it comes to Google, as we all know, the only people that really know what's happening are the people at Google.

 

 

Windows Vista Disappearing System Tray Icons Fix

Yesterday I got out my laptop (which is running Vista Ultimate) to do some work on one of my websites.  When it loaded itself out of sleep mode I noticed that the battery, volume, and network icons in the system tray were missing:

 Naturally I was puzzled by this, and a little annoyed because I could no longer tell how much battery life was left (important because I like to get up and go sit on the deck while I work every so often).  First thing I did was check the Taskbar properties, and found something odd:

 

 

 As you can see in the above image, the checkboxes for the volume, network, and power icons are grayed out and not-selectable.  From my Windows development experience, I knew that Vista keeps a cache of recently used system tray icons in the registry, so on a hunch I popped open RegEdit and found where this cache is stored:

 

and the values

To make a long story short, I backed up the registry, deleted the "IconStreams" and "PastIconsStream" values, opened up the Task Manager, ended the "explorer.exe" process, restarted "explorer.exe" and just like that the missing icons had returned:



An odd problem, but thankfully one that is easily fixed. 

Edit (Added 01/28/2008):  It's worth trying to restart explorer.exe (see Step 4) before trying the registry modifications, because that may fix the problem on its own.


  1. Go to Start > Run (or Windows-key + "R"), type in "regedit" and hit "OK".



  2. Navigate to the key "HKEY_CURRENT_USER\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion \TrayNotify"



  3. Delete the values "IconStreams" and "PastIconsStream"




  4. Now open up the Task Manager (Ctrl + Shift + Esc), go to the "Processes" tab, select "explorer.exe" and click "End Process"



  5. Now open the "Applications" tab and click "New Task..." at the bottom-right of the window.



  6. In the message box that pops up type in "explorer.exe" and hit "OK"



  7. Explorer.exe will reload, and that's it! The missing icons should now be back in the system-tray where they belong.





ASP.NET's Answer to WordPress

It's an exciting time to be an ASP.NET developer.  As the ASP.NET community continues to grow we find ourselves with an ever-increasing aresenal of tools, controls and frameworks at our disposal.  Unfortunately this can make the decision on a component a little more difficult, as very few of the components out there have reached that point where they are largely considered the "standard".  ASP.NET blogging engines certainly fall in to this category, as many of you have probably noticed.

 When I decided to create this blog I had some definite requirements in mind when it came to choosing a blog engine.  First and foremost it had to render completely valid XHTML because I practice what I preach in respect to W3C compliance.  Control over SEO-important aspects such as canonical URLs and meta descrition elements were a necessity.  It also had to have a URL strategy that didn't rely on a "/page.aspx?id=123456789"-style mess of a querystring. Finally, the underlying code had to be well-organized and lean.  Without a de-facto "standard" for ASP.NET blog engines I started hunting around and researching the options that were out there.

I tried some different ASP.NET blog engines such as dasBlog and subText, but found the markup that was rendered was not acceptable.  Then I came across BlogEngine.NET, which, I was thrilled to discover, met all of my requirements.  It's light weight, very easy to set up, and is very well-designed and organized under the hood.  A nice feature is that, by default, it doesn't require a SQL  database to run, instead storing all posts, comments and settings in local XML files.  Of course SQL database integration is available, and is also easy to get up and running.  Aesthetically it comes with a nice collection of themes, which are quite easy to modify, and creating your own themes is a straightforward process. 

The above factors have led me to believe that BlogEngine.NET is in a position to become to ASP.NET what WordPress is for PHP.  If any of you are currently trying to decide on a solid ASP.NET blog system you should definitely try BlogEngine.NET.  You won't be disappointed.