是否有可能使一个基于子域的ASP.NET MVC路线?
是否有可能有一个ASP.NET MVC路线,使用子域信息来确定其路线? 例如:
- user1 .domain.com去一个地方
- user2 .domain.com转到另一个?
或者,我可以做到这一点,所有这些都与username
参数相同的控制器/操作?
你可以通过创build一个新的路由并将其添加到你的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路由function的同时捕获子域 ,请使用从Route
派生的以下SubdomainRoute
类。
此外, SubdomainRoute
允许可选地将子域指定为查询参数 ,使sub.example.com/foo/bar
和example.com/foo/bar?subdomain=sub
等价。 这使您可以在DNS子域configuration之前进行testing。 查询参数(在使用时)通过由Url.Action
等生成的新链接传播
查询参数还可以使用Visual Studio 2013进行本地debugging,而无需使用netsh进行configuration或以pipe理员身份运行 。 默认情况下,IIS Express仅在未提升时绑定到本地主机 ; 它不会绑定到像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); } }
为了方便起见,您可以像使用普通的MapRoute
一样从RegisterRoutes
方法调用以下MapSubdomainRoute
方法:
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() }); }
最后,为了方便地访问子域(从一个真实的子域或一个查询参数),创build一个具有这个Subdomain
属性的控制器基类是有帮助的:
protected string Subdomain { get { return (string)Request.RequestContext.RouteData.Values["subdomain"]; } }
这不是我的工作,但我不得不把它添加到这个答案。
这是一个很好的解决这个问题的方法。 Maartin Balliauw写了创build一个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时捕获子域,请重写“动作select器”以注入subdomain
查询参数。 然后在控制器的操作中使用子域查询参数,如下所示:
public string Get(string id, string subdomain)
这种方法使得debugging变得很方便,因为当使用localhost而不是实际的主机名(请参阅标准的MVC5路由答案了解详情)时,您可以手动指定查询参数。 这是Action Selector的代码:
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
来replace默认的动作select器:
config.Services.Replace(typeof(IHttpActionSelector), new SubdomainActionSelector(config.Services.GetActionSelector()));
是的,但你必须创build自己的路由处理程序。
通常情况下,路由不知道域,因为应用程序可以部署到任何域,路由不会在意这种或那种方式。 但在你的情况下,你想基于控制器和行动的领域,所以你将不得不创build一个自定义路由,知道该域名。
在ASP.NET Core中 ,主机通过Request.Host.Host
。 如果要允许通过查询参数覆盖主机,请首先检查Request.Query
。
要使主机查询参数传播到新的基于路由的URL,请将此代码添加到app.UseMvc
路由configuration中:
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); }
我创build了子域路由的库 ,您可以创build这样的路线。 它目前正在为.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)该lib还将允许您生成URL和表单。 码:
@Html.ActionLink("User home", "Index", "Home" new { username = "user1" }, null)
将生成<a href="http://user1.localhost:54575/Home/Index">User home</a>
生成的URL也将取决于当前的主机位置和架构。
您也可以使用BeginForm
和UrlHelper
html助手。 如果你喜欢,你也可以使用称为标签助手( FormTagHelper
, AnchorTagHelper
)的新function,
那个lib还没有任何文档,但是有一些testing和示例项目,所以可以随意探索它。
在定义了一个新的Route处理程序来查看在URL中传递的主机之后 ,可以使用基本Controller的想法来知道它正在被访问的Site。 它看起来像这样:
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(); }
我指你去卢克·桑普森博客
如果您正在考虑为您的项目提供多租户function,并为每个租户提供不同的域/子域名,则应该查看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-和分辨电stream租户与- ASPNET-MVC -6- ASPNET-5.html
但是这些Gist没有被维护,需要调整以使用最新版本的ASP.NET核心。
直接链接到代码: https : //gist.github.com/maartenba/77ca6f9cfef50efa96ec#file-domaintemplateroutebuilderextensions-cs