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

StackOverflow https://stackoverflow.com/questions/278668

Вопрос

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

  • пользователь1.domain.com идет в одно место
  • пользователь2.domain.com пойдет к другому?

Или, могу ли я сделать так, чтобы оба из них перешли к одному и тому же контроллеру/действию с помощью username параметр?

Это было полезно?

Решение

Вы можете сделать это, создав новый маршрут и добавив его в коллекцию маршрутов в RegisterRoutes в вашем global.asax.Ниже приведен очень простой пример пользовательского маршрута:

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

Это не моя работа, но мне пришлось добавить ее к этому ответу.

Вот отличное решение этой проблемы.Маартин Баллиау написал код, создающий класс 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 
))

;

Чтобы захватить поддомен при использовании Веб-API, переопределите селектор действий, чтобы ввести subdomain параметр запроса.Затем используйте параметр запроса поддомена в действиях ваших контроллеров следующим образом:

public string Get(string id, string subdomain)

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

В Ядро 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();
}

Я советую вам пойти Блог Люка Сэмпсона

Если вы хотите предоставить своему проекту возможности MultiTenancy с разными доменами/субдоменами для каждого арендатора, вам следует взглянуть на 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-специфические-файлы-with-saaskit-in-asp-net-core/

РЕДАКТИРОВАТЬ:Если вы не хотите использовать SaasKit в своем основном проекте ASP.NET, вы можете взглянуть на реализацию маршрутизации домена Maarten для MVC6: https://blog.maartenballiauw.be/post/2015/02/17/domain-routing-and-resolve-current-tenant-with-aspnet-mvc-6-aspnet-5.html

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

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

Несколько месяцев назад я разработал атрибут, который ограничивает методы или контроллеры определенными доменами.

Его довольно легко использовать:

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

Вы также можете применить его непосредственно к контроллеру.

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

Ограничение:Возможно, вы не сможете иметь два одинаковых маршрута на разных методах с разными фильтрами, я имею в виду, что следующее может бросить исключение для дубликата маршрута:

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

[IsDomain("test2.example.com")]
[HttpGet("/Test")]
public IActionResult Test2(){}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top