我一般如何在 MapRoute 方法中实现 URL 重写?
Posted
技术标签:
【中文标题】我一般如何在 MapRoute 方法中实现 URL 重写?【英文标题】:How do I generically implement URL-rewriting in a MapRoute method? 【发布时间】:2014-01-06 05:57:59 【问题描述】:
我正在尝试将 URL 从 C# 的 Pascal-case 重写为对 SEO 友好的格式。
例如,我希望像 /User/Home/MyJumbledPageName
这样的东西看起来像这样:
/user/home/my-jumbled-page-name // lower-case, and words separated by dashes
这是我在 URL 中转换每个“令牌”的方法:
public static string GetSEOFriendlyToken(string token)
StringBuilder str = new StringBuilder();
for (int i = 0, len = token.Length; i < len; i++)
if (i == 0)
// setting the first capital char to lower-case:
str.Append(Char.ToLower(token[i]));
else if (Char.IsUpper(token[i]))
// setting any other capital char to lower-case, preceded by a dash:
str.Append("-" + Char.ToLower(token[i]));
else
str.Append(token[i]);
return str.ToString();
...在我的 RouteConfig.cs
根文件中,我映射了这些路由:
public static void RegisterRoutes(RouteCollection routes)
routes.IgnoreRoute("resource.axd/*pathInfo");
// without this the first URL is blank:
routes.MapRoute(
name: "Default_Home",
url: "index", // hard-coded?? it works...
defaults: new controller = "Home", action = "Index", id = UrlParameter.Optional
);
routes.MapRoute(
name: "Home",
// the method calls here do not seem to have any effect:
url: GetSEOFriendlyToken("action") + "/" + GetSEOFriendlyToken("id"),
defaults: new controller = "Home", action = "Index", id = UrlParameter.Optional
);
使用此代码,/AboutTheAuthor
等 URL 未 转换为我想要的,即 /about-the-author
。
似乎我的方法调用被忽略了,这里发生了什么?实现这一点的传统方法是什么?
【问题讨论】:
【参考方案1】:你必须定义你自己的RouteBase
类或子类Route
public class SeoFriendlyRoute : Route
private readonly string[] _valuesToSeo;
public SeoFriendlyRoute(string url, RouteValueDictionary defaults, IEnumerable<string> valuesToSeo, RouteValueDictionary constraints = null, RouteValueDictionary dataTokens = null, IRouteHandler routeHandler = null)
: base(url, defaults, constraints ?? new RouteValueDictionary(), dataTokens ?? new RouteValueDictionary(), routeHandler ?? new MvcRouteHandler())
if (valuesToSeo == null) throw new ArgumentNullException("valuesToSeo");
_valuesToSeo = valuesToSeo.ToArray();
public override RouteData GetRouteData(HttpContextBase httpContext)
var routeData = base.GetRouteData(httpContext);
if (routeData != null)
foreach (var key in _valuesToSeo)
if (routeData.Values.ContainsKey(key))
routeData.Values[key] = GetActualValue((string)routeData.Values[key]);
return routeData;
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
var seoFriendyValues = new RouteValueDictionary(values);
foreach (var key in _valuesToSeo)
if (seoFriendyValues.ContainsKey(key))
seoFriendyValues[key] = GetSeoFriendlyValue((string)seoFriendyValues[key]);
return base.GetVirtualPath(requestContext, seoFriendyValues);
private string GetSeoFriendlyValue(string actualValue)
//your method
StringBuilder str = new StringBuilder();
for (int i = 0, len = actualValue.Length; i < len; i++)
if (i == 0)
str.Append(Char.ToLower(actualValue[i]));
else if (Char.IsUpper(actualValue[i]))
str.Append("-" + Char.ToLower(actualValue[i]));
else
str.Append(actualValue[i]);
return str.ToString();
private static string GetActualValue(string seoFriendlyValue)
//action name is not case sensitive
//one limitation is the dash can be anywhere but the action will still be resolved
// /my-jumbled-page-name is same as /myjumbled-pagename
return seoFriendlyValue.Replace("-", string.Empty);
用法
routes.Add("Default", new SeoFriendlyRoute(
url: "controller/action/id",
valuesToSeo: new string[] "action", "controller" ,
defaults: new RouteValueDictionary(new controller = "Home", action = "Index", id = UrlParameter.Optional ))
);
【讨论】:
感谢@LostInComputer!这对我来说有点远,所以我一直在研究你的代码。我了解到您正在使用“空合并运算符”??
,它是三元运算符的简写。此外,我看到您正在使用在 Route
上调用 base
所需的构造函数参数,在 Visual Studio 中通过右键单击 Route
并选择“Peek Definition”找到。否则,你怎么知道要覆盖GetRouteData
和GetVirtualPath
?这些方法在框架中的什么位置被调用?
我的其他问题是 GetActualValue
方法是此逻辑的要求,以及将此类放在项目中的好地方在哪里?
GetVirtualPath
被 MVC 调用以生成 URL,就像调用 @html.Action
时一样。 GetRouteData
是相反的,它由 MVC 调用以从 URL 获取控制器操作。我怎么知道的?我有时会浏览开源的 MVC 源代码。
好的,所以我现在看到GetActualValue
方法是 要求——重写的GetRouteData
方法需要任何“子目录”标记 破折号以便 MVC 可以在 RouteData
中找到相应的控制器和/或操作。此外,根据代码状态中的 cmets,MVC 匹配此类“子目录”字符串不区分大小写。
是的。我有点被骗了,因为GetActualValue
只是删除了所有破折号,无论位置如何,所以/index-dash
、/indexd-ash
和/indexdash
都被路由到相同的操作。【参考方案2】:
作为参考,我发现了如何使 @LostInComputer 的代码也适用于区域。用于区域的类必须实现IRouteWithArea
,以便context.Routes.Add
在区域的RegisterArea
方法中工作。
这是一个可用于区域的通用类(它扩展了上述SEOFriendlyRoute
类):
public class AreaSEOFriendlyRoute : SEOFriendlyRoute, IRouteWithArea
private readonly string _areaName;
// constructor:
public AreaSEOFriendlyRoute(string areaName, string url, RouteValueDictionary defaults, IEnumerable<string> valuesToSeo,
RouteValueDictionary constraints = null, RouteValueDictionary dataTokens = null, IRouteHandler routeHandler = null)
: base(url, defaults, valuesToSeo, constraints, dataTokens, routeHandler)
this._areaName = areaName;
// implemented from IRouteWithArea:
public string Area
get
return this._areaName;
...及其用法:
public override void RegisterArea(AreaRegistrationContext context)
context.Routes.Add("Example", new AreaSEOFriendlyRoute(
areaName: this.AreaName,
url: "my-area/action/id",
valuesToSeo: new string[] "action", "controller" ,
defaults: new RouteValueDictionary(new controller = "MyController", action = "MyDefaultPage", id = UrlParameter.Optional ))
);
请注意,我在调用context.Routes.Add
时传递了一个额外的参数,该参数定义为areaName
。构造函数中的同名形参用于从Area
方法返回这个值,该方法是从IRouteWithArea
实现的。
所以一个链接,如:
@Html.ActionLink("my link text", "MyJumbledPageName", "MyController",
new area = "MyArea" , null)
...将导致 URL my-area/my-jumbled-page-name
。另请注意,url 中前面的“my-area
”是通过在路由的url
参数中硬编码获得的。
【讨论】:
以上是关于我一般如何在 MapRoute 方法中实现 URL 重写?的主要内容,如果未能解决你的问题,请参考以下文章