【问题标题】:ASP.NET MVC 4 - Unique routes for Groups of users?ASP.NET MVC 4 - 用户组的唯一路由?
【发布时间】:2013-06-21 07:14:50
【问题描述】:

我正在构建一个应用程序,我想为客户公司提供一个独特的网址,例如“clientcompany.app.com”或“app.com/clientcompany” .

当用户注册时,我想让他们选择他们的子域,并且他们应该能够邀请其他用户在该子域下工作。子域/路由应该是所有用户分组的“父级”。

如何使用 MVC 4 路由实现类似的功能?

【问题讨论】:

  • app.com/clientcompany 子域对每个客户端有不同的视图、控制器、实现?如果是,您应该使用区域!
  • 不,我只是想为我的(组)客户提供他们自己的子域来工作。 CompanyA 的人可以访问 companya.myapp.com,CompanyB 的人可以访问 companyb.myapp.com。他们的数据将在其特定域下是私有的。

标签: asp.net-mvc asp.net-mvc-4 asp.net-mvc-routing


【解决方案1】:

这可以通过创建自定义域路由来实现:

public class DomainRoute : Route
{
    private Regex domainRegex;
    private Regex pathRegex;

    public string Domain { get; set; }

    public DomainRoute(string domain, string url, RouteValueDictionary defaults)
        : base(url, defaults, new MvcRouteHandler())
    {
        Domain = domain;
    }

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

    public DomainRoute(string domain, string url, object defaults)
        : base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
    {
        Domain = domain;
    }

    public DomainRoute(string domain, string url, object defaults, IRouteHandler routeHandler)
        : base(url, new RouteValueDictionary(defaults), routeHandler)
    {
        Domain = domain;
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        // Build regex
        domainRegex = CreateRegex(Domain);
        pathRegex = CreateRegex(Url);

        // Request information
        string requestDomain = httpContext.Request.Headers["host"];

        if (!string.IsNullOrEmpty(requestDomain))
        {
            if (System.Diagnostics.Debugger.IsAttached == false)
            {
                if (requestDomain.IndexOf(":") > 0)
                {
                    requestDomain = requestDomain.Substring(0, requestDomain.IndexOf(":"));
                }
            }

            // Strip Multiple Subdomains
            if (requestDomain.Split('.').Length > 3)
            {
                string[] split = requestDomain.Split('.');

                requestDomain = String.Join(".", split, split.Length - 3, 3);

                string url = String.Format("{0}://{1}/", httpContext.Request.Url.Scheme, requestDomain);

                if (System.Diagnostics.Debugger.IsAttached == true)
                {
                    httpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
                    httpContext.Response.CacheControl = "no-cache";
                }

                httpContext.Response.RedirectPermanent(url, true);
            }
        }
        else
        {
            requestDomain = httpContext.Request.Url.Host;
        }



        string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;

        // Match domain and route
        Match domainMatch = domainRegex.Match(requestDomain);
        Match pathMatch = pathRegex.Match(requestPath);

        // Route data
        RouteData data = null;
        if (domainMatch.Success && pathMatch.Success)
        {
            data = new RouteData(this, RouteHandler);

            // Add defaults first
            if (Defaults != null)
            {
                foreach (KeyValuePair<string, object> item in Defaults)
                {
                    data.Values[item.Key] = item.Value;
                }
            }

            // Iterate matching domain groups
            for (int i = 1; i < domainMatch.Groups.Count; i++)
            {
                Group group = domainMatch.Groups[i];
                if (group.Success)
                {
                    string key = domainRegex.GroupNameFromNumber(i);

                    if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
                    {
                        if (!string.IsNullOrEmpty(group.Value))
                        {
                            data.Values[key] = group.Value;
                        }
                    }
                }
            }

            // Iterate matching path groups
            for (int i = 1; i < pathMatch.Groups.Count; i++)
            {
                Group group = pathMatch.Groups[i];
                if (group.Success)
                {
                    string key = pathRegex.GroupNameFromNumber(i);

                    if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
                    {
                        if (!string.IsNullOrEmpty(group.Value))
                        {
                            data.Values[key] = group.Value;
                        }
                    }
                }
            }
        }

        return data;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        return base.GetVirtualPath(requestContext, RemoveDomainTokens(values));
    }

