Sitemaps in ASP.Net MVC: Icing on the Cake

This is short simple and sweet (forgive the pun).  The reason why i say that is that you have two options when doing a sitemap in MVC (actually, you have more, but whatever).

The first is using a Library. There’s a MVC Sitemap provider on Codeplex that you can download and install. It involves some XML configuration and attributes on all the actions you want to include in your sitemap.

The fact is, I don’t have time to fiddle around with configurations. I just want a simple sitemap file with a handful of products, categories and one or two other links. If the site was larger and more complex I might consider it.

So, we come to the second, DIY way. Now, this is not entirely my idea. I just repurposed it to pull the correct URL parameters out of the db. The original code is found on Codeplex.

Firstly, we have to register a new route to www.example.com/sitemap.xml. Go to Global.asax and put the following in your RegisterRoutes() method. I put mine after the call to IgnoreRoute.

[sourcecode language="csharp"] routes.MapRoute("Sitemap", "sitemap.xml", new { controller = "Home", action = "Sitemap", id = UrlParameter.Optional });
[/sourcecode]

Now, you can use any default routing you want with this. As you can see above, the route is pointing to the Sitemap action of the Home controller.

Then we have to actually populate our Action with some code.

[sourcecode language="csharp"] protected string GetUrl(object routeValues) { RouteValueDictionary values = new RouteValueDictionary(routeValues); RequestContext context = new RequestContext(HttpContext, RouteData);

        string url = RouteTable.Routes.GetVirtualPath(context, values).VirtualPath;

        return new Uri(Request.Url, url).AbsoluteUri;
    }
    [OutputCache (Duration=3600)]
    public ContentResult Sitemap()
    {
        var categories = storeDB.Categories.Include("Products").Where(g => g.Id != 8); //some filtering of categories
        XNamespace xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
        XElement root = new XElement(xmlns + "urlset");

        List<string> urlList = new List<string>();
        urlList.Add(GetUrl(new { controller = "Home", action = "Index" }));
        urlList.Add(GetUrl(new { controller = "Home", action = "Terms" }));
        urlList.Add(GetUrl(new { controller = "Home", action = "ShippingFAQ" }));
        urlList.Add(GetUrl(new { controller = "Home", action = "Testimonials" }));
        foreach (var item in categories)
        {
            urlList.Add(string.Format("{0}?{1}={2}",GetUrl(new { controller = "Store", action = "BrowseProducts"}),"category",item.Name));

            foreach (var product in item.Products)
            {
                urlList.Add(string.Format("{0}/{1}", GetUrl(new { controller = "Store", action = "ProductDetails" }), product.Id));
            }
        }

        foreach (var item in urlList)
        {
            root.Add(
            new XElement("url", 
            new XElement("loc", item), 
            new XElement("changefreq", "daily")));
        }

        using (MemoryStream ms = new MemoryStream())
        {
            using (StreamWriter writer = new StreamWriter(ms, Encoding.UTF8))
            {
                root.Save(writer);
            }

            return Content(Encoding.UTF8.GetString(ms.ToArray()), "text/xml", Encoding.UTF8);
        }
    }

[/sourcecode]

Essentially, we’re just outputting an xml file with the correct format and structure.  This gives us a file that looks like:

[sourcecode language="xml"] <?xml version="1.0" encoding="utf-8"?>

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">;

<url xmlns="">

&lt;loc&gt;http://localhost:26641/&lt;/loc&gt;

&lt;changefreq&gt;daily&lt;/changefreq&gt;

</url>

<url xmlns="">

&lt;loc&gt;http://localhost:26641/Home/Terms&lt;/loc&gt;

&lt;changefreq&gt;daily&lt;/changefreq&gt;

</url>

<url xmlns="">

&lt;loc&gt;http://localhost:26641/Home/ShippingFAQ&lt;/loc&gt;

&lt;changefreq&gt;daily&lt;/changefreq&gt;

</url>

[/sourcecode]

You get the idea.

The above code is using Entity Framework 4.1, so you can replace the line that declares  “var categories” with whatever data source you have. And you’ll have to reformat the url strings to conform to your parameter format.

Now I’m not suggesting this for any large MVC deployment. The code could get rather messy.

 

But for something simple, it works like a dream.