扩展URL Routing:处理URL中的日期参数

对URL中的日期参数,老赵提供了一个非常经典的解决方案。但是这里的日期参数是以yyyy-MM-dd的形式出现的,用户往往会有这样的需求,即URL中的参数一定要为Demo/Date/2009/06/05的形式。由于RouteData是以/来划分segment的,老赵的方案似乎就不适用了。

国外的大牛们大多推崇将日期分隔为年、月、日,通过扩展IRouteConstraint来对年月日分别进行判断。引用老赵的话来说,“无论从易用性还是通用性等角度来看,这种做法都是下下之策。说实话,这样的做法其实并没有跳出框架既有功能给定的圈子,它只是通过“迎合框架”来满足自己的需求,而不是让框架为我们的需求服务。”

其实要实现这样的需求也不难,只需要对老赵的方案稍微修改一下就可以了。我们将URL定义为Demo/Date/{year}/{month}/{day},然后在生成RouteData时,将year、month、day组合成date;生成路径时,再将date划分为year、month、day。

新建一个DateFormatRoute类,让它继承自RouteBase。在GetRouteData方法中拼接年月日,其余方法与老赵的相同。

public override RouteData GetRouteData(HttpContextBase httpContext)
{
    var routeData = route.GetRouteData(httpContext);
    if (routeData.Values["year"] != null)
    {
        string date = routeData.Values["year"].ToString() + routeData.Values["month"].ToString() + routeData.Values["day"].ToString();
        routeData.Values.Remove("year");
        routeData.Values.Remove("month");
        routeData.Values.Remove("day");
        routeData.Values["date"] = date;
    }
    else
    {
        return null;
    }

    var valuesModified = new Dictionary<string, object>();
    foreach (var pair in routeData.Values)
    {
        var key = pair.Key;
        IRouteFormatter formatter = null;
        if (this.formatters.TryGetValue(key, out formatter))
        { 
            object o;
            if (formatter.TryParse(pair.Value, out o))
            {
                valuesModified[key] = o;
            }
            else
            {
                return null;
            }
        }
    }

    foreach (var pair in valuesModified)
    {
        routeData.Values[pair.Key] = pair.Value;
    }
    return routeData;
}

在GetVirtualPath方法中,再将年月日分解到各个segment中。

public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
    var routeValues = new RouteValueDictionary();
    foreach (var pair in values)
    {
        var key = pair.Key;
        IRouteFormatter formatter = null;
        if (this.formatters.TryGetValue(key, out formatter))
        {
            string path;
            if (formatter.TryToString(pair.Value, out path))
            {
                routeValues[key] = path;
            }
            else
            {
                return null;
            }
        }
        else
        {
            routeValues[key] = pair.Value;
        }
    }
    if (routeValues["date"] != null)
    {
        string s = routeValues["date"].ToString();
        routeValues.Remove("date");
        routeValues["year"] = s.Substring(0, 4);
        routeValues["month"] = s.Substring(4, 2);
        routeValues["day"] = s.Substring(6, 2);
        
    }

    return route.GetVirtualPath(requestContext, routeValues);
}

最后,修改Global.asax

routes.Add(
    "Demo.Date",
    new DateFormatRoute(
        "{controller}/{action}/{year}/{month}/{day}",
        new RouteValueDictionary(), // defaults
        new Dictionary<string, IRouteFormatter>
        {
            {"controller", new RegexFormatter("Demo")},
            {"action", new RegexFormatter("Date")},
            {"date", new DateTimeFormatter("yyyyMMdd")}
        },
        new RouteValueDictionary(), // constaints
        new RouteValueDictionary(), // data tokens
        new MvcRouteHandler()));

打开浏览器测试一下,URL和链接都显示正常。

PS:对于URL Routing的扩展,实在是想找一个别的例子来进行探讨。但稍微复杂一点的类型也不可能通过URL体现出来,而稍微简单一点的,用正则表达式就可以约束了。所以最后只要生硬的来修改老赵的示例了……

也可能是我的思路还没有打开,大家有没有什么比较好的思路?欢迎在这里讨论。

posted @ 2009-06-05 14:45  麒麟.NET  阅读(3850)  评论(3编辑  收藏  举报