    public DomainData GetDomainData(RequestContext requestContext, RouteValueDictionary values)
    {
        // Build hostname
        string hostname = values.Aggregate(Domain, (current, pair) => current.Replace("{" + pair.Key + "}", pair.Value.ToString()));

        // Return domain data
        return new DomainData
        {
            Protocol = "http",
            HostName = hostname,
            Fragment = ""
        };
    }

    private Regex CreateRegex(string source)
    {
        // Perform replacements
        source = source.Replace("/", @"\/?");
        source = source.Replace(".", @"\.?");
        source = source.Replace("-", @"\-?");
        source = source.Replace("{", @"(?<");
        source = source.Replace("}", @">([a-zA-Z0-9_]*))");

        return new Regex("^" + source + "$");
    }

    private RouteValueDictionary RemoveDomainTokens(RouteValueDictionary values)
    {
        Regex tokenRegex = new Regex(@"({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?");
        Match tokenMatch = tokenRegex.Match(Domain);
        for (int i = 0; i < tokenMatch.Groups.Count; i++)
        {
            Group group = tokenMatch.Groups[i];
            if (group.Success)
            {
                string key = group.Value.Replace("{", "").Replace("}", "");
                if (values.ContainsKey(key))
                    values.Remove(key);
            }
        }

        return values;
    }
}     

public class DomainData
{
    public string Protocol { get; set; }
    public string HostName { get; set; }
    public string Fragment { get; set; }
}

全球.asax:

routes.Add(
                "DomainRoute", new DomainRoute(
                "{subdomain}.yoururl.com",     // Domain with parameters 
                "{controller}/{action}",    // URL with parameters 
                new { controller = "Home", action = "Index", subdomain = UrlParameter.Optional }  // Parameter defaults 
            ));

http://subdomain.app.com 然后将参数“子域”添加到您的 RouteValueDictionary。

另外,请确保创建通配符 DNS 记录。

【讨论】:

    【解决方案2】:

    如果您想为每个客户公司提供自己的子域,例如 clientcompany.app.com,则必须在第一个用户注册时为客户公司创建 DNS 条目。将每个子域指向您的 MVC4 应用程序,但请确保您的应用程序的 IIS 设置允许多个/通配符主机(默认情况下会发生这种情况)。

    之后,您可以检查客户端在控制器 Request 对象期间请求的域主机,解析域(例如,从域中选择 clientcompany)并将其用作您的组。

    或者,如果您希望客户公司只是 URL 路径的一部分(即常量域),例如 www.app.com/clientcompany/,那么您可以创建如下路由:

    {company}/{controller}/{action}
    

    然后在你关心公司的地方,你可以在你的模型中添加一个company参数或成员,并根据需要读取它。

    【讨论】:

    • 谢谢!我想我会使用 URL Path 选项,这样我就不必为通配符主机而烦恼,只需让我的代码处理它。
    【解决方案3】:

    您将需要一个自定义 IRouteConstraint 来处理子域行为。这里有一篇文章完全按照您想要的方式涵盖了这一点!

    看这里MVC 3 Subdomain Routing

    希望对您有所帮助!

    【讨论】:

      【解决方案4】:

      老实说,这与路由无关,与授权无关。无论您使用子域还是目录样式的路径,您本质上都将“clientcompany”部分视为一个 slug——使用它来查找一个“组”。然后,您将通过该“组”上的关系验证用户/组的所有权,如果不允许用户访问它,您将返回 403 Forbidden 响应。否则,您允许视图呈现。

      【讨论】:

      • 谢谢,您的回答对我很有帮助。这正是我想要做的。
      猜你喜欢
      • 2012-12-13
      • 2017-03-25
      • 2014-05-25
      • 2013-02-14
      • 1970-01-01
      • 1970-01-01
      • 2013-02-23
      • 2015-06-22
      • 2013-09-03
      相关资源
      最近更新 更多