Generating a Sitemap in ASP.NET MVC3

Adding a static sitemap to an ASP.NET MVC3 website is very easy.

Unfortunately, static sitemaps aren’t nearly as useful as ones that update automatically when new content is added.  To do that, we need a dynamically generated sitemap.

Automatically Generated Sitemaps

There are a couple of different ways you can generate a sitemap automatically.  A simple method might be to write every URL a user visits on the site into your sitemap file.  Of course, there might be some performance issues as your site grows since you would need to check for duplicates.  A bigger problem would be that you might include duplicate pages with different URLs, or restricted pages that don’t belong in the sitemap.  Finally, this method does not guarantee that the sitemap is complete.  It simply guarantees that if a user visits a page, it will be in the sitemap.  This isn’t much of an improvement over simply having a spider crawl your site for links.

MVCSitemapProvider

Now that we all agree that simply logging all the URLs manually is a bad idea, we can look at methods to programmatically generate a sitemap based on our sites content.  MVCSitemapProvider is a project that aims to provide a framework for generating the old ASP.NET style Sitemap files in ASP.NET MVC3.  This is a great place to start if you are familiar with ASP.NET .sitemap files and would prefer to continue using those.

Generating the Sitemap Directly

Your final option, if you do not want to use the MVCSitemapProvider project, is to generate the sitemap yourself.  Fortunately, with ASP.NET MVC3 it is relatively simple to do and is very straightforward. 

The Code

There are two basic ways to create a sitemap using the ASP.NET MVC3 controller model. Today I will discuss the easier of the two methods, which is simply creating a view to structure the sitemap.

A sitemap can be a simple text page with a list of links. So we can create an action and view to return a simple text page with a list of dynamically generated URLs.

Create a Controller Action

The first thing you need to do to generate a dynamic sitemap is to create the sitemap action in an appropriate controller:

public ViewResult Sitemap() {
    List<string> urls = new List<string>();
    urls.Add(this.Url.Action("Sitemap", "Home", null, Request.Url.Scheme));
    urls.Add(this.Url.Action("Sitemap", "Blog", null, Request.Url.Scheme));
 
    this.Response.ContentType = "text/plain";
    return View(urls);
}

For simplicity, I called this action “Sitemap” and placed it in the “Home” controller so I can access it directly via “http://site/Home/Sitemap”. 

There are a couple of important things to notice.  The first is that we are setting the content type to be “text/plain”.  Normally the content type would be HTML, so this is necessary since we want a simple text file.

The second thing to notice is that we are simply passing in a list of strings as our model.  There are better ways to encapsulate this data, but for now this is sufficient to generate a simple sitemap.

Finally, you probably noticed that we have not done anything that would not have been just as easy to accomplish via a static sitemap.  The only real advantage we have gathered is that we are utilizing the built-in routing tables to generate the URLs, so if we change the routing, we do not have to manually update the sitemap.

See?  Things are getting better already.

Generate Dynamic Links

Now that we have seen the basics of how to generate links, lets take a look at a more complicated example from a sample blog controller:

List<string> urls = new List<string>();
urls.Add(this.Url.Action("Index",null,null, Request.Url.Scheme));
var slugs = (from a in db.Articles select a.Slug).ToList();
int numberOfPages = (slugs.Count() / ArticlesPerPage) + 1;
for(int i=1;i<numberOfPages;i++) {
    urls.Add(this.Url.Action("Index", null, new { page = i }, Request.Url.Scheme));
}
foreach (var slug in slugs) {
    urls.Add(this.Url.Action("ShowArticle", null, new { @slug = slug }, Request.Url.Scheme));
}

As you can see, this generates links based off information in the database.  In this example, we are using a list of the blog articles to generate all the possible links for two different actions.  The “Index” action simply displays a paginated list of links to the actual blog articles.  The “ShowArticle” action displays the actual article. 

To generate the list of links, we simply query the database for all the articles, and then build the list of page links for the Index action, and the list of slugs for the ShowArticle action.

Since this is run every time the Sitemap action is called, this code can dynamically update the list of links as articles are added or removed.  This is when the real power of dynamically generating your sitemap becomes obvious.     

Create the Sitemap

The final step in creating a dynamically generated sitemap for a controller is to create the view.  Since we are only going to create the simple list of links, the view is trivial:

@model List<string>
@{
    Layout = null;
}
@foreach (var url in Model) {
    @(url + "\n")
}

That’s all there is to it.  Now when you navigate to the Sitemap action of the given controller you will get a simple list of all the links for that controller.

This method of generating the sitemap is great because it is simple and straightforward.  But there are some limitations, including the fact that every controller must either have a sitemap action or a single controller must generate the sitemap for all the controllers.  This also doesn’t address the ability to generate sitemap indexes, nor the additional parameters available in the XML sitemap protocol.