最近有这么个需求:在一个站点上绑定多个域名,每个域名进去后都要进入不同的页面。实现了这个功能以后,对于有多个域名,且有虚拟空间,但是虚拟空间却只匹配有一个站点的用户来说,可以节省很多小钱钱。

很久以前看过《ASP.NET MVC 实现二级域名》和《ASP.NET MVC 使用二级域名来注册Area区域》这两篇文章,它们是有前后延续性的。本文的思路也是延续他们的思想来发展的,因此必须先了解前面两个文章的内容。而解决方法更是采用他们的代码加以改进实现的。在此谢谢两位作者。

1、简单实现多域名对单站点

自己创建一个MVC的站点,大体结构如下:

圈1、HomeController将对应www.demo.com域名的页面;WebController将对应www.web.com的页面

圈2、添加Test的Area将对应test.web.com的页面

圈3、是从上面两篇文章中扒过来的域名解析的类

然后,在global里面加上下面的语句,一个站点绑定多个域名的功能就实现了。问题肯定有,后面慢慢说。

routes.Add("demo", new DomainRoute("www.demo.com",                                     "{controller}/{action}/{id}",new { controller = "Home", action = "Index", id = "" } ));routes.Add("web", new DomainRoute("www.web.com",                                                    "{controller}/{action}/{id}",                                        new { controller = "Web", action = "Index", id = "" }   ));    

View Code

2、简单的原理说明

首先,使用自定义路由解析的入口

通常我们在增加自定义的路由规则时,都会用到MapRoute方法,通过"{controller}/{action}/{id}"或者"{controller}/{action}/{yyyy}/{mm}/{dd}"这样的路由规则串来自定义的路由规则。这时我们只定义了URL的path部分,并没有涉及到URL的host部分。

仔细看看代码,MapRoute方法返回了Route类型的对象,而routes是RouteCollection类型的对象,那么我们可以这么理解:MapRoute方法生成了一个新的路由对象(Route),并把它加入到了routes这个路由集合(RouteCollection)中。其实系统还提供一个Add方法,这个方法更直接,就是往routes里添加一个RouteBase对象。而Route类型实际是继承了RouteBase的。

到这里我们就明白了:注册一个路由新规则就是把一个新的RouteBase对象加入到路由集合中去。我们只要实现一个继承了RouteBase的类来实现对域名的解析,再把这个类型的对象加入到路由集合,就可以增加对域名的解析了。利用这个类和Add方法,我们可以扩展实现各种复杂的路由解析功能。上面例子中添加的DomainRoute类型的对象,实际就是一个解析域名的类。

其次,为了实现二级域名以及二级域名注册Area,还要准备一些基础知识

上面提到的RouteBase类是个抽象类,需要实现两个方法:GetRouteData和GetVirtualPath。简单的理解就是GetRouteData方法是解析url,把解析的结果按键值对存入RouteData中;而GetVirtualPath方法就是从RouteData中把url还原回来。

RouteData类里提供了两个存放路由键值对的地方,一个Values,一个DataTokens。那么哪些数据放Values里,哪些数据放DataTokens里呢?我们拿Route类来看,参数最全的构造函数如下:

public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler);

url:对应我们自己定义的路由规则串

defaults:路由规则串里解析出来的键的对应缺省值

大体上说defaults里的键值对会放到Values里,constraints和dataTokens这里不讨论,我觉得应该会放到DataTokens里,这个需要看源代码确定一下。感兴趣的同学可以自己去验证一下。实际过程中defaults会有点特殊。defaults里的“键”(键值对里的键)是和url中“{}”中的内容是一一对应的,例如:

url:"{controller}/{action}/{id}"
defaults:new { controller = "Web", action = "Index", id = "" } 

也就是说url规定有哪些键,defaults说明这些键对应的值是什么。其中,controller和action可以认为是类似保留字这样的通用键,通用键不能修改;而id则是自定义的键,自定义的键是可以自己设定和增加的,例如yyyy这些。这些从url里解析出来的键值对都会保存到RouteData.Values里。当站点项目里增加了Area之后,并在defaults中传入了area键值,area也可以认为是类似保留字这样的通用键,不但RouteData.Values里要保存,RouteData.DataTokens里也要保存。只有这样,路由才会正确解析area。另外Namespaces这样的约束键(constraints里的)也要保存到DataTokens里。

如果我们自定义的域名路由解析类,能很好的解析代表域名的路由规则串,并将对应的键值对保存到正确的RouteData中,注册到路由集合中后,就能正确进行域名解析了。域名解析类的具体实现请参考文章开头提到的两篇文章,我这里只说改进的地方。

3、二级域名注册Area的问题及解决方法

从上面的知识点得知,我们可以使用下面的域名规则串来进行二级域名的路由注册了:

{controller}.web.com -- 使用controller控制二级域名
{area}.web.com -- 使用area控制二级域名

      为了实现二级域名注册area,在上面简单实现的例子里增加一个路由规则来解析二级域名(注意路由名称不能重复):

routes.Add("areaforweb", new DomainRoute("{area}.web.com",                                                    "{controller}/{action}/{id}",                                        new { area = "Test", controller = "Page", action = "Index", id = "" }   ));

