it-swarm.com.de

Ist es möglich, eine ASP.NET-MVC-Route basierend auf einer Unterdomäne zu erstellen?

Ist es möglich, eine ASP.NET-MVC-Route zu verwenden, die Subdomain-Informationen zum Bestimmen der Route verwendet? Zum Beispiel:

  • user1 . domain.com geht an einen Ort
  • user2 . domain.com geht zu einem anderen?

Oder kann ich es so einrichten, dass beide mit einem username-Parameter zu derselben Steuerung/Aktion gehen?

229
Dan Esparza

Sie können dies tun, indem Sie eine neue Route erstellen und der Routensammlung in RegisterRoutes in Ihrer global.asax hinzufügen. Im Folgenden finden Sie ein sehr einfaches Beispiel für eine benutzerdefinierte Route:

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;
    }
}
167
Jon Cahill

Verwenden Sie die folgende SubdomainRoute-Klasse, die von Route abgeleitet ist, um die Unterdomäne zu zu erfassen, während die MVC5-Routing-Features beibehalten werden.

Darüber hinaus ermöglicht SubdomainRoute, dass die Unterdomäne optional als query-Parameter angegeben werden kann, wodurch sub.example.com/foo/bar und example.com/foo/bar?subdomain=sub gleichwertig werden. Auf diese Weise können Sie testen, bevor die DNS-Unterdomänen konfiguriert sind. Der Abfrageparameter (wenn verwendet) wird über neue, von Url.Action generierte Links usw. weitergegeben.

Der Abfrageparameter ermöglicht auch das lokale Debuggen mit Visual Studio 2013, ohne dass Sie mit netsh konfigurieren oder als Administrator ausführen müssen. Standardmäßig bindet IIS Express nur an localhost, wenn es nicht erhöht ist. es bindet sich nicht an Hostnamen wie 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);
    }
}

Rufen Sie zur Vereinfachung die folgende MapSubdomainRoute-Methode von Ihrer RegisterRoutes-Methode auf, genau wie Sie es mit der alten MapRoute-Methode tun würden:

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

Für den bequemen Zugriff auf die Subdomäne (entweder von einer echten Subdomain oder einem Abfrageparameter) ist es hilfreich, eine Controller-Basisklasse mit der folgenden Subdomain-Eigenschaft zu erstellen:

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

Dies ist nicht meine Arbeit, aber ich musste es zu dieser Antwort hinzufügen.

Hier ist eine großartige Lösung für dieses Problem. Maartin Balliauw hat Code geschrieben, mit dem eine DomainRoute-Klasse erstellt wird, die dem normalen Routing sehr ähnlich ist.

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

Beispielanwendung wäre so ...

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

;

23
Jim Blake

Um die Subdomain bei Verwendung von Web API zu erfassen, überschreiben Sie die Aktionsauswahl, um einen Abfrageparameter subdomain einzufügen. Verwenden Sie dann den Abfrageparameter der Unterdomäne in den Aktionen Ihrer Controller wie folgt:

public string Get(string id, string subdomain)

Dieser Ansatz macht das Debuggen praktisch, da Sie den Abfrageparameter manuell angeben können, wenn Sie localhost anstelle des tatsächlichen Hostnamens verwenden (siehe Standard MVC5 Routing-Antwort für Details). Dies ist der Code für die Aktionsauswahl:

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

Ersetzen Sie die Standard-Aktionsauswahl, indem Sie Folgendes zu WebApiConfig.Register hinzufügen:

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

Ich habe library für das Subdomain-Routing erstellt mit dem Sie eine solche Route erstellen können. Es arbeitet derzeit für .NET Core 1.1 und .NET Framework 4.6.1, wird aber in naher Zukunft aktualisiert. So funktioniert es: 
1) Map-Subdomain-Route 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) Mit dieser lib können Sie auch URLs und Formulare generieren. Code:

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

Generiert <a href="http://user1.localhost:54575/Home/Index">User home</a> Die generierte URL hängt auch vom aktuellen Host-Speicherort und Schema ab.
Sie können auch HTML-Helfer für BeginForm und UrlHelper verwenden. Wenn Sie möchten, können Sie auch eine neue Funktion namens Tag-Helfer verwenden (FormTagHelper, AnchorTagHelper
Diese Bibliothek hat noch keine Dokumentation, aber es gibt einige Test- und Beispielprojekte, also zögern Sie nicht, sie zu erkunden.

3
Mariusz

Ja, aber Sie müssen einen eigenen Routen-Handler erstellen. 

Normalerweise kennt die Route die Domäne nicht, da die Anwendung in einer beliebigen Domäne bereitgestellt werden könnte und die Route sich nicht für die eine oder andere Weise interessieren würde. In Ihrem Fall möchten Sie jedoch, dass der Controller und die Aktion von der Domäne ausgehen. Daher müssen Sie eine benutzerdefinierte Route erstellen, die die Domäne kennt.

3
Nick Berardi

In ASP.NET Core ist der Host über Request.Host.Host verfügbar. Wenn Sie das Überschreiben des Hosts über einen Abfrageparameter zulassen möchten, überprüfen Sie zunächst Request.Query.

Fügen Sie diesen Code der app.UseMvc-Routerkonfiguration hinzu, um zu bewirken, dass ein Host-Abfrageparameter in neue routenbasierte URLs übertragen wird:

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

Und definiere HostPropagationRouter so:

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

Nachdem Sie einen neuen Route-Handler definiert haben, der den in der URL übergebenen Host betrachtet, können Sie sich auf die Idee eines Basis-Controllers verlassen, der die Site kennt, auf die er zugreift. Es sieht aus wie das:

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 ist eine einfache Schnittstelle:

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

Ich beziehe mich auf Luke Sampson Blog

Wenn Sie Ihrem Projekt MultiTenancy-Funktionen mit unterschiedlichen Domänen/Subdomänen für jeden Mandanten zur Verfügung stellen möchten, sollten Sie SaasKit ansehen:

https://github.com/saaskit/saaskit

Codebeispiele finden Sie hier: http://benfoster.io/blog/saaskit-multi-tenancy-made-easy

Einige Beispiele, die ASP.NET-Kern verwenden: http://andrewlock.net/forking-the-pipeline-adding-tenant-specific-files-with-saaskit-in-asp-net-core/

BEARBEITEN: Wenn Sie SaasKit nicht in Ihrem ASP.NET-Kernprojekt verwenden möchten, können Sie einen Blick auf die Implementierung von Domain-Routing für MVC6 von Maarten werfen: https://blog.maartenballiauw.be/post/2015/ 02/17/domain-routing-und-auflösung-current-tenant-with-aspnet-mvc-6-aspnet-5.html

Diese Gists werden jedoch nicht beibehalten und müssen angepasst werden, um mit der neuesten Version des ASP.NET-Kerns zu arbeiten.

Direkter Link zum Code: https://Gist.github.com/maartenba/77ca6f9cfef50efa96ec#file-domaintemplateroutebuilderextensions-cs

1
Darxtar

Vor wenigen Monaten habe ich ein Attribut entwickelt, das Methoden oder Controller auf bestimmte Domänen beschränkt. 

Es ist ganz einfach zu benutzen: 

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

Sie können es auch direkt auf einem Controller anwenden.

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

Einschränkung: Sie können möglicherweise nicht zwei gleiche Routen auf verschiedenen Methoden mit unterschiedlichen Filtern haben Ich meine, Folgendes kann eine Ausnahme für eine doppelte Route auslösen:

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

[IsDomain("test2.example.com")]
[HttpGet("/Test")]
public IActionResult Test2(){}
0
Jean