MVC實現動態二級域名

  前段時間,一個朋友問我ASP.NET MVC下實現動態二級域名的問題。跟他聊了一些解決方案,這裏也總結一下,以供參考。html

  相信你們都發現相似58同城這樣的網站,成都的網址是cd.58.com 上海的是sh.58.com相似的上千個網站,其實沒有那麼多個網站,域名前面那部分就是泛域名解析,至關因而傳遞一個參數,全部的域名實際上訪問的都是一個網站,僅僅是傳遞了不同的參數顯示不同的內容。服務器

  好比網站主域名入口爲:www.58.comdom

  當成都的用戶登陸時,解析到:cd.58.comide

  當上海的用戶登陸時,則解析到:sh.58.com測試

  首先想到的是對Url的重寫:(這在ASP.NET中也是經常使用的手法。網上有關於UrlRewrite的實現,這裏再也不重複。)網站

  還有就是MVC 應用程序中的典型URL模式,這裏只討論MVC應用程序URL模式下的動態二級域名實現,測試實例下載this

  1.定義DomainData、DomainRoute類url

  public class DomainRoute : Route
  {
  private Regex domainRegex;
  private Regex pathRegex;

  public string Domain { get; set; }

  public DomainRoute(string domain, string url, RouteValueDictionary defaults): base(url, defaults, new MvcRouteHandler())
  {
    Domain = domain;
  }

  public DomainRoute(string domain, string url, RouteValueDictionary defaults, IRouteHandler routeHandler): base(url, defaults, routeHandler)

  {
    Domain = domain;
  }

  public DomainRoute(string domain, string url, object defaults): base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
  {
    Domain = domain;
  }

  public DomainRoute(string domain, string url, object defaults, IRouteHandler routeHandler): base(url, new RouteValueDictionary(defaults), routeHandler)
  {
    Domain = domain;
  }

  public override RouteData GetRouteData(HttpContextBase httpContext)
  {
    // 構造 regex
    domainRegex = CreateRegex(Domain);
    pathRegex = CreateRegex(Url);
    // 請求信息
    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 domainMatch = domainRegex.Match(requestDomain);
    Match pathMatch = pathRegex.Match(requestPath);

    // 路由數據
    RouteData data = null;
    if (domainMatch.Success && pathMatch.Success)
    {
      data = new RouteData(this, RouteHandler);
      // 添加默認選項
      if (Defaults != null)
      {
        foreach (KeyValuePair<string, object> item in Defaults)
        {
          data.Values[item.Key] = item.Value;
        }
      }

      // 匹配域名路由
      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;
            }
          }
        }
      }

      // 匹配域名路徑
      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)
  {
    // 得到主機名
    string hostname = Domain;
    foreach (KeyValuePair<string, object> pair in values)
    {
      hostname = hostname.Replace("{" + pair.Key + "}", pair.Value.ToString());
    }

    // Return 域名數據
    return new DomainData
    {
      Protocol = "http",
      HostName = hostname,
      Fragment = ""
    };
  }

    private Regex CreateRegex(string source)
  {
    // 替換
    source = source.Replace("/", @"\/?");
    source = source.Replace(".", @"\.?");
    source = source.Replace("-", @"\-?");
    source = source.Replace("{", @"(?<");
    source = source.Replace("}", @">([a-zA-Z0-9_]*))");

    return new Regex("^" + source + "$", RegexOptions.IgnoreCase);
  }

  private RouteValueDictionary RemoveDomainTokens(RouteValueDictionary values)
  {
    Regex tokenRegex = new Regex(@"({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?");
    Match tokenMatch = tokenRegex.Match(Domain);
    for (int i = 0; i < tokenMatch.Groups.Count; i++)
    {
      Group group = tokenMatch.Groups[i];
      if (group.Success)
      {
        string key = group.Value.Replace("{", "").Replace("}", "");
        if (values.ContainsKey(key))
          values.Remove(key);
      }
    }

    return values;
   }
  }
  public class DomainData
  {
    public string Protocol { get; set; }
    public string HostName { get; set; }
    public string Fragment { get; set; }
  }

 

  2.修改RouteConfig,增長以下代碼spa

    routes.Add(
      "DomainRoute", new DomainRoute(
      "{CityNameUrl}.weiz.com",
      "{controller}/{action}/{id}",
      new { CityNameUrl = "", controller = "City", action = "Index", id = "" }
    ));

  

  3.增長CityController控制類code

public class CityController : Controller
  {
    public ActionResult Index()
    {
      var cityName = RouteData.Values["CityNameUrl"];
      ViewBag.CityName = cityName;
      return View();
    }
  }

 

  4.發佈網站,並修改相關配置
  方式一:修改host,咱們經過修改host文件,來實現對二級域名的,只能經過一個一個增長解析如:
    #host文件
    127.0.0.1 www.weiz.com
    127.0.0.1 a.weiz.com
    127.0.0.1 b.weiz.com
    127.0.0.1 c.weiz.com

  方式二:增長泛域名解析,配置DNS服務,也就是讓你的域名支持泛解析 (Windows Server 纔會有,其餘的Windows系統只能修改嘗試修改Host文件,便於測試) 請看個人另外一篇文章《域名泛解析設置

  5. 效果

  

   須要注意:若是你的服務器上有多個站點,則主站不要綁定主機頭。其餘二級域名的子系統,須要綁定主機頭。

   

相關文章
相關標籤/搜索