首页 文章

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

提问于
浏览
228

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

  • user1 .domain.com去了一个地方

  • user2 .domain.com去了另一个?

或者,我可以这样做,所以这两个都与 username 参数进入相同的控制器/动作?

9 回答

  • 52

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

    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;
        }
    }
    
  • 4

    capture the subdomain while retaining the standard MVC5 routing features ,请使用从 Route 派生的以下 SubdomainRoute 类 .

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

    query参数还允许使用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"]; }
    }
    
  • 166

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

    这是一个很好的解决方案 . 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 
    ))
    

    ;

  • 2

    要在使用 Web API 时捕获子域,请覆盖操作选择器以注入 subdomain 查询参数 . 然后在控制器的操作中使用子域查询参数,如下所示:

    public string Get(string id, string subdomain)
    

    这种方法使调试变得方便,因为您可以在使用localhost而不是实际主机名时手动指定查询参数(有关详细信息,请参阅standard MVC5 routing answer) . 这是Action Selector的代码:

    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()));
    
  • 3

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

    通常,路由不知道域,因为应用程序可以部署到任何域,并且路由不会这样或那样 . 但在您的情况下,您希望将控制器和操作基于域,因此您必须创建一个知道域的自定义路由 .

  • 1

    我创建了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)Controllers / 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还将取决于当前的主机位置和架构 .
    您还可以为 BeginFormUrlHelper 使用html助手 . 如果您愿意,还可以使用称为标记助手的新功能( FormTagHelperAnchorTagHelper
    lib还没有任何文档,但有一些测试和示例项目,所以随时可以探索它 .

  • 23

    ASP.NET Core 中,主机可通过 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);
    }
    
  • 1

    在定义了一个新的Route处理程序,该处理程序将查看在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

  • 3

    如果您正在考虑为每个租户提供具有不同域/子域的项目的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-specific-files-with-saaskit-in-asp-net-core/

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

    但是,这些Gists不会被维护,需要进行调整以使用最新版本的ASP.NET核心 .

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

相关问题