I have a .NET 7 web app, where I have a controller that results in a sitemap.xml
file. When I run the application locally, I get an XML file as a result with this content:
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"/>
And it looks like this:
However, when this is pushed to production (everything is hosted as a web app on Azure), the same endpoint returns nothing. It does recognize the endpoint and looks like this:
My code to generate this is shown below:
[Route("/sitemap.xml")]
public async Task SitemapXml()
{
var countries = await _countryService.GetBySpecificationAsync(new CountrySpecification()
{
Take = int.MaxValue
});
Response.ContentType = "application/xml";
using (var xml = XmlWriter.Create(Response.Body, new XmlWriterSettings { Indent = true }))
{
xml.WriteStartDocument();
xml.WriteStartElement("urlset", "http://www.sitemaps.org/schemas/sitemap/0.9");
xml.WriteEndElement();
}
}
My question: I am completely lost. At first, I thought it was because I didn't add support for static files and this is considered a static file, but I do have:
app.UseStaticFiles();
In the Program.cs
.
Any hints on where I should be starting?
I spent some time this week wanting to answer this question, and I have time now.
The main issue with your attempt is you are not returning XML results. To do so I suggest using IActionResult
interface.
Now time to create sitemap.xml
. IMO there are 2 ways to go from here, either using a library OR writing your own sitemap method.
I will start with a library. For instance, there is a very simple library (NuGet) called SimpleMvcSitemap.Core
. Install it in your project, and in your controller insert the following code:
[Route("/sitemap.xml")]
public async Task<IActionResult> SitemapXml()
{
// your await call etc
List<SitemapNode> nodes = new List<SitemapNode>
{
new SitemapNode(Url.Action("Index","Home")),
new SitemapNode(Url.Action("About","Home")),
//other nodes
};
return new SitemapProvider().CreateSitemap(new SitemapModel(nodes));
}
Btw for this test, I created an asp.net MVC .net 7 project.
I have deployed the solution to azure and it works both on local development and on azure. Here is the result:
If you do want to do it manually, you can do following
var listUrls = new List<string>
{
Url.Action("Index", "Home"),
Url.Action("About", "Home")
};
return new SitemapResult(listUrls);
And here is the implementation:
public class SitemapResult : ActionResult
{
private readonly IEnumerable<string> _urls;
public SitemapResult(IEnumerable<string> urls)
{
_urls = urls;
}
public override async Task ExecuteResultAsync(ActionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var response = context.HttpContext.Response;
response.ContentType = "application/xml; charset=utf-8";
var settings = new XmlWriterSettings() { Async = true, Encoding = Encoding.UTF8, Indent = false };
using (var writer = XmlWriter.Create(response.Body, settings))
{
WriteToXML(writer);
await writer.FlushAsync();
}
}
private void WriteToXML(XmlWriter writer)
{
writer.WriteStartDocument();
// Write the urlset.
writer.WriteStartElement("urlset", "http://www.sitemaps.org/schemas/sitemap/0.9");
// url element
foreach (var item in _urls)
{
writer.WriteStartElement("url");
// loc
writer.WriteStartElement("loc");
writer.WriteValue(item);
writer.WriteEndElement();
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
The manual way is also deployed on azure and works, but in the manual way you need to do a lot of work that is already done in a library. To be fair both above outcome is inspired form the question How to dynamically create a sitemap.xml in .NET core 2?.