Insight Insight - 3 months ago 28
C# Question

Tenant area route with default route

So I have a site that has basically an 'area' per tenant. so it will show up as www.site.com/ and that will go to that groups page using an area.

Thing is I also have a default route for outside the area so you can go to www.site.com/ which will take you to the actual ~/Views/Home/Index page. However if you try to type www.site.com/Home/Index or say the page to create a new group www.site.com/Group/Create it thinks it needs to go to the area which that doesn't exist and gives the 404 resource cannot be found.

Here is the default route in the RouteConfig.cs

routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "TicketSystem.Controllers" }
);


Here is the route config for the area:

context.MapRoute(
"Group_default",
"{group}/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "TicketSystem.Areas.Group.Controllers" });


so the {group} is whatever group you are currently visiting and then it goes to the regular controller/action for that group. However for the default route it still seems to go to the area route instead no matter what.

I was thinking that there could be a fallback. So when it tries to go to the area and it can't find the correct controller/action it will check the default route next. If it still can't find anything it will give the 404 error resource cannot be found. Though I am not exactly sure how to do this.

So to make www.site.com/ to work and allow www.site.com/Home/Index to work.

Answer

The problem is, When you try to access /Home/Index The route engine does not know by "Home" , you meant the controller name or a groupName!

To solve this, you can create a custom route constraint which checks whether the group value in the request url is a valid controller name in your app. If yes, The request won't be handled by the area route registration definition.

public class GroupNameConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName,
                       RouteValueDictionary values, RouteDirection routeDirection)
    {
        var asm = Assembly.GetExecutingAssembly();

        //Get all the controller names

        var controllerTypes = (from t in asm.GetExportedTypes()
            where typeof(IController).IsAssignableFrom(t)
            select t.Name.Replace("Controller", ""));
        var groupName = values["group"];
        if (groupName != null)
        {
            if (controllerTypes.Any(x => x.Equals(groupName.ToString(),
                                                       StringComparison.OrdinalIgnoreCase)))
            {
                return false;
            }
        }
        return true;
    }
}

Register this constraint when you register your area route.

    public override void RegisterArea(AreaRegistrationContext context) 
    {
        context.MapRoute(
            "Group_default",
            "{group}/{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional },
            new { anything = new GroupNameConstraint() }


        );
    }

This should work assuming you will never have a groupName same as your controller name (Ex : Home )