我正在构建一个appplication,我希望为客户公司提供一个独特的网址,例如' clientcompany .app.com'或'app.com/ clientcompany '
当用户注册时,我想让他们选择他们的子域名,他们也应该能够邀请其他用户在该子域名下工作。子域/路由应该是所有用户分组的“父”。
如何使用MVC 4路由实现这样的功能?
答案 0 :(得分:3)
这可以通过创建自定义域路由来实现:
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; }
}
Global.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然后将参数'subdomain'添加到您的RouteValueDictionary中。
此外,请确保您创建通配符DNS记录。
答案 1 :(得分:2)
如果您想为每个客户公司提供他们自己的子域,例如clientcompany.app.com
,您必须在第一个用户注册时为客户公司创建DNS条目。将每个子域指向您的MVC4应用程序,但确保您的应用程序的IIS设置允许多个/通配符主机(默认情况下会发生这种情况)。
之后,您可以检查客户端在控制器Request
对象期间请求的域主机,解析域(例如从域中选择clientcompany
)并将其用作您的组。 / p>
或者,如果您希望客户公司只是URL路径(即常量域)的一部分,例如www.app.com/clientcompany/
,那么您可以创建一个路由,例如:
{company}/{controller}/{action}
然后,在您关心公司的地方,您可以在模型中添加company
参数或成员,并根据需要进行阅读。
答案 2 :(得分:1)
答案 3 :(得分:1)
老实说,这与路由无关,也与授权无关。无论您使用子域还是目录样式路径,您基本上都将“clientcompany”部分视为slug - 使用它来查找“组”。然后,您将通过该“组”上的关系验证用户/组所有权,如果不允许用户访问它,则返回403 Forbidden响应。否则,您允许视图呈现。