MVC Custom Routing Subdomain

老子叫甜甜 提交于 2019-11-29 15:58:24

The post that you linked to has a bug: When a constraint or the URL does not match, the base.GetRouteData method will return null. In this case, adding the subdomain name to the route dictionary will obviously throw an exception. There should be a null guard clause before that line.

public override RouteData GetRouteData(HttpContextBase httpContext)
{
    var routeData = base.GetRouteData(httpContext);
    if (routeData != null)
    {
        routeData.Values.Add("client", httpContext.Request.Url.Host.Split('.').First());
    }
    return routeData;
}

As should be the case with your route. You need to ensure you return null in the case where the base class returns null (which indicates either the URL or a constraint did not match, and we need to skip processing this route).

Also, I am not sure if it makes any difference than adding the data directly to the DataTokens, but the MVC framework has an IRouteWithArea that can be implemented to configure the Area the route applies to.

public class SubdomainRouteP : Route, IRouteWithArea
{
    public string Area { get; private set; }

    public SubdomainRouteP(string area, string url, RouteValueDictionary defaults): this(area, url, defaults, new MvcRouteHandler())
    {
    }

    public SubdomainRouteP(string area, string url, object defaults): this(area, url, new RouteValueDictionary(defaults), new MvcRouteHandler())
    {
    }

    public SubdomainRouteP(string area, string url, object defaults, IRouteHandler routeHandler): this(area, url, new RouteValueDictionary(defaults), routeHandler)
    {
    }

    public SubdomainRouteP(string area, string url, RouteValueDictionary defaults, IRouteHandler routeHandler): base(url, defaults, routeHandler)
    {
        this.Area = area;
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var routeData = base.GetRouteData(httpContext);

        // This will ignore anything where the URL or a constraint doesn't match
        // in the call to base.GetRouteData().
        if (routeData != null)
        {
            var subdomain = httpContext.Request.Url.Host.Split('.').First();

            string[] blacklist = { "www", "mydomain", "localhost" };

            // This will ignore anything that is not a client tenant prefix
            if (blacklist.Contains(subdomain))
            {
                return null; // Continue to the next route
            }

            routeData.DataTokens["UseNamespaceFallback"] = bool.FalseString;
            routeData.Values.Add("subdomain", subdomain);
        }

        // IMPORTANT: Always return null if there is no match.
        // This tells .NET routing to check the next route that is registered.
        return routeData;
    }

}

I can't figure out what you are trying to do with the domain parameter. The URL will most likely return something for domain. So, it seems like you should have a constraint in the first "{controller}/{action}/{id}" route or you will never have a case that will pass through to the default route. Or, you could use an explicit segment in the URL so you can differentiate it (the same way you did with your admin route).

routes.Add("Admin_Subdomain", new SubdomainRouteP(
    "Tenant",
    "admin/{action}/{id}",
    new { controller = "Admin", action = "Index", id = UrlParameter.Optional }));

routes.Add("Public_Subdomain", new SubdomainRouteP(
    "Tenant",
    "public/{action}/{id}",
    new { controller = "Public", action = "Index", id = UrlParameter.Optional }));

// This is the MVC default Route
routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional });

Another option would be to add another constructor parameter to pass in an explicit list of valid domains to check against.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!