1

I am using the standard MVC template from MVC 2013.

There is a Home controller with actions About, Contact, etc.

There is an Account controller with actions Login, Logout, etc.

The app is deployed at domain website. The url http://website will produce the output of /Home/Index, without changing the url in the browser address box, ie what the browser shows is not the result of a Http redirect.

How do I make the url http://website/X route to /Home/X if X is not another controller in my application? Otherwise it should route to /Home/X/Index.

The reason is that I would like http://website/about, http://website/contact etc without the Home.

haim770
  • 48,394
  • 7
  • 105
  • 133
Old Geezer
  • 14,854
  • 31
  • 111
  • 198
  • 1
    http://stackoverflow.com/questions/24414960/hide-one-controller-name-from-mvc-url-show-other-controller-names http://stackoverflow.com/questions/3337372/asp-net-mvc-removing-controller-name-from-url – Vinod Apr 01 '15 at 08:50
  • Thanks. It seems that creating a route for every other controller can't be avoided? – Old Geezer Apr 01 '15 at 09:07
  • @OldGeezer, It can be avoided using the solution proposed in my answer. – haim770 Apr 01 '15 at 10:12

1 Answers1

2

A naive solution would be to simply define a new route above the default (catch-all) that looks like:

routes.MapRoute(
    name: "ShortUrlToHomeActions",
    url: "{action}",
    defaults: new { controller = "Home" }
);

The problem with this approach is that it will prevent accessing the Index (default action) of other controllers (requesting /Other, when you have OtherContoller with Index action would result in 404, requesting /Other/Index would work).

A better solution would be to create a RouteConstraint that will match our /{action} only in case there is no other controller with the same name:

public class NoConflictingControllerExists : IRouteConstraint
{
    private static readonly Dictionary<string, bool> _cache = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        var path = httpContext.Request.Path;

        if (path == "/" || String.IsNullOrEmpty(path))
            return false;

        if (_cache.ContainsKey(path))
            return _cache[path];

        IController ctrl;

        try
        {
            var ctrlFactory = ControllerBuilder.Current.GetControllerFactory();
            ctrl = ctrlFactory.CreateController(httpContext.Request.RequestContext, values["action"] as string);
        }
        catch
        {
            _cache.Add(path, true);
            return true;
        }

        var res = ctrl == null;
        _cache.Add(path, res);

        return res;
    }
}

Then applying the constraint:

routes.MapRoute(
    name: "ShortUrlToHomeActions",
    url: "{action}",
    defaults: new { controller = "Home" },
    constraints: new { noConflictingControllerExists = new NoConflictingControllerExists() }
);

See MSDN

haim770
  • 48,394
  • 7
  • 105
  • 133
  • Thanks. The route constraint thing is too complex and beyond me. No time to understand it to determine whether there are other side effects. First option suits me. For those controllers with a default "document", I can add a route for it before the ShortUrlToHomeActions one. – Old Geezer Apr 01 '15 at 13:01