ASP.NET Core 2.0 中基于域的路由
Domain-based routing in ASP.NET Core 2.0
我有一个 ASP.NET Core 2.0 应用托管在 Azure 应用服务上。
此应用程序绑定到 domainA.com
。我的应用程序中有一条路线,例如 domainA.com/route
.
现在,我想引入另一个域,但让它只响应不同的路由——例如,domainB.com
。
最好的方法是什么?
实现此目的的一种方法是制作一个 custom route constraint 来指定每个域名的路由功能。
域约束
public class DomainConstraint : IRouteConstraint
{
private readonly string[] domains;
public DomainConstraint(params string[] domains)
{
this.domains = domains ?? throw new ArgumentNullException(nameof(domains));
}
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
{
string domain =
#if DEBUG
// A domain specified as a query parameter takes precedence
// over the hostname (in debug compile only).
// This allows for testing without configuring IIS with a
// static IP or editing the local hosts file.
httpContext.Request.Query["domain"];
#else
null;
#endif
if (string.IsNullOrEmpty(domain))
domain = httpContext.Request.Host.Host;
return domains.Contains(domain);
}
}
用法
app.UseMvc(routes =>
{
routes.MapRoute(
name: "DomainA",
template: "route",
defaults: new { controller = "DomainA", action = "Route" },
constraints: new { _ = new DomainConstraint("domaina.com") });
routes.MapRoute(
name: "DomainB",
template: "route",
defaults: new { controller = "DomainB", action = "Route" },
constraints: new { _ = new DomainConstraint("domainb.com") });
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
请注意,如果您在 Visual Studio 中启动它,它将无法使用标准配置。为了在不更改配置的情况下轻松调试,您可以指定 URL 并将域作为查询字符串参数:
/route?domain=domaina.com
这只是为了让您不必重新配置 IIS 和本地主机文件即可进行调试(但如果您愿意,您仍然可以这样做)。在 Release
构建期间,此功能已被删除,因此它仅适用于生产中的 实际 域名。
由于默认情况下路由响应所有域名,因此只有在域之间共享大量功能时才有意义。如果没有,最好为每个域设置单独的区域:
routes.MapRoute(
name: "DomainA",
template: "{controller=Home}/{action=Index}/{id?}",
defaults: new { area = "DomainA" },
constraints: new { _ = new DomainConstraint("domaina.com") }
);
routes.MapRoute(
name: "DomainA",
template: "{controller=Home}/{action=Index}/{id?}",
defaults: new { area = "DomainB" },
constraints: new { _ = new DomainConstraint("domainb.com") }
);
对于 .net Core MVC,您可以创建一个新的 IRouteConstraint 和一个 RouteAttribute
=> IRouteConstraint.cs
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Microsoft.AspNetCore.Routing
{
public class ConstraintHost : IRouteConstraint
{
public string _value { get; private set; }
public ConstraintHost(string value)
{
_value = value;
}
public bool Match(HttpContext httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
string hostURL = httpContext.Request.Host.ToString();
if (hostURL == _value)
{
return true;
}
//}
return false;
//return hostURL.IndexOf(_value, StringComparison.OrdinalIgnoreCase) >= 0;
}
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
{
throw new NotImplementedException();
}
}
public class ConstraintHostRouteAttribute : RouteAttribute
{
public ConstraintHostRouteAttribute(string template, string sitePermitted)
: base(template)
{
SitePermitted = sitePermitted;
}
public RouteValueDictionary Constraints
{
get
{
var constraints = new RouteValueDictionary();
constraints.Add("host", new ConstraintHost(SitePermitted));
return constraints;
}
}
public string SitePermitted
{
get;
private set;
}
}
}
并且在您的控制器中可以这样使用它:
[ConstraintHostRoute("myroute1/xxx", "domaina.com")]
[ConstraintHostRoute("myroute2/yyy", "domainb.com")]
public async Task<ActionResult> MyController()
{
return View();
}
Built-in方法
从 ASP.NET Core 3 开始——并且在 ASP.NET Core 5 和 6 中继续支持——你可以通过使用 RequireHost()
extension method, as discussed in Allow routing to areas by hostname. (与问题标题相反,这不特定于区域。)
例子
因此,为了适应 @nightowl888's example in the accepted answer,您现在可以实现相同的结果而无需定义自定义 IRouteConstraint
:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "DomainA",
template: "route",
defaults: new { controller = "DomainA", action = "Route" }
).RequireHost("domaina.com");
routes.MapRoute(
name: "DomainB",
template: "route",
defaults: new { controller = "DomainB", action = "Route" }
).RequireHost("domainb.com");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}"
);
});
属性路由
或者,如果您更喜欢属性路由,as used in @yanga's approach, you can now use the new (but poorly documented) HostAttribute
(source code):
[Host("domainb.com")]
public DomainController: Controller
{
…
}
显然,这并没有解决 ASP.NET 核心 2 的原始问题。但是,由于这是一个未(未)记录的功能,所以我想把它留在这里,以供尝试的人使用为 ASP.NET Core 3+.
解决这个问题
我有一个 ASP.NET Core 2.0 应用托管在 Azure 应用服务上。
此应用程序绑定到 domainA.com
。我的应用程序中有一条路线,例如 domainA.com/route
.
现在,我想引入另一个域,但让它只响应不同的路由——例如,domainB.com
。
最好的方法是什么?
实现此目的的一种方法是制作一个 custom route constraint 来指定每个域名的路由功能。
域约束
public class DomainConstraint : IRouteConstraint
{
private readonly string[] domains;
public DomainConstraint(params string[] domains)
{
this.domains = domains ?? throw new ArgumentNullException(nameof(domains));
}
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
{
string domain =
#if DEBUG
// A domain specified as a query parameter takes precedence
// over the hostname (in debug compile only).
// This allows for testing without configuring IIS with a
// static IP or editing the local hosts file.
httpContext.Request.Query["domain"];
#else
null;
#endif
if (string.IsNullOrEmpty(domain))
domain = httpContext.Request.Host.Host;
return domains.Contains(domain);
}
}
用法
app.UseMvc(routes =>
{
routes.MapRoute(
name: "DomainA",
template: "route",
defaults: new { controller = "DomainA", action = "Route" },
constraints: new { _ = new DomainConstraint("domaina.com") });
routes.MapRoute(
name: "DomainB",
template: "route",
defaults: new { controller = "DomainB", action = "Route" },
constraints: new { _ = new DomainConstraint("domainb.com") });
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
请注意,如果您在 Visual Studio 中启动它,它将无法使用标准配置。为了在不更改配置的情况下轻松调试,您可以指定 URL 并将域作为查询字符串参数:
/route?domain=domaina.com
这只是为了让您不必重新配置 IIS 和本地主机文件即可进行调试(但如果您愿意,您仍然可以这样做)。在 Release
构建期间,此功能已被删除,因此它仅适用于生产中的 实际 域名。
由于默认情况下路由响应所有域名,因此只有在域之间共享大量功能时才有意义。如果没有,最好为每个域设置单独的区域:
routes.MapRoute(
name: "DomainA",
template: "{controller=Home}/{action=Index}/{id?}",
defaults: new { area = "DomainA" },
constraints: new { _ = new DomainConstraint("domaina.com") }
);
routes.MapRoute(
name: "DomainA",
template: "{controller=Home}/{action=Index}/{id?}",
defaults: new { area = "DomainB" },
constraints: new { _ = new DomainConstraint("domainb.com") }
);
对于 .net Core MVC,您可以创建一个新的 IRouteConstraint 和一个 RouteAttribute
=> IRouteConstraint.cs
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Microsoft.AspNetCore.Routing
{
public class ConstraintHost : IRouteConstraint
{
public string _value { get; private set; }
public ConstraintHost(string value)
{
_value = value;
}
public bool Match(HttpContext httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
string hostURL = httpContext.Request.Host.ToString();
if (hostURL == _value)
{
return true;
}
//}
return false;
//return hostURL.IndexOf(_value, StringComparison.OrdinalIgnoreCase) >= 0;
}
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
{
throw new NotImplementedException();
}
}
public class ConstraintHostRouteAttribute : RouteAttribute
{
public ConstraintHostRouteAttribute(string template, string sitePermitted)
: base(template)
{
SitePermitted = sitePermitted;
}
public RouteValueDictionary Constraints
{
get
{
var constraints = new RouteValueDictionary();
constraints.Add("host", new ConstraintHost(SitePermitted));
return constraints;
}
}
public string SitePermitted
{
get;
private set;
}
}
}
并且在您的控制器中可以这样使用它:
[ConstraintHostRoute("myroute1/xxx", "domaina.com")]
[ConstraintHostRoute("myroute2/yyy", "domainb.com")]
public async Task<ActionResult> MyController()
{
return View();
}
Built-in方法
从 ASP.NET Core 3 开始——并且在 ASP.NET Core 5 和 6 中继续支持——你可以通过使用 RequireHost()
extension method, as discussed in Allow routing to areas by hostname. (与问题标题相反,这不特定于区域。)
例子
因此,为了适应 @nightowl888's example in the accepted answer,您现在可以实现相同的结果而无需定义自定义 IRouteConstraint
:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "DomainA",
template: "route",
defaults: new { controller = "DomainA", action = "Route" }
).RequireHost("domaina.com");
routes.MapRoute(
name: "DomainB",
template: "route",
defaults: new { controller = "DomainB", action = "Route" }
).RequireHost("domainb.com");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}"
);
});
属性路由
或者,如果您更喜欢属性路由,as used in @yanga's approach, you can now use the new (but poorly documented) HostAttribute
(source code):
[Host("domainb.com")]
public DomainController: Controller
{
…
}
显然,这并没有解决 ASP.NET 核心 2 的原始问题。但是,由于这是一个未(未)记录的功能,所以我想把它留在这里,以供尝试的人使用为 ASP.NET Core 3+.
解决这个问题