2

我正在尝试构建一个附加到 MVC 区域的“租户”子域路由。在这种情况下,我有一个名为“租户”的区域,它有两个控制器;公共和管理员。如果匹配,我的自定义路由用于获取子域,然后将它们路由到正确的 Controller-Action-Area。

该项目的基础来自以下 http://www.matrichard.com/post/asp.net-mvc-5-routing-with-subdomain

我遇到的问题出在自定义子域路由中。当我点击Public/IndexRoute 时,routeData返回 null 并且我看到以下错误。虽然如果路线是/admin它返回正确的routeData

“/”应用程序中的服务器错误。

匹配的路由不包含必需的“控制器”路由值。

它似乎也总是使用 RouteDebugger 工具匹配,这是我问题的线索吗?

示例路线:

控制器=公共行动=索引,区域=租户

http://tenant1.mydomain.com:8080/

http://tenant1.mydomain.com:8080/logon

控制器=管理操作=索引,区域=租户

http://tenant1.mydomain.com:8080/admin

http://tenant1.mydomain.com:8080/admin/edit

--

子域路由P.cs

public class SubdomainRouteP : Route
{
    public string Domain { get; set; }

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

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

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

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

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        // 
        // routeData object returns null in some cases 
        // 
        var routeData = base.GetRouteData(httpContext);

        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
        }

        // Why is this NULL?
        if (routeData == null)
        {
          
            routeData = new RouteData(this, new MvcRouteHandler());
           
        }

        routeData.DataTokens["Area"] = "Tenant";
        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;
    }

}

路由配置.cs

        routes.Add("Admin_Subdomain", new SubdomainRouteP(
            "{client}.mydomain.com", //of course this should represent the real intent…like I said throwaway demo project in local IIS
            "admin/{action}/{id}",
            new { controller = "Admin", action = "Index", id = UrlParameter.Optional }));

        routes.Add("Public_Subdomain", new SubdomainRouteP(
            "{client}.mydomain.com", //of course this should represent the real intent…like I said throwaway demo project in local IIS
            "{controller}/{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 });

下面的 Url 为我提供了来自 RouteDebugger 的以下结果。在测试 1 和 2 中,路由仍然匹配 /admin。

测试 1 失败:http://tenant.mydomain.com/

失败的测试 2:http://tenant.mydomain.com/logon

成功3:http://tenant.mydomain.com/admin

匹配 URL 默认值

真的 admin/{action}/{id} controller = Admin, action = Index

真的 {controller}/{action}/{id} controller = Public, action = Index

4

1 回答 1

1

您链接到的帖子有一个错误:当约束或 URL 不匹配时,该base.GetRouteData方法将返回null. 在这种情况下,将子域名添加到路由字典中显然会抛出异常。该行之前应该有一个空保护子句。

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;
}

你的路线应该是这样。您需要确保在基类返回 null 的情况下返回 null(这表明 URL 或约束不匹配,我们需要跳过处理此路由)。

另外,我不确定它是否与直接将数据添加到 . 有什么不同DataTokens,但是 MVC 框架有一个IRouteWithArea可以实现来配置路由适用的区域。

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;
    }

}

我不知道你想用这个domain参数做什么。URL 很可能会为域返回一些内容。因此,您似乎应该在第一"{controller}/{action}/{id}"条路线中有一个约束,否则您将永远不会有一个案例会通过默认路线。或者,您可以在 URL 中使用显式段,以便区分它(与您对管理路由所做的相同)。

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 });

另一种选择是添加另一个构造函数参数以传入要检查的有效域的显式列表。

于 2016-02-17T18:39:15.307 回答