3

I'm trying to implement my own route class, inheriting from the default Route.

This is what my custom route class looks like:

public class FriendlyRoute : Route
{
    public FriendlyRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
        : base(url, defaults, routeHandler)
    {

    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var routeData = base.GetRouteData(httpContext);

        var controllerName = routeData.Values["controller"].ToString();
        var actionName = routeData.Values["action"].ToString();

        routeData.Values["controller"] = fix(controllerName);
        routeData.Values["action"] = fix(actionName);

        return routeData;
    }

    private string fix(string name)
    {
        //Remove dashes: "my-controller" => "mycontroller"
    }
}

What I'm doing is accepting urls with dashes and routing the to the correct action ("my-controller/my-action" to "MyController/MyAction"), but I have some more plans for this custom route to.

To put my custom route class in action, I use the following route config:

routes.Add("Default",
           new FriendlyRoute("{controller}/{action}/{id}",
                             new RouteValueDictionary(new { controller = "Public", action = "Start", id = UrlParameter.Optional }),
                             new MvcRouteHandler()));

This works fine! But I'm not happy with the url structure. I want to have some urls with no controller names only action names (e.g. "/about", "/contact") and some with controller names (e.g. "/mypage", "/mypage/invoices"). I start by using the default route class (not my own custom) and fix this problem:

routes.Add("MyPages",
           new Route("MyPage/{action}",
                     new RouteValueDictionary(new { controller = "MyPage", action = "Summary"}),
                     new MvcRouteHandler()));

routes.Add("Public",
           new Route("{action}/{id}",
                     new RouteValueDictionary(new { controller = "Public", action = "Start", id = UrlParameter.Optional }),
                     new MvcRouteHandler()));

This also works fine, but now there's no support for urls with dashes. So I just swap in my custom route class into the route config:

routes.Add("MyPages",
           new FriendlyRoute("MyPage/{action}",
                             new RouteValueDictionary(new { controller = "MyPage", action = "Summary" }),
                             new MvcRouteHandler()));

routes.Add("Public",
           new FriendlyRoute("{action}/{id}",
                             new RouteValueDictionary(new { controller = "Public", action = "Start", id = UrlParameter.Optional }),
                             new MvcRouteHandler()));

Now when I run the application I try to go to the default page ("/") it crashes because the call to base.GetRouteData(httpContext) in my FriendlyRoute.GetRouteData() returns null.

I'm all new to creating a custom route class, so any hints on what I' doing wrong would be appreciated.

4

1 回答 1

2

After adding the additional two routes, when the root url is hit ("/") it will be processed against route definitions sequentially top to bottom until a match is made. So now for the first route "MyPage/{action}" it will call the public abstract RouteData GetRouteData(HttpContextBase httpContext); method, which in turn matches the url with the route definition and check for constraints. It returns a RouteValueDictionary object in case of match and constraint check, null otherwise. So for the first route definition it is bound to return null as the url does not match. You should add a null check as foollowing

if (routeData != null)
{
    var controllerName = routeData.Values["controller"].ToString();
    var actionName = routeData.Values["action"].ToString();

    routeData.Values["controller"] = Fix(controllerName);
    routeData.Values["action"] = Fix(actionName);
}

relevant resource: Route.cs

hope this helps.

于 2013-04-22T18:49:21.300 回答