2008-11-10 92 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還沒有任何文檔,但有一些測試和示例項目,所以隨時去探索它。

相關問題