2008-11-10 41 views
222

是否有可能使用子域信息来确定其路由的ASP.NET MVC路由?例如:是否有可能基于子域创建ASP.NET MVC路线?

  • USER1 .domain.com去一个地方
  • user2的 .domain.com去到另一个?

或者,我可以做到这一点,所以这两个这些去与username参数相同的控制器/行动?

+0

我为多租户应用程序实现了类似的事情,但使用抽象基本控制器而不是自定义Route类。我的博客文章是[here](http://lukesampson.com/post/303245177/subdomains-for-a-single-application-with-asp-net-mvc)。 – 2009-07-06 06:26:51

+6

一定要考虑这种方法:[http://blog.tonywilliams.me.uk/asp-net-mvc-2-routing-subdomains-to-areas](http://blog.tonywilliams.me.uk/ asp-net-mvc-2-routing-subdomains-to-areas)我发现将多租户引入我的应用比其他解决方案更好,因为MVC领域是一种很好的方式来引入租户特定的控制器和视图有组织的方式 – trebormf 2011-05-16 19:55:10

+2

@trebormf - 我认为你应该把它作为答案添加,这是我最终用作解决方案的基础。 – Shagglez 2012-09-14 13:48:16

回答

162

您可以通过创建一个新的路由并将其添加到您的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; 
    } 
} 
+1

感谢您的详细示例,但我不是如何执行从Global.asax添加。 – justSteve 2012-01-04 17:25:47

3

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

通常情况下,路由不知道域,因为应用程序可能部署到任何域,并且路由不会以任何方式处理。但在你的情况下,你希望将控制器和操作基于域,所以你将不得不创建一个自定义路由,以了解域。

22

这不是我的工作,但我不得不将其添加到此答案。

这是解决这个问题的好方法。 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 
)) 

;

50

捕获子域时保留标准MVC5路由功能,使用以下SubdomainRoute类从Route导出。

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

查询参数还启用了Visual Studio 2013的本地调试,而无需configure with netsh or run as Administrator。默认情况下,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); 
    } 
} 

为了方便起见,从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() 
    }); 
} 

最后,为了方便地访问子域(无论是从真正的子域或查询参数),创建具有此Subdomain属性的Controller基类很有帮助:

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

要在使用Web API时捕获子域,请覆盖Action Selector以注入subdomain查询参数。然后使用子域名查询参数在控制器的行为是这样的:使用本地主机而不是实际的主机名的时候

public string Get(string id, string subdomain) 

这种方法使得调试方便,因为你可以手工指定查询参数(见standard MVC5 routing answer了解详情)。这是行动选择的代码:

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

定义一个新的路由处理会看在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(); 
} 

我是指你去Luke Sampson Blog

1

如果你正在寻找让多重任务处理功能,将项目与不同的域/每个租户的子域名,你应该看看SaasKit:

https://github.com/saaskit/saaskit

示例代码可以在这里看到:http://andrewlock.net/forking-the-pipeline-adding-tenant-specific-files-with-saaskit-in-asp-net-core/

编辑: 如果你不想要在你的ASP.NET核心项目使用SaasKit你可以有http://benfoster.io/blog/saaskit-multi-tenancy-made-easy

使用ASP.NET核心的一些例子看看Maarten为MVC6实现的域路由:https://blog.maartenballiauw.be/post/2015/02/17/domain-routing-and-resolving-current-tenant-with-aspnet-mvc-6-aspnet-5.html

但是,这些Gist没有被维护,需要调整才能使用最新版本的ASP.NET核心。

直接链接代码:https://gist.github.com/maartenba/77ca6f9cfef50efa96ec#file-domaintemplateroutebuilderextensions-cs

2

ASP.NET核心,主机是通过Request.Host.Host可用。如果您想允许通过查询参数覆盖主机,请首先检查Request.Query

使A主机查询参数传播到新的基于路由的网址,该代码添加到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); 
} 
2

我创建了library for subdomain routing,您可以创建此类路线。它目前正在为.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)这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帮助程序BeginFormUrlHelper。如果你喜欢,你也可以使用称为标签助手的新功能(FormTagHelperAnchorTagHelper
该lib还没有任何文档,但有一些测试和示例项目,所以随时去探索它。

相关问题