MVC项目不同域之间的UrlRouting
一、DomainAction,方便生成不同域下的url
1、新建3个MVC项目,一个公用类库WebCore
Demo.WebApplication0
绑定域名 www.demo.com demo.com t0.demo.com
Demo.WebApplication1(可不用建)
绑定域名 t1.demo.com
Demo.WebApplication2(可不用建)
绑定域名 t2.demo.com
Demo.WebCore
2、项目Demo.WebApplication0新增一个学生的Controller
public class StudentController : Controller
{
public ActionResult Search(int gender, int degree, int pageSize, int page)
{
return Content(string.Format("查找学生,筛选条件。性别:{0},学历:{1},每页学生数量:{2},页码:{3}"
, (gender == ? "男" : (gender == ? "女" : "不限"))
,degree, pageSize, page
));
//return View();
} }
3、域名枚举
public enum DemoDomain
{
/// <summary>
/// www.demo.com
/// demo.com
/// t0.demo.com
/// </summary>
T0, /// <summary>
/// t1.demo.com
/// </summary>
T1, /// <summary>
/// t2.demo.com
/// </summary>
T2, /// <summary>
/// demo.com
/// </summary>
Default, }
4、System.Web.Mvc.UrlHelper的静态扩展类DemoUrlExtensions
public static class DemoUrlExtensions
{
public static string DomainAction(this UrlHelper urlHelper, string actionName, string controllerName)
{
return urlHelper.DomainAction(actionName, controllerName, DemoDomain.Default);
} public static string DomainAction(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues)
{
return urlHelper.DomainAction(actionName, controllerName, routeValues, DemoDomain.Default);
} public static string DomainAction(this UrlHelper urlHelper, string actionName, string controllerName, DemoDomain Domain)
{
return urlHelper.DomainAction(actionName, controllerName, new { }, Domain);
} public static string DomainAction(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues, DemoDomain Domain)
{
return DomainAction(urlHelper, actionName, controllerName, new RouteValueDictionary(routeValues), Domain);
} public static string DomainAction(this UrlHelper urlHelper, string actionName, string controllerName, RouteValueDictionary routeValues, DemoDomain Domain)
{
string subDomain = string.Empty;
string HostName = string.Empty;
switch (Domain)
{
case DemoDomain.Default:
subDomain = string.Empty;
break;
case DemoDomain.T0:
subDomain = "t0";
break;
case DemoDomain.T1:
subDomain = "t1";
break;
case DemoDomain.T2:
subDomain = "t2";
break;
default:
subDomain = string.Empty;
break;
} if (!string.IsNullOrEmpty(subDomain))
{
HostName = subDomain + ".demo.com";
}
else
{
HostName = "www.demo.com";
}
//核心的一行
string url = urlHelper.Action(actionName, controllerName, routeValues, "http", HostName);
if (url.Contains('?'))
{
url = url.TrimEnd('/');
}
else
{
if (url.EndsWith(".html/"))
{
url = url.TrimEnd('/');
}
}
return url;
} }
5、修改Home/Index内容
<div>
<a href="@Url.DomainAction("Index","Home", DemoDomain.T1)">我要去t1.demo.com</a>
<a href="@Url.DomainAction("Index","Home", DemoDomain.T2)">我要去t2.demo.com</a>
<a href="@Url.DomainAction("Index", "Home", new { from = "www" }, DemoDomain.T2)">我要去t2.demo.com,我要带参数,表示我是从www过来的</a> <a href="@Url.DomainAction("Search", "Student", new { gender = 1, degree = 0, pageSize = 20, page = 1 }, DemoDomain.T0)">查找性别男的学生</a> </div>
对应的html源码
<div>
<a href="http://t1.demo.com/">我要去t1.demo.com</a>
<a href="http://t2.demo.com/">我要去t2.demo.com</a>
<a href="http://t2.demo.com/?from=www">我要去t2.demo.com,我要带参数,表示我是从www过来的</a> <a href="http://t0.demo.com/Student/Search?gender=1&degree=0&pageSize=20&page=1">查找性别男的学生</a> </div>
二、注册路由,SEO优化
现在已经能做到
@Url.DomainAction("Search", "Student", new { gender = , degree = , pageSize = , page = }, DemoDomain.T0)
对应的Url为
http://t0.demo.com/Student/Search?gender=1°ree=0&pageSize=20&page=1
,但这个链接不利于SEO,理想的情况可能是这样(请把t0当成student)
http://t0.demo.com/Search/1-0-20-1.html
定义路由
public class DomainRoute : Route
{
private static 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_]*})*-?\.?\/?", RegexOptions.Compiled);
private static Dictionary<string, Regex> domainRegDic = new Dictionary<string, Regex>();
private static Dictionary<string, Regex> pathRegDic = new Dictionary<string, Regex>(); /// <summary>
///
/// </summary>
public Regex PathRegex
{
get
{
if (!pathRegDic.Keys.Contains(Url))
{
switch (Url)
{
case WebCoreConsts.URL_T0_SearchStudent:
pathRegDic[Url] = new Regex(ConvertToPattern("search/{gender}-{degree}-{pagesize}-{page}.html"), RegexOptions.Compiled | RegexOptions.IgnoreCase);
break;
default:
pathRegDic[Url] = CreateRegex(Url);
break;
}
}
return pathRegDic[Url];
}
} /// <summary>
///
/// </summary>
public Regex DomainRegex
{
get
{
if (!domainRegDic.Keys.Contains(Domain))
{ domainRegDic[Domain] = CreateRegex(Domain);
}
return domainRegDic[Domain];
} } 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;
} //new
public DomainRoute(string domain, string url, object defaults, object constraints)
: base(url, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), 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)
{
// 请求信息
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)
{ VirtualPathData pathData = base.GetVirtualPath(requestContext, RemoveDomainTokens(values));
if (pathData != null && !string.IsNullOrEmpty(pathData.VirtualPath))
{
if (pathData.VirtualPath.EndsWith(".html/"))
{
pathData.VirtualPath = pathData.VirtualPath.TrimEnd('/');
}
else if (!pathData.VirtualPath.EndsWith("/") && !pathData.VirtualPath.EndsWith(".html"))
{
pathData.VirtualPath = pathData.VirtualPath + "/";
}
}
return pathData;
} private Regex CreateRegex(string source)
{
try
{
string src = 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.Compiled);
}
catch (Exception ex)
{
//AppLogger.Error("创建正则出错:" + source + " 详情:" + ex.Message);
}
return new Regex("^" + source + "$", RegexOptions.Compiled);
} private RouteValueDictionary RemoveDomainTokens(RouteValueDictionary values)
{ 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;
} private static string ConvertToPattern(string source)
{
try
{
string src = source;
// 替换
source = source.Replace("/", @"\/?");
source = source.Replace(".", @"\.?");
source = source.Replace("-", @"\-?");
source = source.Replace("{", @"(?<");
source = source.Replace("}", @">([a-zA-Z0-9_]*))");
return "^" + source + "$";
}
catch (Exception ex)
{
}
return source;
} }
注册路由
routes.LowercaseUrls = true; routes.Add("T0StudentSearchDomainRoute", new DomainRoute(
"t0.demo.com", // Domain with parameters
WebCoreConsts.URL_T0_SearchStudent, // URL with parameters
new { controller = "Student", action = "Search" }
//, new { }
));
可能还需要在web.config的system.webServer项中增加一行配置,否则.html不会进入路由,会直接映射成本地路径
<modules runAllManagedModulesForAllRequests="true" />
这时候
<a href="@Url.DomainAction("Search", "Student", new { gender = 1, degree = 0, pageSize = 20, page = 1 }, DemoDomain.T0)" target="_blank">查找性别男的学生</a>
就会对应成
<a href="http://t0.demo.com/search/1-0-20-1.html" target="_blank">查找性别男的学生</a>
附:源码下载
MVC项目不同域之间的UrlRouting的更多相关文章
- Mvc中域的添加和不同域之间的跳转
一.在新添加的域中中的 AreaRegistration中作如下设置: 二.在原来的Global.asax中设置: 三.不同域之间的跳转 @Url.Action("Index", ...
- MVC项目实践,在三层架构下实现SportsStore-06,实现购物车
SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...
- 基于MVC4+EasyUI的Web开发框架经验总结(15)--在MVC项目中使用RDLC报表
RDLC是一个不错的报表,有着比较不错的设计模式和展现效果,在我的Winform开发里面,使用RDLC也是一个比较方便操作,如可以参考文章<DevExpress的XtraReport和微软RDL ...
- 1.2 认识ASP.NET MVC项目结构
1.开发环境 操作系统:xp.vista.windows 7.windows 8.windows server 2003|2008|2008R2|2012: 集成开发环境IDE: Vsiual Stu ...
- 在MVC项目中使用RDLC报表
原文地址:http://www.cnblogs.com/wuhuacong/p/4109833.html RDLC是一个不错的报表,有着比较不错的设计模式和展现效果,在我的Winform开发里面,使用 ...
- spring mvc的跨域解决方案
什么是跨域 一句话:同一个ip.同一个网络协议.同一个端口,三者都满足就是同一个域,否则就是跨域. 为什么非得跨域 基于两个方面: a. web应用本身是部署在不同的服务器上 b.基于开发的角度 -- ...
- 在已有的Asp.net MVC项目中引入Taurus.MVC
Taurus.MVC是一个优秀的框架,如果要应用到已有的Asp.net MVC项目中,需要修改一下. 1.前提约定: 走Taurus.MVC必须指定后缀.如.api 2.原项目修改如下: web.co ...
- .NET Core实战项目之CMS 第十三章 开发篇-在MVC项目结构介绍及应用第三方UI
作为后端开发的我来说,前端表示真心玩不转,你如果让我微调一个位置的样式的话还行,但是让我写一个很漂亮的后台的话,真心做不到,所以我一般会选择套用一些开源UI模板来进行系统UI的设计.那如何套用呢?今天 ...
- 使用JavaConfig和注解方式实现零xml配置的Spring MVC项目
1. 引言 Spring MVC是Spring框架重要组成部分,是一款非常优秀的Web框架.Spring MVC以DispatcherServlet为核心,通过可配置化的方式去处理各种web请求. 在 ...
随机推荐
- bzoj 5334 数学计算
bzoj 5334 数学计算 开始想直接模拟过程做,但模数 \(M\) 不一定为质数,若没有逆元就 \(fAKe\) 掉了. 注意到操作 \(2\) 是删除对应的操作 \(1\) ,相当于只有 \(1 ...
- BZOJ1131 POI2008 Sta 【树形DP】
BZOJ1131 POI2008 Sta Description 给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大 Input 给出一个数字N,代表有N个点.N<=10 ...
- 在制作跨平台的 NuGet 工具包时,如何将工具(exe/dll)的所有依赖一并放入包中
NuGet 提供了工具类型的包支持,生成一个基于 .NET Core 的 dll 或者基于 .NET Framework 的 exe 之后,你几乎可以对项目做任何事情.但是,默认情况下,NuGet 不 ...
- flex 伸缩布局
伸缩布局 布局的传统解决方案,基于盒状模型,依赖 display属性 + position属性 + float属性.它对于那些特殊布局非常不方便.CSS3在布局方面做了非常大的改进,使得我们对块级元素 ...
- C++中const使用注意要点(二)
当const修饰类的成员变量 1.const修饰类的非静态成员时必须在构造函数初始化列表上初始化: 在构造函数内会提示表达式必须是可修改的左值,因为在构造函数内并不是初始化,仅仅是赋值,而const类 ...
- SPUtils
public class SPUtils { /** * 保存在手机里的SP文件名 */ public static final String FILE_NAME = "my_sp" ...
- 科普Spark,Spark是什么,如何使用Spark
科普Spark,Spark是什么,如何使用Spark 1.Spark基于什么算法的分布式计算(很简单) 2.Spark与MapReduce不同在什么地方 3.Spark为什么比Hadoop灵活 4.S ...
- Centos6-7安装Python3.5以及SSL的编译安装,识别https
Python3中无法导入ssl模块的解决办法 如果你发现在python3脚本运行过程中发现涉及到ssl模块都无法运行的情况下.那么需要进行如下步骤 第一步: yum install openssl o ...
- 第六章 通过Service访问Pod(上)
不应该直接使用Pod的ID地址作为对外提供服务的接口,应为一旦Pod重启,IP地址就变化了,解决方案是使用Service. 6.1 创建Service K8s service从逻辑上代表了一组Pod, ...
- Code blocks调试教程
特别声明:本教程已转移至百度经验:https://jingyan.baidu.com/article/6b182309939a87ba58e159bf.html 一.题外话 之前一直想用Code bl ...