È ansible creare un percorso MVC ASP.NET basato su un sottodominio?

È ansible avere una route MVC ASP.NET che utilizza le informazioni sul sottodominio per determinarne il percorso? Per esempio:

  • user1 .domain.com va in un posto
  • user2 .domain.com va a un altro?

Oppure posso fare in modo che entrambi entrino nello stesso controller / azione con un parametro username ?

Puoi farlo creando un nuovo percorso e aggiungendolo alla raccolta rotte in RegisterRoutes nel tuo global.asax. Di seguito è riportato un esempio molto semplice di un percorso personalizzato:

 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; } } 

Per acquisire il sottodominio mantenendo le caratteristiche di routing MVC5 standard , utilizzare la seguente class SubdomainRoute derivata da Route .

Inoltre, SubdomainRoute consente al sottodominio di essere specificato come parametro di query , rendendo sub.example.com/foo/bar e example.com/foo/bar?subdomain=sub equivalenti. Ciò consente di eseguire il test prima che i sottodomini DNS siano configurati. Il parametro di query (quando in uso) viene propagato tramite nuovi collegamenti generati da Url.Action , ecc.

Il parametro query consente anche il debugging locale con Visual Studio 2013 senza dover configurare netsh o eseguire come amministratore . Per impostazione predefinita, IIS Express si collega solo a localhost quando non elevato; non si associa a nomi di host sinonimi come 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); } } 

Per comodità, chiama il seguente metodo MapSubdomainRoute dal tuo metodo RegisterRoutes proprio come faresti semplicemente con MapRoute vecchio:

 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() }); } 

Infine, per accedere comodamente al sottodominio (da un vero sottodominio o un parametro di query), è utile creare una class di base Controller con questa proprietà Subdomain :

 protected string Subdomain { get { return (string)Request.RequestContext.RouteData.Values["subdomain"]; } } 

Questo non è il mio lavoro, ma ho dovuto aggiungerlo a questa risposta.

Ecco un’ottima soluzione a questo problema. Maartin Balliauw ha scritto codice che crea una class DomainRoute che può essere utilizzata in modo molto simile al normale routing.

http://blog.maartenballiauw.be/post/2009/05/20/ASPNET-MVC-Domain-Routing.aspx

L’uso del campione sarebbe come questo …

 routes.Add("DomainRoute", new DomainRoute( "{customer}.example.com", // Domain with parameters "{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "" } // Parameter defaults )) 

;

Per acquisire il sottodominio quando si utilizza l’ API Web , eseguire l’override del selettore di azioni per iniettare un parametro di query del subdomain . Quindi utilizza il parametro di query del sottodominio nelle azioni dei tuoi controller in questo modo:

 public string Get(string id, string subdomain) 

Questo approccio rende conveniente il debug poiché è ansible specificare manualmente il parametro di query quando si utilizza localhost anziché il nome host effettivo (vedere la risposta di routing MVC5 standard per i dettagli). Questo è il codice per Action Selector:

 class SubdomainActionSelector : IHttpActionSelector { private readonly IHttpActionSelector defaultSelector; public SubdomainActionSelector(IHttpActionSelector defaultSelector) { this.defaultSelector = defaultSelector; } public ILookup 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); } } 

Sostituisci il selettore azioni predefinito aggiungendolo a WebApiConfig.Register :

 config.Services.Replace(typeof(IHttpActionSelector), new SubdomainActionSelector(config.Services.GetActionSelector())); 

Sì, ma devi creare il tuo gestore di percorsi.

In genere, il percorso non è a conoscenza del dominio perché l’applicazione può essere distribuita su qualsiasi dominio e il percorso non gli interesserebbe in un modo o nell’altro. Ma nel tuo caso vuoi basare il controller e l’azione fuori dal dominio, quindi dovrai creare un percorso personalizzato che sia a conoscenza del dominio.

Ho creato una libreria per il routing sottodominio che è ansible creare tale percorso. Funziona attualmente per .NET Core 1.1 e .NET Framework 4.6.1 ma verrà aggiornato in un prossimo futuro. Ecco come funziona:
1) Mappare il percorso sottodominio in 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) Controller / HomeController.cs

 public IActionResult Index(string username) { //code } 

3) Quella lib ti permetterà anche di generare URL e moduli. Codice:

 @Html.ActionLink("User home", "Index", "Home" new { username = "user1" }, null) 

Genera User home L’URL generato dipenderà anche dalla posizione e dallo schema dell’host corrente.
Puoi anche utilizzare gli helper html per BeginForm e UrlHelper . Se lo desideri, puoi anche utilizzare una nuova funzione chiamata helper dei tag ( FormTagHelper , AnchorTagHelper )
Quella lib non ha ancora alcuna documentazione, ma ci sono alcuni test e progetti di campionamento quindi sentitevi liberi di esplorarlo.

In ASP.NET Core , l’host è disponibile tramite Request.Host.Host . Se si desidera consentire l’override dell’host tramite un parametro di query, controllare prima Request.Query .

Per fare in modo che un parametro di query host si propaghino in nuovi URL basati su route, aggiungi questo codice alla configurazione della route app.UseMvc :

 routes.Routes.Add(new HostPropagationRouter(routes.DefaultHandler)); 

E definisci HostPropagationRouter questo modo:

 ///  /// A router that propagates the request's "host" query parameter to the response. ///  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); } 

Dopo aver definito un nuovo gestore di instradamento che avrebbe esaminato l’host passato nell’URL , si può andare con l’idea di un controller di base che sia a conoscenza del sito per cui è stato effettuato l’accesso. Sembra questo:

 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 è un’interfaccia semplice:

 public interface ISiteProvider { void Initialise(string host); Site GetCurrentSite(); } 

Ti rimando al blog di Luca Sampson

Se stai cercando di dare funzionalità MultiTenancy al tuo progetto con domini / sottodomini diversi per ogni inquilino, dovresti dare un’occhiata a SaasKit:

https://github.com/saaskit/saaskit

Esempi di codice possono essere visti qui: http://benfoster.io/blog/saaskit-multi-tenancy-made-easy

Alcuni esempi che utilizzano il nucleo di ASP.NET: http://andrewlock.net/forking-the-pipeline-adding-tenant-specific-files-with-saaskit-in-asp-net-core/

MODIFICA: se non si desidera utilizzare SaasKit nel progetto principale ASP.NET, è ansible dare un’occhiata all’implementazione del routing del dominio per MVC6 di Maarten: https://blog.maartenballiauw.be/post/2015/02/17/domain -routing-e-risolvere-corrente-tenant-con-ASPNET-MVC-6-ASPNET-5.html

Tuttavia, questi Gists non vengono mantenuti e devono essere ottimizzati per funzionare con l’ultima versione del nucleo di ASP.NET.

Link diretto al codice: https://gist.github.com/maartenba/77ca6f9cfef50efa96ec#file-domaintemplateroutebuilderextensions-cs