1
votes

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.

1
Thanks. It seems that creating a route for every other controller can't be avoided?Old Geezer
@OldGeezer, It can be avoided using the solution proposed in my answer.haim770

1 Answers

2
votes

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