是否可以有一个使用子域信息来确定其路由的 ASP.NET MVC 路由?例如:

  • 用户1.domain.com 去一个地方
  • 用户2.domain.com 去另一个?

或者,我可以让这两个都进入同一个控制器/动作吗? username 范围?

有帮助吗?

解决方案

您可以通过创建一个新路由并将其添加到 global.asax 中 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/barexample.com/foo/bar?subdomain=sub 相等的。这允许您在配置 DNS 子域之前进行测试。查询参数(使用时)通过生成的新链接传播 Url.Action, , ETC。

查询参数还可以使用 Visual Studio 2013 进行本地调试,而无需 使用 netsh 配置或以管理员身份运行. 。默认情况下,IIS Express 仅绑定到 本地主机 未升高时;它不会绑定到同义主机名,例如 子本地测试.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()
    });
}

最后,为了方便地访问子域(无论是从真正的子域还是查询参数),使用此创建一个 Controller 基类会很有帮助 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 
))

;

使用时捕获子域 网络应用程序接口, ,覆盖操作选择器以注入 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 帮助器 BeginFormUrlHelper. 。如果您愿意,您还可以使用称为标签助手的新功能(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();
}

我建议你去 卢克·桑普森博客

如果您正在考虑为您的项目提供多租户功能,并为每个租户提供不同的域/子域,那么您应该看看 SaasKit:

https://github.com/saaskit/saaskit

代码示例可以在这里查看: http://benfoster.io/blog/saaskit-multi-tenancy-made-easy

使用 ASP.NET core 的一些示例: http://andrewlock.net/forking-the-pipeline-adding-tenant-specific-files-with-saaskit-in-asp-net-core/

编辑:如果您不想在 ASP.NET Core 项目中使用 SaasKit,您可以查看 Maarten 的 MVC6 域路由实现: https://blog.maartenballiauw.be/post/2015/02/17/domain-routing-and-resolving-current-tenant-with-aspnet-mvc-6-aspnet-5.html

然而,这些要点并未得到维护,需要进行调整才能与最新版本的 ASP.NET core 配合使用。

直接链接到代码: 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