While ASP.NET Core is a robust web framework, it lacks some core features that make executing a creative-focused site more straightforward. One of those features is the ability to generate a sitemap. If folks can’t find your content, then for all intents and purposes, it doesn’t exist. Sitemaps tell search engines which parts of your site are essential for user searches.
In this post, I’ll be combining an open-source project called DotnetSitemapGenerator with some of my custom infrastructure code to generate sitemap nodes from ASP.NET Core Minimal APIs, ASP.NET Core MVC actions, and Razor Pages. The post covers all the server-side rendered paradigms within ASP.NET Core, except for Blazor. As a bonus, I also generate sitemap nodes from a database using Entity Framework Core.
What is a Sitemap?
A sitemap is a file provided by your web application that provides search engines with information about pages, videos, and other potential points of interest. Search engines use this file to crawl your site more efficiently, helping keep your search results up to date and more relevant for prospective visitors. A sitemap is typically hosted at the root of a website at /sitemap.xml
by convention but can be configured depending on your use case.
While a sitemap is optional for a well-linked site, there are advantages to having one for your application.
For instance, crawlers might have difficulty distinguishing between valuable pages if you have an extensive website. A large site can lead to issues indexing newer pages, and thus you may lose valuable visitors for more recent content.
Newer sites might also struggle to have crawlers discover their existence, as fewer or no inbound links exist. Generating a sitemap and submitting it to popular search engines ensures your site finds its way quickly into the results of users. As your site becomes more popular, this is less likely a concern, but we all start somewhere.
You may want search engines to index your rich media content separately from your written content. Having separate sitemaps can mean new avenues for users to discover your site. Sitemaps can clarify what value you are providing by creating distinctions between different types of content.
While sitemaps can be helpful for many sites, they do not guarantee all items within your XML files will be indexed by search engines. That said, there are values for change frequency and priority that can help search engines prioritize their crawlers. Generally, it’s better to be safe than sorry when there’s so much competition for visitors.
A Basic Sitemap
Sitemaps, in their simplest form, are a collection of URLs. Let’s look at the final sample we’ll generate in this post.
The XML file is presentationally unremarkable but essential to building an online presence.
The XML file has endpoints from ASP.NET Core MVC controllers, Razor Pages, and Minimal APIs. Let’s look at how we register our Sitemap infrastructure, and then we’ll go through each mechanism that provides these values to our sitemap.
Setting up the ASP.NET Core Infrastructure
We’ll start inside Program.cs
to see how all the pieces come together, then explore how we pull these nodes into our sitemap XML file.
We first start by adding our services to the ASP.NET Core services collection. The registrations include our sitemap providers, the Endpoints Api Explorer (for Minimal APIs), and the HttpContextAccessor
for generating links.
Further down the file is our sitemap.xml
endpoint, enabling output caching.
I’ve defined these methods in the project’s SitemapExtensions.cs
file.
The AddSiteMap
method registers our URL providers, which will ultimately be used by our SitemapBuilder
to combine all the nodes for our sitemap.xml
file.
Now that we have infrastructure covered let’s talk about each ASP.NET Core approach your endpoints may be implemented in.
ASP.NET Core Razor Pages and MVC Actions
Razor Pages and MVC Views are considered Actions in ASP.NET Core, so they behave very similarly within the context of sitemap generation. The only difference between the two approaches is how you generate links from each route.
Let’s look at our PagesSitemapUrlProvider
, which will support MVC and Razor Pages.
The trick in this method is utilizing the information from ActionDescriptor
to determine how we want to generate links using the LinkGenerator
instance. We also require an HttpContext
to determine values for the base URL.
Now all we need to do is decorate the endpoints we want to include in our Sitemap generation process. Here’s an example using custom attributes on our controller, where the closest attribute will be used when creating the sitemap node.
The implementation in Razor Pages is similar, but here we use the base SitemapAttribute
.
Cool! What about Minimal API endpoints?
ASP.NET Core Minimal API endpoints
While most developers use ASP.NET Core Minimal APIs for API endpoints that return JSON or XML, developers may also choose to return any content type, which may include HTML files. So you may find these endpoints straddling the line between API and presentational duties.
For completeness, let’s add an option for Minimal API endpoints.
We’ll need to annotate our API endpoints to get Minimal API endpoints into the sitemap.
In this example, we have a route value that needs to be satisfied before generating the link. Using the WithSitemap
method, we can provide a default value. The extension method also adds a name to our endpoint metadata, which allows us to generate the correct link.
We’re on a roll. What about data-driven pages?
Database-driven pages with Entity Framework Core
Many sites have dynamic pages built on a dataset. Dynamic URLs are standard on shopping sites with an extensive product catalog. Let’s see how we can implement a ProductSitemapUrlProvider
.
That’s pretty straightforward. We must point to our product detail page, a Razor page at /Products
, and get a unique URL for each product.
Conclusion
Working sample at GitHub repository for ASP.NET Core Sitemap
With some help from an OSS project, I created a robust framework for generating sitemaps in ASP.NET Core using different programming approaches. While I’m sure this approach has more room for improvement, I’m pretty happy with the final result. What do you think?
I added this project to GitHub, and I don’t intend to make it a NuGet package soon since I see developers adapting the code for each project. If you’re inspired to make it a NuGet package, you can do so; just let me know.
Thanks for reading and sharing my posts with friends and colleagues.