11

In MVC 5.2.2 I can set Routes.AppendTrailingSlash to true so that trailing slash are appended to urls.

However I also have a robots controller which returns the content for the robots.txt.

How can I prevent a Slash from being appended to the robots.txt route and have it callable with out the trailing slash?

My Controller code:

[Route("robots.txt")]
public async Task<ActionResult> Robots()
{
  string robots = getRobotsContent();
  return Content(robots, "text/plain");
}

My Route Config looks like this:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

RouteTable.Routes.AppendTrailingSlash = true;
Zhaph - Ben Duguid
  • 26,785
  • 5
  • 80
  • 117
Boas Enkler
  • 12,264
  • 16
  • 69
  • 143
  • 1
    I have the exact same problem, except I also have it for sitemap.xml and opensearch.xml. You can see my code [here](https://github.com/RehanSaeed/ASP.NET-MVC-Boilerplate). I will add a bounty to this question. – Muhammad Rehan Saeed Apr 14 '15 at 10:38

2 Answers2

11

How about an Action Filter. I wrote this quickly and not for efficiency. I have tested it against URL's where I manually placed and leading "/" and worked like a charm.

    public class NoSlash : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
            var originalUrl = filterContext.HttpContext.Request.Url.ToString();
            var newUrl = originalUrl.TrimEnd('/');
            if (originalUrl.Length != newUrl.Length)
                filterContext.HttpContext.Response.Redirect(newUrl);
        }

    }

Try Using it this way

   [NoSlash]
   [Route("robots.txt")]
   public async Task<ActionResult> Robots()
   {
       string robots = getRobotsContent();
       return Content(robots, "text/plain");
    }
Dave Alperovich
  • 32,320
  • 8
  • 79
  • 101
2

If you navigate to a static .txt file, ASP.NET's behaviour is to return a 404 Not Found if the URL has a trailing slash.

I took @Dave Alperovich's approach (Thank You!) and return a HttpNotFoundResult instead of redirecting to the URL without the trailing slash. I think either approach is totally valid.

/// <summary>
/// Requires that a HTTP request does not contain a trailing slash. If it does, return a 404 Not Found.
/// This is useful if you are dynamically generating something which acts like it's a file on the web server.
/// E.g. /Robots.txt/ should not have a trailing slash and should be /Robots.txt.
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class NoTrailingSlashAttribute : FilterAttribute, IAuthorizationFilter
{
    /// <summary>
    /// Determines whether a request contains a trailing slash and, if it does, calls the <see cref="HandleTrailingSlashRequest"/> method.
    /// </summary>
    /// <param name="filterContext">An object that encapsulates information that is required in order to use the <see cref="System.Web.Mvc.RequireHttpsAttribute"/> attribute.</param>
    /// <exception cref="System.ArgumentNullException">The filterContext parameter is null.</exception>
    public virtual void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        string url = filterContext.HttpContext.Request.Url.ToString();
        if (url[url.Length - 1] == '/')
        {
            this.HandleTrailingSlashRequest(filterContext);
        }
    }

    /// <summary>
    /// Handles HTTP requests that have a trailing slash but are not meant to.
    /// </summary>
    /// <param name="filterContext">An object that encapsulates information that is required in order to use the <see cref="System.Web.Mvc.RequireHttpsAttribute"/> attribute.</param>
    protected virtual void HandleTrailingSlashRequest(AuthorizationContext filterContext)
    {
        filterContext.Result = new HttpNotFoundResult();
    }
}
Muhammad Rehan Saeed
  • 35,627
  • 39
  • 202
  • 311