Можно ли сделать ASP.NET маршрут MVC на основе поддомена?



возможно ли иметь ASP.NET маршрут MVC, который использует информацию о поддомене для определения своего маршрута? Например:





  • user1.domain.com идет в одно место


  • user2.domain.com идет к другому?


или я могу сделать так, чтобы оба они шли к одному контроллеру / действию с ?

526   9  

9 ответов:

вы можете сделать это, создав новый маршрут и добавив его в коллекцию маршрутов в 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 метод так же, как обычный 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 свойства:

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, переопределите селектор действий, чтобы ввести 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) Карта маршрута поддомена при запуске.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 также будет зависеть от текущего местоположения хоста и схемы.
Вы также можете использовать HTML-помощники для BeginForm и UrlHelper. Если вам нравится, вы также можете использовать новую функцию под названием tag helpers (FormTagHelper,AnchorTagHelper)
Эта библиотека еще не имеет никакой документации, но есть некоторые тесты и образцы проекта, поэтому не стесняйтесь исследовать его.

In ASP.NET ядро, хост доступен через 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);
}

после определения нового обработчика маршрута, который будет смотреть на хост, переданный в 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();
}

Я отсылаю вас к Блог Люка Сэмпсона

Если вы рассматриваете возможность предоставления Мультитенантности вашему проекту с различными доменами / поддоменами для каждого арендатора, вы должны взглянуть на 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/

изменить: Если вы не хотите использовать SaasKit в своем ASP.NET основной проект вы можете посмотреть на реализацию Маартена доменной маршрутизации для MVC6: https://blog.maartenballiauw.be/post/2015/02/17/domain-routing-and-resolving-current-tenant-with-aspnet-mvc-6-aspnet-5.html

однако эти Gists не поддерживаются и должны быть изменена для работы с последней версией ASP.NET ядро.

прямая ссылка на код: https://gist.github.com/maartenba/77ca6f9cfef50efa96ec#file-domaintemplateroutebuilderextensions-cs

Comments

    Ничего не найдено.