zoukankan      html  css  js  c++  java
  • [转]ASP.NET MVC Domain Routing

    本文转自:http://blog.maartenballiauw.be/post/2009/05/20/ASPNET-MVC-Domain-Routing.aspx

    ASP.NET MVC Domain Routing 
    
    20. May 2009 08:23  /  maartenba  /  ASP.NET . C# . General . MVC . Projects  /  Comments (33) 
    
    
    Routing Ever since the release of ASP.NET MVC and its routing engine (System.Web.Routing), Microsoft has been trying to convince us that you have full control over your URL and routing. This is true to a certain extent: as long as it’s related to your application path, everything works out nicely. If you need to take care of data tokens in your (sub)domain, you’re screwed by default.
    
    Earlier this week, Juliën Hanssens did a blog post on his approach to subdomain routing. While this is a good a approach, it has some drawbacks:
    •All routing logic is hard-coded: if you want to add a new possible route, you’ll have to code for it. 
    •The VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) method is not implemented, resulting in “strange” urls when using HtmlHelper ActionLink helpers. Think of http://live.localhost/Home/Index/?liveMode=false where you would have just wanted http://develop.localhost/Home/Index. 
    
    Unfortunately, the ASP.NET MVC infrastructure is based around this VirtualPathData class. That’s right: only tokens in the URL’s path are used for routing… Check my entry on the ASP.NET MVC forums on that one.
    
    Now for a solution… Here are some scenarios we would like to support:
    •Scenario 1: Application is multilingual, where www.nl-be.example.com should map to a route like “www.{language}-{culture}.example.com”. 
    •Scenario 2: Application is multi-tenant, where www.acmecompany.example.com should map to a route like “www.{clientname}.example.com”. 
    •Scenario 3: Application is using subdomains for controller mapping: www.store.example.com maps to "www.{controller}.example.com/{action}...." 
    
    Sit back, have a deep breath and prepare for some serious ASP.NET MVC plumbing…
    
    kick it on DotNetKicks.com 
    
    Defining routes
    
    Here are some sample route definitions we want to support. An example where we do not want to specify the controller anywhere, as long as we are on home.example.com:
    
    [code:c#]
    
    routes.Add("DomainRoute", new DomainRoute( 
        "home.example.com", // Domain with parameters 
        "{action}/{id}",    // URL with parameters 
        new { controller = "Home", action = "Index", id = "" }  // Parameter defaults 
    ));
    
    [/code]
    
    Another example where we have our controller in the domain name:
    
    [code:c#]
    
    routes.Add("DomainRoute", new DomainRoute( 
        "{controller}.example.com",     // Domain with parameters< br />    "{action}/{id}",    // URL with parameters 
        new { controller = "Home", action = "Index", id = "" }  // Parameter defaults 
    ));
    
    [/code]
    
    Want the full controller and action in the domain?
    
    [code:c#]
    
    routes.Add("DomainRoute", new DomainRoute( 
        "{controller}-{action}.example.com",     // Domain with parameters 
        "{id}",    // URL with parameters 
        new { controller = "Home", action = "Index", id = "" }  // Parameter defaults 
    ));
    
    [/code]
    
    Here’s the multicultural route:
    
    [code:c#]
    
    routes.Add("DomainRoute", new DomainRoute( 
        "{language}.example.com",     // Domain with parameters 
        "{controller}/{action}/{id}",    // URL with parameters 
        new { language = "en", controller = "Home", action = "Index", id = "" }  // Parameter defaults 
    ));
    
    [/code]
    
    HtmlHelper extension methods
    
    Since we do not want all URLs generated by HtmlHelper ActionLink to be using full URLs, the first thing we’ll add is some new ActionLink helpers, containing a boolean flag whether you want full URLs or not. Using these, you can now add a link to an action as follows:
    
    [code:c#]
    
    <%= Html.ActionLink("About", "About", "Home", true)%>
    
    [/code]
    
    Not too different from what you are used to, no?
    
    Here’s a snippet of code that powers the above line of code:
    
    [code:c#]
    
    public static class LinkExtensions 
    { 
        public static string ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, bool requireAbsoluteUrl) 
        { 
            return htmlHelper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary(), requireAbsoluteUrl); 
        }
    
        // more of these...
    
        public static string ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool requireAbsoluteUrl) 
        { 
            if (requireAbsoluteUrl) 
            { 
                HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current); 
                RouteData routeData = RouteTable.Routes.GetRouteData(currentContext);
    
                routeData.Values["controller"] = controllerName; 
                routeData.Values["action"] = actionName;
    
                DomainRoute domainRoute = routeData.Route as DomainRoute; 
                if (domainRoute != null) 
                { 
                    DomainData domainData = domainRoute.GetDomainData(new RequestContext(currentContext, routeData), routeData.Values); 
                    return htmlHelper.ActionLink(linkText, actionName, controllerName, domainData.Protocol, domainData.HostName, domainData.Fragment, routeData.Values, null); 
                } 
            } 
            return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes); 
        } 
    }
    
    [/code]
    
    Nothing special in here: a lot of extension methods, and some logic to add the domain name into the generated URL. Yes, this is one of the default ActionLink helpers I’m abusing here, getting some food from my DomainRoute class (see: Dark Magic).
    
    Dark magic
    
    You may have seen the DomainRoute class in my code snippets from time to time. This class is actually what drives the extraction of (sub)domain and adds token support to the domain portion of your incoming URLs.
    
    We will be extending the Route base class, which already gives us some properties and methods we don’t want to implement ourselves. Though there are some we will define ourselves:
    
    [code:c#]
    
    public class DomainRoute : Route 
    {  
        // ...
    
        public string Domain { get; set; }
    
        // ...
    
        public override RouteData GetRouteData(HttpContextBase httpContext) 
        { 
            // Build regex 
            domainRegex = CreateRegex(Domain); 
            pathRegex = CreateRegex(Url);
    
            // Request information 
            string requestDomain = httpContext.Request.Headers["host"]; 
            if (!string.IsNullOrEmpty(requestDomain)) 
            { 
                if (requestDomain.IndexOf(":") > 0) 
                { 
                    requestDomain = requestDomain.Substring(0, requestDomain.IndexOf(":")); 
                } 
            } 
            else 
            { 
                requestDomain = httpContext.Request.Url.Host; 
            } 
            string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
    
            // Match domain and route 
            Match domainMatch = domainRegex.Match(requestDomain); 
            Match pathMatch = pathRegex.Match(requestPath);
    
            // Route data 
            RouteData data = null; 
            if (domainMatch.Success && pathMatch.Success) 
            { 
                data = new RouteData(this, RouteHandler);
    
                // Add defaults first 
                if (Defaults != null) 
                { 
                    foreach (KeyValuePair<string, object> item in Defaults) 
                    { 
                        data.Values[item.Key] = item.Value; 
                    } 
                }
    
                // Iterate matching domain groups 
                for (int i = 1; i < domainMatch.Groups.Count; i++) 
                { 
                    Group group = domainMatch.Groups[i]; 
                    if (group.Success) 
                    { 
                        string key = domainRegex.GroupNameFromNumber(i); 
                        if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0)) 
                        { 
                            if (!string.IsNullOrEmpty(group.Value)) 
                            { 
                                data.Values[key] = group.Value; 
                            } 
                        } 
                    } 
                }
    
                // Iterate matching path groups 
                for (int i = 1; i < pathMatch.Groups.Count; i++) 
                { 
                    Group group = pathMatch.Groups[i]; 
                    if (group.Success) 
                    { 
                        string key = pathRegex.GroupNameFromNumber(i); 
                        if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0)) 
                        { 
                            if (!string.IsNullOrEmpty(group.Value)) 
                            { 
                                data.Values[key] = group.Value; 
                            } 
                        } 
                    } 
                } 
            }
    
            return data; 
        }
    
        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) 
        { 
            return base.GetVirtualPath(requestContext, RemoveDomainTokens(values)); 
        }
    
        public DomainData GetDomainData(RequestContext requestContext, RouteValueDictionary values) 
        { 
            // Build hostname 
            string hostname = Domain; 
            foreach (KeyValuePair<string, object> pair in values) 
            { 
                hostname = hostname.Replace("{" + pair.Key + "}", pair.Value.ToString()); 
            }
    
            // Return domain data 
            return new DomainData 
            { 
                Protocol = "http", 
                HostName = hostname, 
                Fragment = "" 
            }; 
        }
    
        // ... 
    }
    
    [/code]
    
    Wow! That’s a bunch of code! What we are doing here is converting the incoming request URL into tokens we defined in our route, on the domain level and path level. We do this by converting {controller} and things like that into a regex which we then try to match into the route values dictionary. There are some other helper methods in our DomainRoute class, but these are the most important.
    
    Download the full code here: MvcDomainRouting.zip (250.72 kb)
  • 相关阅读:
    Leetcode 814. 二叉树剪枝
    Leetcode 104. 二叉树的最大深度
    Leetcode 617. 合并二叉树
    Leetcode 226. 翻转二叉树
    Leetcode 654.最大二叉树
    【Leetcode】413. Arithmetic Slices
    【Leetcode】128. Longest Consecutive Sequence
    【Leetcode】605. Can Place Flowers
    【Leetcode】647. Palindromic Substrings
    高可用架构
  • 原文地址:https://www.cnblogs.com/freeliver54/p/3833735.html
Copyright © 2011-2022 走看看