是否可以有一个使用子域信息来确定其路由的 ASP.NET MVC 路由?例如:
- user1 .domain.com去一个地方
- user2 .domain.com转到另一个?
username
或者,我可以让它们都使用参数进入同一个控制器/动作吗?
是否可以有一个使用子域信息来确定其路由的 ASP.NET MVC 路由?例如:
username
或者,我可以让它们都使用参数进入同一个控制器/动作吗?
您可以通过创建新路由并将其添加到 global.asax 中 RegisterRoutes 中的路由集合来实现。下面是一个非常简单的自定义路由示例:
public class ExampleRoute : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var url = httpContext.Request.Headers["HOST"];
var index = url.IndexOf(".");
if (index < 0)
return null;
var subDomain = url.Substring(0, index);
if (subDomain == "user1")
{
var routeData = new RouteData(this, new MvcRouteHandler());
routeData.Values.Add("controller", "User1"); //Goes to the User1Controller class
routeData.Values.Add("action", "Index"); //Goes to the Index action on the User1Controller
return routeData;
}
if (subDomain == "user2")
{
var routeData = new RouteData(this, new MvcRouteHandler());
routeData.Values.Add("controller", "User2"); //Goes to the User2Controller class
routeData.Values.Add("action", "Index"); //Goes to the Index action on the User2Controller
return routeData;
}
return null;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
//Implement your formating Url formating here
return null;
}
}
要在保留标准 MVC5 路由功能的同时捕获子域,请使用以下SubdomainRoute
派生自Route
.
此外,SubdomainRoute
允许选择将子域指定为查询参数、制作sub.example.com/foo/bar
和example.com/foo/bar?subdomain=sub
等效项。这允许您在配置 DNS 子域之前进行测试。Url.Action
查询参数(使用时)通过由等生成的新链接传播。
查询参数还可以使用 Visual Studio 2013 进行本地调试,而无需使用 netsh 进行配置或以管理员身份运行。默认情况下,IIS Express 仅在非提升时绑定到localhost ;它不会绑定到sub.localtest.me等同义主机名。
class SubdomainRoute : Route
{
public SubdomainRoute(string url) : base(url, new MvcRouteHandler()) {}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var routeData = base.GetRouteData(httpContext);
if (routeData == null) return null; // Only look at the subdomain if this route matches in the first place.
string subdomain = httpContext.Request.Params["subdomain"]; // A subdomain specified as a query parameter takes precedence over the hostname.
if (subdomain == null) {
string host = httpContext.Request.Headers["Host"];
int index = host.IndexOf('.');
if (index >= 0)
subdomain = host.Substring(0, index);
}
if (subdomain != null)
routeData.Values["subdomain"] = subdomain;
return routeData;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
object subdomainParam = requestContext.HttpContext.Request.Params["subdomain"];
if (subdomainParam != null)
values["subdomain"] = subdomainParam;
return base.GetVirtualPath(requestContext, values);
}
}
MapSubdomainRoute
为方便起见,从您的方法中调用以下方法,RegisterRoutes
就像您使用普通的 old 一样MapRoute
:
static void MapSubdomainRoute(this RouteCollection routes, string name, string url, object defaults = null, object constraints = null)
{
routes.Add(name, new SubdomainRoute(url) {
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints),
DataTokens = new RouteValueDictionary()
});
}
最后,为了方便地访问子域(从真正的子域或查询参数),创建具有此Subdomain
属性的 Controller 基类会很有帮助:
protected string Subdomain
{
get { return (string)Request.RequestContext.RouteData.Values["subdomain"]; }
}
这不是我的工作,但我不得不在这个答案中添加它。
这是一个很好的解决这个问题的方法。Maartin Balliauw 编写了创建 DomainRoute 类的代码,该类的使用与普通路由非常相似。
http://blog.maartenballiauw.be/post/2009/05/20/ASPNET-MVC-Domain-Routing.aspx
样品使用将是这样的......
routes.Add("DomainRoute", new DomainRoute(
"{customer}.example.com", // Domain with parameters
"{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
))
;
要在使用Web API时捕获子域,请覆盖 Action Selector 以注入subdomain
查询参数。然后在控制器的操作中使用子域查询参数,如下所示:
public string Get(string id, string subdomain)
这种方法使调试方便,因为您可以在使用localhost而不是实际主机名时手动指定查询参数(有关详细信息,请参阅标准 MVC5 路由答案)。这是动作选择器的代码:
class SubdomainActionSelector : IHttpActionSelector
{
private readonly IHttpActionSelector defaultSelector;
public SubdomainActionSelector(IHttpActionSelector defaultSelector)
{
this.defaultSelector = defaultSelector;
}
public ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor)
{
return defaultSelector.GetActionMapping(controllerDescriptor);
}
public HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
var routeValues = controllerContext.Request.GetRouteData().Values;
if (!routeValues.ContainsKey("subdomain")) {
string host = controllerContext.Request.Headers.Host;
int index = host.IndexOf('.');
if (index >= 0)
controllerContext.Request.GetRouteData().Values.Add("subdomain", host.Substring(0, index));
}
return defaultSelector.SelectAction(controllerContext);
}
}
通过将其添加到以下内容来替换默认的操作选择器WebApiConfig.Register
:
config.Services.Replace(typeof(IHttpActionSelector), new SubdomainActionSelector(config.Services.GetActionSelector()));
是的,但您必须创建自己的路由处理程序。
通常,路由不知道域,因为应用程序可以部署到任何域,并且路由不会以一种或另一种方式关心。但是在您的情况下,您希望控制器和操作基于域,因此您必须创建一个知道域的自定义路由。
我为子域路由创建了库,您可以创建这样的路由。它目前适用于 .NET Core 1.1 和 .NET Framework 4.6.1,但将在不久的将来进行更新。它是这样工作的:
1) 在 Startup.cs 中映射子域路由
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var hostnames = new[] { "localhost:54575" };
app.UseMvc(routes =>
{
routes.MapSubdomainRoute(
hostnames,
"SubdomainRoute",
"{username}",
"{controller}/{action}",
new { controller = "Home", action = "Index" });
)};
2) 控制器/HomeController.cs
public IActionResult Index(string username)
{
//code
}
3) 该库还将允许您生成 URL 和表单。代码:
@Html.ActionLink("User home", "Index", "Home" new { username = "user1" }, null)
将生成<a href="http://user1.localhost:54575/Home/Index">User home</a>
生成的 URL 还将取决于当前主机位置和架构。
您还可以将 html 助手用于BeginForm
和UrlHelper
。如果您愿意,您还可以使用称为标签助手 ( FormTagHelper
, AnchorTagHelper
)的新功能,
该库还没有任何文档,但是有一些测试和示例项目,因此请随意探索。
在ASP.NET Core中,主机可通过Request.Host.Host
. 如果您想允许通过查询参数覆盖主机,请首先检查Request.Query
.
要使主机查询参数传播到新的基于路由的 URL,请将此代码添加到app.UseMvc
路由配置:
routes.Routes.Add(new HostPropagationRouter(routes.DefaultHandler));
并像这样定义HostPropagationRouter
:
/// <summary>
/// A router that propagates the request's "host" query parameter to the response.
/// </summary>
class HostPropagationRouter : IRouter
{
readonly IRouter router;
public HostPropagationRouter(IRouter router)
{
this.router = router;
}
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
if (context.HttpContext.Request.Query.TryGetValue("host", out var host))
context.Values["host"] = host;
return router.GetVirtualPath(context);
}
public Task RouteAsync(RouteContext context) => router.RouteAsync(context);
}
在定义了一个新的 Route 处理程序来查看在 URL 中传递的主机之后,您可以采用一个基本控制器的想法,该控制器知道它正在被访问的站点。它看起来像这样:
public abstract class SiteController : Controller {
ISiteProvider _siteProvider;
public SiteController() {
_siteProvider = new SiteProvider();
}
public SiteController(ISiteProvider siteProvider) {
_siteProvider = siteProvider;
}
protected override void Initialize(RequestContext requestContext) {
string[] host = requestContext.HttpContext.Request.Headers["Host"].Split(':');
_siteProvider.Initialise(host[0]);
base.Initialize(requestContext);
}
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
ViewData["Site"] = Site;
base.OnActionExecuting(filterContext);
}
public Site Site {
get {
return _siteProvider.GetCurrentSite();
}
}
}
ISiteProvider
是一个简单的界面:
public interface ISiteProvider {
void Initialise(string host);
Site GetCurrentSite();
}
我推荐你去 卢克桑普森博客
如果您正在考虑为您的项目提供 MultiTenancy 功能,每个租户具有不同的域/子域,您应该看看 SaasKit:
https://github.com/saaskit/saaskit
代码示例可以在这里看到:http: //benfoster.io/blog/saaskit-multi-tenancy-made-easy
使用 ASP.NET 核心的一些示例:http: //andrewlock.net/forking-the-pipeline-adding-tenant-specific-files-with-saaskit-in-asp-net-core/
编辑:如果您不想在 ASP.NET 核心项目中使用 SaasKit,您可以查看 Maarten 为 MVC6 实现的域路由:https ://blog.maartenballiauw.be/post/2015/02/17/domain -routing-and-resolving-current-tenant-with-aspnet-mvc-6-aspnet-5.html
然而,这些要点没有得到维护,需要进行调整才能与最新版本的 ASP.NET 核心一起使用。
直接链接代码:https ://gist.github.com/maartenba/77ca6f9cfef50efa96ec#file-domaintemplateroutebuilderextensions-cs
几个月前,我开发了一个将方法或控制器限制在特定域中的属性。
它很容易使用:
[IsDomain("localhost","example.com","www.example.com","*.t1.example.com")]
[HttpGet("RestrictedByHost")]
public IActionResult Test(){}
您也可以直接在控制器上应用它。
public class IsDomainAttribute : Attribute, Microsoft.AspNetCore.Mvc.Filters.IAuthorizationFilter
{
public IsDomainAttribute(params string[] domains)
{
Domains = domains;
}
public string[] Domains { get; }
public void OnAuthorization(AuthorizationFilterContext context)
{
var host = context.HttpContext.Request.Host.Host;
if (Domains.Contains(host))
return;
if (Domains.Any(d => d.EndsWith("*"))
&& Domains.Any(d => host.StartsWith(d.Substring(0, d.Length - 1))))
return;
if (Domains.Any(d => d.StartsWith("*"))
&& Domains.Any(d => host.EndsWith(d.Substring(1))))
return;
context.Result = new Microsoft.AspNetCore.Mvc.NotFoundResult();//.ChallengeResult
}
}
限制:您可能无法在具有不同过滤器的不同方法上拥有两条相同的路由我的意思是以下可能会为重复路由引发异常:
[IsDomain("test1.example.com")]
[HttpGet("/Test")]
public IActionResult Test1(){}
[IsDomain("test2.example.com")]
[HttpGet("/Test")]
public IActionResult Test2(){}