程序运行后,很好,完美的解决了对 test.web.com/Page/Index 路径的解析,可还有什么问题吗?

1、当我们进入二级域名的页面时,在使用ActionLink这样的方法生成二级域名的链接地址是可以的,但是要生成顶级域名下的链接地址就有问题了

2、同理,当我们在顶级域名的页面上要使用ActionLink这样的方法生成二级域名的链接地址也是有问题的

产生这些问题的原因在哪呢?我们得回过头去看看:

RouteBase类的GetVirtualPath方法是实现从RouteData到url的还原,而且它只还原url的path部分,不还原host的部分,如果传入了多余的host的参数,生成的path部分就会有问题。因此在我们自定义的DomainRoute类中,在实现GetVirtualPath方法时,需要把保存在RouteData中的域名解析出来的键值对给去掉,类里面是通过RemoveDomainTokens方法实现的。

在DomainRoute类中实现了对域名的解析,因此对应的我们还需要增加一个还原域名的方法,类里面是通过GetDomainData方法实现的。方法的原始实现方法是将域名规则串中用"{}"扩起来的部分的键在RouteData中找到其对应的值去替换。这就导致了我们上面1,2点问题的产生。

当我们在二级域名的页面,使用的是二级域名的解析规则,还原域名时,域名中“{area}”部分始终会被“test”代替,无法回到顶级域名的部分;而当我们在顶级域名的页面时,使用的是顶级域名的解析规则,还原域名时,即使传入了像 area=“test" 的值,因为域名串中没有“{}”括起来的部分,所以始终会返回www.web.com,而无法得到去二级域名的链接地址。

怎么解决呢?我想的是:顶级域名能不能用area="www"或者area=""来进行匹配?解析域名串,往Values和DataTokens写入数据是现成的,完全没有问题;我们需要修改的是GetDomainData方法,让它判断一下area="www"或者area=""时,还原时域名进行特殊的处理。修改后的方法如下:

public DomainData GetDomainData(RequestContext requestContext, RouteValueDictionary values){// 获得主机名string hostname = Domain;foreach (KeyValuePair<string, object> pair in values){if (pair.Key == "area" && string.IsNullOrEmpty(pair.Value.ToString())){hostname = hostname.Replace("{" + pair.Key + "}", "www");}else{hostname = hostname.Replace("{" + pair.Key + "}", pair.Value.ToString());}}//如果域名的area还没有被替换,说明路由数据里没有area,恢复为顶级域名if (hostname.Contains("{area}")){hostname = hostname.Replace("{area}", "www");}RemoveDomainTokens(values);// Return 域名数据return new DomainData{Protocol = "http",HostName = hostname,Fragment = ""};}

View Code

我们还需要一个重写的生成链接的ActionLink方法,这个方法在LinkExtensions类里,是HtmlHelper的一个自定义的扩展方法,修改后的方法如下:

public static MvcHtmlString 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;//如果不需要变换area,则routeValues不传入area的值if (routeValues.Keys.Contains("area")){routeData.Values["area"] = routeValues["area"];                 }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);}

View Code

一般我们生成的链接地址在需要跳转到其他area的时候,才在routeValues参数中传入area的值,否则不需要传入。另外,requireAbsoluteUrl参数对应是否需要生成带域名的地址,即绝对地址。调用方式如下:

@Html.ActionLink("前往", "Index", "Page", new { area = "Test" }, null, true)

另外,我将原始方法的返回值从string改成了MvcHtmlString。否则返回值是string时,还得加上一层壳:

@Html.Raw(@Html.ActionLink("前往", "Index", "Page", new { area = "Test" }, null, true))

至此,我们的问题基本上都解决了。在global中我们仅需要注册一个路由即可,默认值是顶级域名的首页。在项目的Area部分的TestAreaRegistration的RegisterArea方法中,注册area路由的内容就可以注销了。

routes.Add("web", new DomainRoute("{area}.web.com",                                                    "{controller}/{action}/{id}",                                        new { area = "", controller = "Web", action = "Index", id = "" }   ));

对于本机调试来说,不但需要设置hosts文件,而且在IIS站点上,需要把顶级域名和二级域名都绑定到站点上,如下图:

这里是源代码下载。

最后再说一下:在我的虚拟空间里,站点上只能绑定顶级域名,空间提供商限制了只能输入除www之外的域名部分。虽然可以用www.test.web.com这样的域名来实现二级域名,可实在有点不符合使用习惯。而且二级子域名也只能绑定三个。我哭啊……果然买的不如卖的精……

最后的最后,对于这个功能其实老赵也有一系列的文章来进行解说和实现,给个地址:http://www.cnblogs.com/JeffreyZhao/archive/2009/08/25/url-routing-with-domain.html,从这个地址里可以把整个系列看完。他的实现方式更偏向于使用MVC的源代码进行实现,相对优雅不少。非常赞。大家也可以去研究一下。

转载于:https://www.cnblogs.com/Showshare/p/multidomain-to-onesite-subdomain-to-area-by-routing.html

MVC利用Routing实现多域名绑定一个站点、二级域名以及二级域名注册Area相关推荐

  1. 教大家多个域名绑定一个空间的解决办法,原创,自己已经测试过了,完全可以绕过杀毒软件,以及空间商.

    一个空间放多个站的办法,多个域名绑定一个空间,域名解析问题的解决办法...创梦原创 技术qq交流群:JavaDream:251572072 教程下载,在线交流:创梦IT社区: www.credream ...

  2. 利用Linux系统搭建网站(含相关,域名申请,服务器选择,Java JDK安装,数据库配置,域名绑定服务器,服务器绑定域名),最终实现通过域名访问你的网站项目,创作不易,如果对你有帮助的还请点赞支持

    搭建之前的理论知识 先来简略说一下什么是域名,什么是服务器 域名(英语:Domain Name),又称网域,是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时 ...

  3. linux 下tomcat配置多域名访问怎么访问到的是一个站点,linux 配置多个子域名映射到tomcat中不同项目...

    5.配置域名指向的目录文件 1)appBase=""webapps",这个不变:name设置成你想要的域名,比如www.baidu.com(这是不可能的,还是换成你自己的 ...

  4. 自动检测更新域名绑定技术

    一.背景介绍 当前网络处于校内网环境,登录外网需要学生账号,中间有小米路由.不同时间登录可能会出现ip地址更换.(当前路由下有两台电脑和一台nano) 要实现的目标:将个人路由下的设备用二级域名绑定, ...

  5. Nginx技巧:灵活的server_name,Nginx配置一个服务器多个站点 和 一个站点多个二级域名...

    http://www.cnblogs.com/buffer/archive/2011/08/17/2143514.html Nginx强大的正则表达式支持,可以使server_name的配置变得很灵活 ...

  6. 网站更换域名和服务器不收录,更换域名就是一个坑,没事千万不要轻易入坑

    懿古今是从14年11月开始建立,至今已将两年的时间了.时间虽然不长,但是两年间却更换了两次域名总共用过三个域名,真的是"折腾域名专业户"啊!这次更换域名让我觉得:更换域名就是一个坑 ...

  7. 第一次创业做网站,公司企业如何选择一个合适自己又好的域名?

    现如今已经是互联网+移动互联网的时代,互联网上每天的网站数量与日剧增,那么搭建一个网站肯定离不开域名,一个好记还用的域名或多或少可以推动企业业务的发展,下面,IT工作室创始人王晴儿本人根据多年的互联网 ...

  8. APACHE如何里一个站点绑定多个域名?用ServerAlias

    APACHE2如何里一个站点绑定多个域名?用ServerAlias 以前很笨,要使多个域名指向同一站点总是这样写: <VirtualHost *:80> ServerAdmin i@kui ...

  9. Shopify:管理一个顶级域名绑定shopify网店

    你可以进入你的域名管理界面- Domains page: 登陆你的shopify 管理员账户,, 点击 Online Store: Shopify:管理一个顶级域名绑定shopify网店 然后点击 D ...

