2013-12-18 31 views
1


我正在尝试从C#的Pascal格式重写URL到SEO友好的格式。
例如,我想是这样/User/Home/MyJumbledPageName看起来像这样:如何在MapRoute方法中一般实现URL重写?

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


有了这个代码,这样的URL /AboutTheAuthor转换成我想要的东西,这将是/about-the-author

看来我的方法调用被忽略了,这里发生了什么?传统的实现方式是什么?

回答

2

你必须定义自己的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 })) 
); 
+0

Thanks @LostInComputer!这对我来说有点远了,所以我一直在研究你的代码。我了解到您正在使用“空合并运算符”'''',其中三元运算符是速记。此外,我看到您正在使用在Route中调用'base'所需的构造函数参数,在Visual Studio中右键单击'Route'并选择“Peek Definition”。否则,你怎么知道重写'GetRouteData'和'GetVirtualPath'?框架中的哪些方法可以调用这些方法? –

+0

我有的其他问题是'GetActualValue'方法是这个逻辑的一个要求,哪里是把这个类放在一个项目中的好地方? –

+1

MVC调用'GetVirtualPath'来生成URL,就像调用'@ Html.Action'一样。 'GetRouteData'是相反的,它被MVC调用来从URL获取控制器动作。我怎么知道的?我有时会通过开源的MVC源代码。 – LostInComputer

2


作为参考,我已经找到了如何使@LostInComputer的代码工作地区也是如此。用于某个区域的类必须实现IRouteWithAreacontext.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参数中对其进行硬编码获得的。