MVC 4 DomainRoute to Area uses wrong instance from RouteTable

左心房为你撑大大i 提交于 2019-12-14 02:43:48

问题


I have an ASP.NET MVC 4 app where I have two areas:

  • Backstage
  • Signup

I have a class called DomainRoute that makes it possible to route an entire subdomain to an area:

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using System.Web.Routing;

namespace Admin.Web.PresentationLogic
{
    /// <summary>
    /// DomainRoute is an extension of the default Route, that makes it possible to route domains and subdomains the specific controllers.
    /// </summary>
    public class DomainRoute : Route
    {
        private string _subDomain;

        private string[] _namespaces;

        /// <summary>
        /// Initializes a new instance of the <see cref="DomainRoute"/> class.
        /// </summary>
        /// <param name="subDomain">The sub domain.</param>
        /// <param name="url">The URL format.</param>
        /// <param name="defaults">The defaults.</param>
        public DomainRoute(string subDomain, string url, RouteValueDictionary defaults)
            : base(url, defaults, new MvcRouteHandler())
        {
            this._subDomain = subDomain;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DomainRoute" /> class.
        /// </summary>
        /// <param name="subDomain">The sub domain.</param>
        /// <param name="url">The URL format.</param>
        /// <param name="defaults">The defaults.</param>
        /// <param name="namespaces">The namespaces.</param>
        public DomainRoute(string subDomain, string url, RouteValueDictionary defaults, string[] namespaces)
            : base(url, defaults, new MvcRouteHandler())
        {
            this._subDomain = subDomain;
            this._namespaces = namespaces;
        }

        /// <summary>
        /// Returns information about the requested route.
        /// </summary>
        /// <param name="httpContext">An object that encapsulates information about the HTTP request.</param>
        /// <returns>
        /// An object that contains the values from the route definition.
        /// </returns>
        public override RouteData GetRouteData(System.Web.HttpContextBase httpContext)
        {
            // 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;
            }

            var index = requestDomain.IndexOf(".");

            if (index < 0)
            {
                return RouteTable.Routes["Default"].GetRouteData(httpContext);
            }

            var subDomain = requestDomain.Substring(0, index);

            if (!String.IsNullOrWhiteSpace(subDomain))
            {
                if (this._subDomain.Equals(subDomain, StringComparison.InvariantCultureIgnoreCase))
                {
                    RouteData data = new RouteData(this, this.RouteHandler);

                    // Add defaults first 
                    if (Defaults != null)
                    {
                        foreach (KeyValuePair<string, object> item in Defaults)
                        {
                            data.Values[item.Key] = item.Value;
                        }
                    }

                    var pathRegex = this.CreateRegex(Url);
                    var requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;

                    // Match domain and route
                    Match pathMatch = pathRegex.Match(requestPath);

                    // 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;
                                }
                            }
                        }
                    }

                    if (!data.Values.ContainsKey("action"))
                    {
                        data.Values.Add("action", "Index");
                    }

                    data.DataTokens["Namespaces"] = this._namespaces;
                    data.DataTokens["area"] = data.Values["area"] ?? this._subDomain;

                    return data;
                }
            }

            return RouteTable.Routes["Default"].GetRouteData(httpContext);
        }

        /// <summary>
        /// Creates the regex.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <returns>Returns the Regex for the source.</returns>
        private Regex CreateRegex(string source)
        {
            // Perform replacements
            source = source.Replace("/", @"\/?");
            source = source.Replace(".", @"\.?");
            source = source.Replace("-", @"\-?");
            source = source.Replace("{", @"(?<");
            source = source.Replace("}", @">([a-zA-Z0-9_]*))");

            return new Regex("^" + source + "$");
        }
    }
}

When I register the areas, I do this:

context.Routes.Add("Signup_default", 
    new DomainRoute("signup", "{controller}/{action}/{id}", 
    new RouteValueDictionary(new { area = "Signup", controller = "Home", action = "Index", id = UrlParameter.Optional }), 
    new string[] { "Admin.Web.Areas.Signup.Controllers" }));

So, the problem has to do with the way the DomainRoute's GetRouteData method is executed.

Whenever I try to access signup.localhost the instance of the DomainRoute class is the one that I used when registering the Backstage area.

I tried disabling the Backstage area, and then the Signup area worked.

It uses the instance of DomainRoute that occurs first in the RouteTable.

What am I missing?


回答1:


I was missing the concept of the RouteCollection.

After step debugging into the .Net source, I realized that in my DomainRoute, if the sub-domain doesn't match, instead of returning default route data I should return null.

That's the way ASP.NET Routing determines which one to use -- by calling GetRouteData with the HttpContext and let the specific route figure out if "I'm a match for the HttpContext?"



来源:https://stackoverflow.com/questions/12942021/mvc-4-domainroute-to-area-uses-wrong-instance-from-routetable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!