Domanda

È possibile avere una route ASP.NET MVC che utilizza le informazioni del sottodominio per determinarne la route? Ad esempio:

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

Oppure posso farlo in modo che entrambi vadano sullo stesso controller / azione con un parametro username?

È stato utile?

Soluzione

Puoi farlo creando una nuova rotta e aggiungendola alla raccolta delle rotte in RegisterRoutes in 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;
    }
}

Altri suggerimenti

Per acquisire il sottodominio mantenendo le funzionalità di routing MVC5 standard , utilizzare la seguente SubdomainRoute classe derivata da Route.

Inoltre, sub.example.com/foo/bar consente facoltativamente di specificare il sottodominio come parametro di query , rendendo example.com/foo/bar?subdomain=sub e Url.Action equivalenti. Ciò consente di eseguire il test prima di configurare i sottodomini DNS. Il parametro di query (quando in uso) viene propagato attraverso nuovi collegamenti generati da MapSubdomainRoute, ecc.

Il parametro query abilita anche il debug locale con Visual Studio 2013 senza dover configurare con netsh o eseguire come amministratore . Per impostazione predefinita, IIS Express si lega a localhost solo quando non elevato; non si legherà nemmeno a nomi host 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 RegisterRoutes metodo dal tuo MapRoute metodo come faresti con il vecchio Subdomain:

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 sottodominio vero o da un parametro di query), è utile creare una classe base del controller con questa proprietà <=>:

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 classe DomainRoute che può essere utilizzata in modo molto simile al normale routing.

http: //blog.maartenballiauw. essere / 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 API Web , sovrascrivere il Selettore azioni per iniettare un parametro di query subdomain. Quindi utilizzare il parametro di query del sottodominio nelle azioni dei controller in questo modo:

public string Get(string id, string subdomain)

Questo approccio rende conveniente il debug poiché è possibile specificare manualmente il parametro di query quando si utilizza localhost invece del nome host effettivo (consultare risposta al routing MVC5 standard per dettagli). Questo è il codice per 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);
    }
}

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 la route non è a conoscenza del dominio perché l'applicazione potrebbe essere distribuita a qualsiasi dominio e la route non si occuperebbe 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 libreria per il routing di sottodomini che puoi creare tale percorso. Funziona attualmente per .NET Core 1.1 e .NET Framework 4.6.1 ma verrà aggiornato nel prossimo futuro. Funziona così:
1) Mappa il percorso del 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 <a href="http://user1.localhost:54575/Home/Index">User home</a> 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 tag (FormTagHelper, AnchorTagHelper)
Quella lib non ha ancora documentazione, ma ci sono alcuni progetti di test e campioni, quindi sentiti libero di esplorarla.

In ASP.NET Core , l'host è disponibile tramite Request.Host.Host. Se si desidera consentire la sostituzione dell'host tramite un parametro di query, selezionare prima Request.Query.

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

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

E definisci HostPropagationRouter in questo modo:

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

Dopo aver definito un nuovo gestore di route che guarderebbe l'host passato nell'URL , puoi andare con l'idea di un controller di base che è a conoscenza del sito & # 8217; a cui si accede per. Sembra così:

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

Mi riferisco a Blog di Luke Sampson

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

https://github.com/saaskit/saaskit

Esempi di codice sono disponibili qui: http://benfoster.io/blog / saaskit-multi-tenancy-made-facile

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

EDIT: Se non vuoi usare SaasKit nel tuo progetto principale ASP.NET puoi dare un'occhiata all'implementazione di Maarten del routing del dominio per MVC6: https://blog.maartenballiauw.be/post/2015/02/ 17 / dominio di routing-e-risolvere-corrente-tenant-con-ASPNET-MVC-6-aspnet-5.html

Tuttavia questi Gists non sono mantenuti e devono essere ottimizzati per funzionare con l'ultima versione del core ASP.NET.

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

Pochi mesi fa ho sviluppato un attributo che limita metodi o controller a domini specifici.

È abbastanza facile da usare:

[IsDomain("localhost","example.com","www.example.com","*.t1.example.com")]
[HttpGet("RestrictedByHost")]
public IActionResult Test(){}

Puoi anche applicarlo direttamente su un controller.

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

Limitazione: potresti non essere in grado di avere due stessi percorsi su metodi diversi con filtri diversi Intendo che quanto segue può generare un'eccezione per il percorso duplicato:

[IsDomain("test1.example.com")]
[HttpGet("/Test")]
public IActionResult Test1(){}

[IsDomain("test2.example.com")]
[HttpGet("/Test")]
public IActionResult Test2(){}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top