最新文章

  1. rbac 一个用户对应多个账号_电商后台系统:管理后台之账号管理(一)
  2. 【转】Failed to load module for FS type ‘bdb’ in TortoiseSVN 1.6.x
  3. linux 安装系统划分lvm分区
  4. php 获取js对象的属性值,js获取对象,数组所有属性键值(key)和对应值(value)的方法示例...
  5. Javaweb MVC设计模式、Modle发展史、项目分层和三层架构
  6. C# 中执行 msi 安装
  7. oracle的ocm怎么算通过,Oracle 11g OCM的通过祝贺信
  8. 面向项目(六)—— 错误(异常)信息的书写
  9. Fortran入门教程(八)——子例程及函数
  10. LTspice基础教程-004.系统自带仿真库介绍
  11. Springboot使用J2Cache,整合ehcache和redis缓存框架,实现两级缓存
  12. 第三方服务挂了,如何保证服务不受影响?
  13. 手机定位浅析 AGPS定位 LBS基站定位 卫星定位
  14. 中关村买手机上当案例
  15. DDCTFRre3-虚拟指令分析
  16. Spring Boot -- JPA
  17. Java内置包装类:Float 类
  18. 爬出某电影网站上电影下载地址的简单爬虫程序
  19. 使用NTP协议获取网络时间代码
  20. u8出现向服务器传输错误_分享,HTTP协议错误代码大全

热门文章

  1. 算法竞赛从入门到进阶pdf_【算法趣谈】线段树 从入门到进阶
  2. python中的reg_如何在python中从注册表读取字符串格式的Reg_二进制类型值
  3. vmWare中安装centos7
  4. AndroidStuido连接不上手机的解决方法
  5. vb listview扁平表头的列表框
  6. 面试指南(一):想跳槽,你真的做好准备了吗?
  7. 特斯拉又熬过破产危机后,马斯克说自动驾驶芯片已经准备好了
  8. 何小鹏:从工程师思维到用户思维,这是互联网造车将带来的变革
  9. 网络协议 反扒机制 fidder 抓包工具
  10. Git系列(七):使用Git管理二进制大对象