Asp.net mvc 是否可以基于子域创建ASP.NET MVC路由?

Asp.net mvc 是否可以基于子域创建ASP.NET MVC路由?,asp.net-mvc,routing,asp.net-mvc-routing,Asp.net Mvc,Routing,Asp.net Mvc Routing,是否可以使用子域信息确定其路由的ASP.NET MVC路由?例如: user1.domain.com只到一个地方 user2.domain.com转到另一个 或者,我是否可以使用用户名参数将这两个属性设置为同一控制器/操作?是,但您必须创建自己的路由处理程序 通常,路由不知道域,因为应用程序可以部署到任何域,并且路由不会以任何方式关注。但在您的情况下,您希望将控制器和操作建立在域之外,因此您必须创建一个了解域的自定义路由。您可以通过创建一个新路由并将其添加到global.asax中Regis

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

  • user1.domain.com只到一个地方
  • user2.domain.com转到另一个

或者,我是否可以使用
用户名
参数将这两个属性设置为同一控制器/操作?

是,但您必须创建自己的路由处理程序


通常,路由不知道域,因为应用程序可以部署到任何域,并且路由不会以任何方式关注。但在您的情况下,您希望将控制器和操作建立在域之外,因此您必须创建一个了解域的自定义路由。

您可以通过创建一个新路由并将其添加到global.asax中RegisterRoutes中的routes集合来实现。下面是一个非常简单的自定义路线示例:

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;
    }
}
公共类示例Route:RouteBase
{
公共覆盖路由数据GetRouteData(HttpContextBase httpContext)
{
var url=httpContext.Request.Headers[“主机”];
var index=url.IndexOf(“.”);
如果(指数<0)
返回null;
var subDomain=url.Substring(0,索引);
如果(子域==“用户1”)
{
var routeData=new routeData(这是新的MvcRouteHandler());
routeData.Values.Add(“controller”、“User1”);//转到User1Controller类
routeData.Values.Add(“action”,“Index”);//转到User1Controller上的索引操作
返回路由数据;
}
如果(子域==“用户2”)
{
var routeData=new routeData(这是新的MvcRouteHandler());
routeData.Values.Add(“controller”、“User2”);//转到User2Controller类
routeData.Values.Add(“action”、“Index”);//转到User2Controller上的索引操作
返回路由数据;
}
返回null;
}
公共覆盖VirtualPathData GetVirtualPath(RequestContext RequestContext,RouteValueDictionary值)
{
//在此处实现格式化Url格式化
返回null;
}
}

这不是我的工作,但我必须在这个答案上加上它

这是解决这个问题的好办法。Maartin Balliauw编写了创建DomainRoute类的代码,该类的使用方式与普通路由非常类似

示例使用如下所示

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

)

要在保留标准MVC5路由功能的同时捕获子域,请使用从
路由
派生的
子域路由

此外,
SubdomainRoute
允许可选地将子域指定为查询参数,使
sub.example.com/foo/bar
example.com/foo/bar?subdomain=sub
等效。这允许您在配置DNS子域之前进行测试。查询参数(使用时)通过
Url.Action
等生成的新链接传播

查询参数还支持使用Visual Studio 2013进行本地调试,而无需执行此操作。默认情况下,IIS Express仅在非提升时绑定到本地主机;它不会绑定到类似sub.localtest.me的同义主机名

为方便起见,请从您的
RegisterRoutes
方法调用以下
MapSubdomainRoute
方法,就像普通的
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()
    });
}
最后,为了方便地访问子域(从真正的子域或查询参数),使用此
子域
属性创建控制器基类很有帮助:

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

要在使用Web API时捕获子域,请重写操作选择器以插入
子域
查询参数。然后在控制器的操作中使用子域查询参数,如下所示:

public string Get(string id, string subdomain)
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();
        }
    }

}
/// <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);
}
这种方法便于调试,因为在使用localhost而不是实际主机名时,可以手动指定查询参数(有关详细信息,请参阅)。这是操作选择器的代码:

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);
    }
}
在定义了一个新的路由处理程序来查看在URL中传递的主机之后,您可以使用一个基本控制器的概念,该控制器知道访问它的站点。看起来是这样的:

public string Get(string id, string subdomain)
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();
        }
    }

}
/// <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);
}
ISiteProvider
是一个简单的界面:

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

我建议您转到

如果您希望为您的项目提供多租户功能,为每个租户提供不同的域/子域,您应该看看SaasKit:

代码示例如下所示:

使用ASP.NET core的一些示例:

编辑: 如果您不想在ASP.NET核心项目中使用SaasKit,可以看看Maarten针对MVC6的域路由实现:

但是,这些GIST没有得到维护,需要进行调整才能与最新版本的ASP.NET core配合使用


直接链接到代码:

ASP.NET Core中,可以通过
请求.host.host
访问主机。如果要允许通过查询参数覆盖主机,请首先选中
Request.query

要使主机查询参数传播到新的基于路由的URL,请将此代码添加到
app.UseMvc
route配置:

routes.Routes.Add(new HostPropagationRouter(routes.DefaultHandler));
然后定义主机传播路由器,如下所示:

public string Get(string id, string subdomain)
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();
        }
    }

}
/// <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);
}
//
///将请求的“主机”查询参数传播到响应的路由器。
/// 
类HostPropagationRouter:IRouter
{
只读外部路由器;
公共主机传播路由器(IRouter路由器)
{
this.router=路由器;
}
公共VirtualPathData GetVirtualPath(VirtualPathContext上下文)
{
if(context.HttpContext.Request.Query.TryGetValue(“主机”,out-var-host))
context.Values[“host”]=host;
返回router.GetVirtualPath(上下文);
}
公共任务RouteAsync(RouteContext)=>router.RouteAsync
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(){}