Sitemap Action Result for ASP.NET MVC
One common feature of any modern web application to improve its SEO and traffic is the use of sitemap as a standard XML derivation. I’ve been using such sitemaps on my blog for a very long time. I can remember the days when sitemap was an extension to Community Server written by the community, and the days when it became a built-in feature. I also had written a plug-in for Graffiti CMS to add sitemap to my blog.
However, for Behistun (my new ASP.NET MVC powered blog engine) I had to implement the sitemap with my own code. In ASP.NET WebForms the most common way to implement a sitemap is through an HttpHandler that handles the request to sitemap URL and generates the valid XML data of the sitemap in response.
For ASP.NET MVC and Behistun I used a different approach that replaces HttpModule and HttpHandler in many cases: Action Result. I implemented a custom action result that generates the sitemap as the output.
Looking around, I didn’t see such an implementation shared on the community even though it’s comparatively easy to implement it in ASP.NET MVC. So here I share my approach shortly hoping it helps some developers in the future.
Action result is a very nice and helpful part of ASP.NET MVC that can act as a extensibility point as well. There are various derivations of ActionResult base class available in ASP.NET MVC, and there are some interesting extensions such as the one written for RSS feed generation.
For my sitemap implementation in ASP.NET MVC I created my own SitemapActionResult class by inheriting from ActionResult base class, and used it to generate the sitemap based on the blog posts list.
First, assume that I have a Post class that represents a post with a very simple structure that I’ll use later in my example.
using System;
namespace SitemapActionResultSample.Models
{
public class Post
{
public int ID { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public DateTime DateAdded { get; set; }
public DateTime UpdatedOn { get; set; }
public string Slug { get; set; }
}
}
Now I implement the custom action result for sitemap generation as follows.
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Xml;
using SitemapActionResultSample.Models;
namespace SitemapActionResultSample.Components
{
public class SitemapActionResult : ActionResult
{
private List<Post> _posts;
public SitemapActionResult(List<Post> posts)
{
this._posts = posts;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.ContentType = "application/rss+xml";
using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.Output))
{
writer.WriteStartElement("urlset", "http://www.sitemaps.org/schemas/sitemap/0.9");
writer.WriteStartElement("url");
writer.WriteElementString("loc", "http://site.com");
writer.WriteElementString("lastmod", DateTime.Now.ToString("yyyy-MM-dd"));
writer.WriteElementString("changefreq", "daily");
writer.WriteElementString("priority", "1.0");
writer.WriteEndElement();
foreach (Post post in this._posts)
{
writer.WriteStartElement("url");
writer.WriteElementString("loc", string.Format("http://site.com/{0}", post.Slug));
writer.WriteElementString("lastmod", post.UpdatedOn.ToString("yyyy-MM-dd"));
writer.WriteElementString("changefreq", "daily");
writer.WriteElementString("priority", "0.5");
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.Flush();
writer.Close();
}
}
}
}
The code is straightforward. The SitemapActionResult gets a list of posts via its public constructor and applies some XML operations to iterate through the list of posts and generate the appropriate and valid XML output in the response.
To test this in action, I create a controller with a single action method to generate a few fake posts and pass them to the SitemapActionResult and generate the sitemap.
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using SitemapActionResultSample.Components;
using SitemapActionResultSample.Models;
namespace SitemapActionResultSample.Controllers
{
public class SitemapController : Controller
{
public SitemapActionResult Index()
{
List<Post> posts = new List<Post>();
posts.Add(new Post()
{
ID = 1,
Title = "First Post",
Body = "Body for the first post.",
Slug = "first-post",
DateAdded = DateTime.Parse("October 4, 2009 7:02 AM"),
UpdatedOn = DateTime.Parse("October 4, 2009 7:02 AM")
});
posts.Add(new Post()
{
ID = 2,
Title = "Second Post",
Body = "Body for the second post.",
Slug = "second-post",
DateAdded = DateTime.Parse("October 5, 2009 1:48 PM"),
UpdatedOn = DateTime.Parse("October 6, 2009 6:36 AM")
});
posts.Add(new Post()
{
ID = 3,
Title = "Third Post",
Body = "Body for the third post.",
Slug = "third-post",
DateAdded = DateTime.Parse("October 6, 2009 11:20 AM"),
UpdatedOn = DateTime.Parse("October 6, 2009 11:20 AM")
});
return new SitemapActionResult(posts);
}
}
}
And I add a route to my routes collection to map incoming requests to the sitemap URL to this controller.
using System.Web.Mvc;
using System.Web.Routing;
namespace SitemapActionResultSample
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Sitemap",
"sitemap",
new { controller = "Sitemap", action = "Index" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
}
}
This generates something like the below output.

The sample code for this post is available for download here.
| Attachment | Size |
|---|---|
| SitemapActionResultSample.zip | 252.69 KB |
Keyvan Nayyeri is a Ph.D. student in Computer Science and previously held a B.Sc. degree in Applied Mathematics. He was born in Kermanshah, Kurdistan, Iran in 1984, and is currently living in San Antonio, TX.
His primary research interests are Programming Languages & Compilers and Software Engineering. He’s also a software architect and developer with focus on Microsoft development technologies as well as Open Source platforms.
Keyvan is an avid community leader and contributor who has written four books for Wiley/Wrox and several articles for prominent community websites. Also he has contributed to many Open Source projects.
As a result of his long-time contributions to the community, he has received several recognitions and awards from Microsoft, its partners, and community websites.
Keyvan is a continues learner who loves to study, learn, and discover new technologies everyday, and is enthusiast for serving to the humankind through his research and contributions.
For a long time he has been blogging about various topics on his blog that has become a rich resource for software developers. His blog is available at www.nayyeri.net.
Keyvan is a DZone MVB and is not an employee of DZone and has posted 16 posts at DZone.- Login or register to post comments
- 1780 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)














