MVC: How to route /sitemap.xml to an ActionResult?
Asked Answered
L

3

17

I've got a SitemapActionResult that overrides the ActionResult, and delivers a SEO sitemap.xml when http://www.sprelle.no/Home/SiteMap is hit. So far so good.

What I would like, though, is to serve the sitemap.xml when Google visits /sitemap.xml. For that to work, I need a route that sees "sitemap.xml" and directs to /Home/Sitemap.

How do I create this mapping (in the Routes table)?

Laze answered 5/1, 2010 at 14:18 Comment(0)
K
22

Add a map for:

routes.MapRoute(
            "Sitemap",
            "sitemap.xml",
            new { controller = "Home", action = "SiteMap" }
            );

Notice that the route, controller, and action options are hard coded.

Kerman answered 5/1, 2010 at 14:22 Comment(6)
I actually tried this earlier today, but couldn't get it to work because I added the route after the Default route. It needs to be inserted before the Default route to work. Thank you.Laze
Also remember to add runAllManagedModulesForAllRequests="true" for your modules configuration inside system.webServer, otherwise it will try to use the staticfile handler and it will return a 404Illustrate
@RaulVejar Thank you! I've been searching the internet for over an hour and you are the first person to mention the modules piece. It solved my issue.Rounding
runAllManagedModulesForAllRequest will impact your performance. Maybe you can add an enry for sitemap.xml inside system.webServer.modules only: <add name="ManagedFileWithExtension" path="sitemap.xml" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />Stoned
@RaduD It should go in system.webServer.handlers, not .modules.Clerissa
I have developed utility to generate sitemap for my website mrnams.com Problem I have is, “when code generate sitemap.xml and try to save in \wwwroot\sitemap.xml,then its fails with access denied” As I am using plesk, I can’t access IIS, also I don’t want to give write permissions to all other static files. Please help.Salonika
P
9

You can used this.

Step 1. Mapping the file extension to TransferRequestHandler

IIS 7 Integrated mode uses HTTP Handler mappings which point path / verb combinations to an HTTP Handler. For example, there's a default handler mapping which points path="*.axd" verb="GET,HEAD,POST,DEBUG" to the appropriate ISAPI module for the .NET runtime version the site's running under. The easiest way to see the default handlers under IIS Express is to run a site under IIS Express, right-click the IIS Express icon in the system tray, click "show all applications", and click on a site. The applicationhost.config link at the bottom is linked, so you can just click on it and it should load in Visual Studio.

If you scroll to the bottom, you'll see that there's a catchall StaticFile mapping for path="*" verb="*" which points to StaticFileModule,DefaultDocumentModule,DirectoryListingModule. That's what will handle your .html request if you do nothing. So the first step is to add a handler in your web.config that will point *.html requests to the TransferRequestHandler. TransferRequestHandler is the handler that takes care of the extensionless URLs you're used to seeing in MVC routes, e.g. /store/details/5.

Adding a handler mapping is really easy - just open up your web.config and add it to the <system.webServer/handlers> node.

<add name="HtmlFileHandler" path="*.html" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />

Note that you can make the path more specific if you'd like. For instance, if you only wanted to intercept one specific request, you could use path="sample.html"

Step 2. Configuring the route

Next, you'll need a new route. Open up App_Start/RouteConfig.cs and it to the RegisterRoutes call. My complete RegisterRoutes looks like this:

  routes.MapRoute(
       name: "XMLPath",
       url: "sitemapindex.xml",
       defaults: new { controller = "Home", action = "Html", page = UrlParameter.Optional }
   );

Step 3. Route Existing Files

That almost covers it, but there's one more thing to take care of - overriding requests that match an existing file. If you've got an actual file called myfile.html, the routing system won't allow your route to run. I forgot about this, ended up with an HTTP 500 error (recursion overflow) and had to ask Eilon Lipton for help.

Anyways, that's easy to fix - just add routes.RouteExistingFiles = true to your route registration. My completed RegisterRoutes call looks like this:

public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.RouteExistingFiles = true;

        routes.MapRoute(
            name: "CrazyPants",
            url: "{page}.html",
            defaults: new { controller = "Home", action = "Html", page = UrlParameter.Optional }
        );

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
    }

That's it.

I tested by adding this controller action:

public FileResult Html()
{
    var stringBuilder = new StringBuilder();
    stringBuilder.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
    stringBuilder.AppendLine("<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">");
    stringBuilder.AppendLine("<sitemap>");
    stringBuilder.AppendLine("<loc>http://sprint-newhomes.move.com/sitemaps/sitemap_01.xml</loc>");
    stringBuilder.AppendLine("<lastmod>" + DateTime.Now.ToString("MMMM-dd-yyyy HH:mm:ss tt") + "</lastmod>");
    stringBuilder.AppendLine("</sitemap>");
    stringBuilder.AppendLine("<sitemap>");
    stringBuilder.AppendLine("<loc>http://sprint-newhomes.move.com/sitemaps/sitemap_02.xml</loc>");
    stringBuilder.AppendLine("<lastmod>" + DateTime.Now.ToString("MMMM-dd-yyyy HH:mm:ss tt") + "</lastmod>");
    stringBuilder.AppendLine("</sitemap>");
    stringBuilder.AppendLine("</sitemapindex>");

    var ms = new MemoryStream(Encoding.ASCII.GetBytes(stringBuilder.ToString()));



    Response.AppendHeader("Content-Disposition", "inline;filename=sitemapindex.xml");
    return new FileStreamResult(ms, "text/xml");
}
Percentage answered 19/5, 2015 at 2:5 Comment(1)
Although this answer may be correct, it would be better to include the relevant code here. Link-only answers can become incorrect if the website moves or is deleted.Byrnie
R
8

To get this to work you need to do 2 things:

  1. Instruct the IIS to allow the static file request "/sitemap.xml" to hit your controller. Otherwise the IIS will bypass your application and look directly for a file with this name. Add the following line to your web.config:
<system.webServer>
    <handlers>

        <!-- add the following line -->
        <add name="SitemapXml" path="sitemap.xml" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>

    </handlers>
</system.webServer>
  1. Place a route in your MVC application that matches this request to an ActionResult (make sure you place it before your default route):
routes.MapRoute(
    name: "Sitemap",
    url: "sitemap.xml",
    defaults: new { controller = "YourControllerName", action = "YourActionName" }
);
Retinue answered 10/12, 2017 at 12:18 Comment(1)
this answer is also very useful, but it's down at the bottomMyongmyopia

© 2022 - 2024 — McMap. All rights reserved.