如何在MapRoute方法中一般地实现URL重写?

时间:2013-12-18 07:05:46

标签: c# url-rewriting seo asp.net-mvc-routing


我试图将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等网址转换为我想要的内容,即/about-the-author

看来我的方法调用被忽略了,这里发生了什么?实现这个的常规方法是什么?

2 个答案:

答案 0 :(得分: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 }))
);

答案 1 :(得分: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) 中实现。

<小时/> 所以链接如:

my-area/my-jumbled-page-name


...会产生网址my-area。另请注意,url中的前面的“url”是通过在路由的{{1}}参数中对其进行硬编码来获得的。