views:

3747

answers:

5

In the StackOverflow Podcast #54, Jeff mentions they register their URL routes in the StackOverflow codebase via an attribute above the method that handles the route. Sounds like a good concept (with the caveat that Phil Haack brought up regarding route priorities).

Could someone provide some sample to to make this happen?

Also, any "best practices" for using this style of routing?

+33  A: 
DSO
I guess doing so with attributes prevents to use route defaults and route constraints...
Nicolas Cadilhac
With this approach I've never needed default routes because you're binding each route to a specific method. You are right about constraints. I looked into being able to add constraints as an attribute property, but ran into a snag in that MVC constraints are specified using anonymous objects and attribute properties can only be simple types. It is still possible I think to do constraints as an attribute (with more coding), but I haven't bothered with it yet because I haven't really needed constraints in my MVC work up to this point (I tend to validate route values in the controller).
DSO
I meant defaults for the items in the route path like {username} but I also realize that they are usually primitives.
Nicolas Cadilhac
Just updated with some additions I made to support route ordering, route parameter constraints and default values.
DSO
Well done, and thank you. On my side I was also playing with it. A version of the UrlRouteParameterConstraintAttribute could also take the type of a IRouteConstraint, but of course this custom constraint could not be initialized with live variables, only constants.
Nicolas Cadilhac
Has anyone run into problems run this in IIS6 / windows 2003? Runs fine on my localhost but testing it on my other machine it breaks! (cant debug as dont have VS installed on it)
David Liddle
I've been using this on a live web site running IIS6/Win2k3 without issues so far. I use the IIS wildcard mapping approach.
DSO
Very nice! Our RouteAttribute is very similar to this, only adding a bit of additional helper functionality. I'll have to add an answer detailing the differences.
Jarrod Dixon
This is fantastic. I'm loving it.
BowserKingKoopa
This is great! I've been using MvcContrib for a while and had no idea this was in there. You mentioned in your original post that you didn't have time to document it. Is that still the case? Seems like at least a mention of it in the MvcContrib docs would be very helpful so that developers at least know it's there. Thanks!
Todd Menier
+2  A: 

This post is just to extend DSO's answer.

While converting my routes to attributes, I needed to handle the ActionName attribute. So in GetRouteParamsFromAttribute:

ActionNameAttribute anAttr = methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), false)
    .Cast<ActionNameAttribute>()
    .SingleOrDefault();

// Add to list of routes.
routeParams.Add(new MapRouteParams()
{
    RouteName = routeAttrib.Name,
    Path = routeAttrib.Path,
    ControllerName = controllerName,
    ActionName = (anAttr != null ? anAttr.Name : methodInfo.Name),
    Order = routeAttrib.Order,
    Constraints = GetConstraints(methodInfo),
    Defaults = GetDefaults(methodInfo),
});

Also I found the naming of the route not suitable. The name is built dynamically with controllerName.RouteName. But my route names are const strings in the controller class and I use those const to also call Url.RouteUrl. That's why I really need the route name in the attribute to be the actual name of the route.

Another thing that I will do is to convert the default and constraint attributes to AttributeTargets.Parameter so that I can stick them to params.

Nicolas Cadilhac
Yeah I kind of oscillated on the route naming behavior. Its probably best to do what you did, just use whats in the attribute as-is or make it null. Nice idea putting the default/constraints on the params themselves. I'll probably post this on codeplex at some point to better manage changes.
DSO
+6  A: 

1. Download RiaLibrary.Web.dll and reference it in your ASP.NET MVC website project

2. Decoreate controller methods with the [Url] Attributes:

public SiteController : Controller
{
    [Url("")]
    public ActionResult Home()
    {
        return View();
    }

    [Url("about")]
    public ActionResult AboutUs()
    {
        return View();
    }

    [Url("store/{?category}")]
    public ActionResult Products(string category = null)
    {
        return View();
    }
}

BTW, '?' sign in '{?category}' parameter means that it's optional. You won't need to specify this explicitly in route defaults, which is equals to this:

routes.MapRoute("Store", "store/{category}",
new { controller = "Store", action = "Home", category = UrlParameter.Optional });

3. Update Global.asax.cs file

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoutes(); // This do the trick
    }

    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);
    }
}

How to set defaults and constraints? Example:

public SiteController : Controller
{
    [Url("admin/articles/edit/{id}", Constraints = @"id=\d+")]
    public ActionResult ArticlesEdit(int id)
    {
        return View();
    }

    [Url("articles/{category}/{date}_{title}", Constraints =
         "date=(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])")]
    public ActionResult Article(string category, DateTime date, string title)
    {
        return View();
    }
}

How to set ordering? Example:

[Url("forums/{?category}", Order = 2)]
public ActionResult Threads(string category)
{
    return View();
}

[Url("forums/new", Order = 1)]
public ActionResult NewThread()
{
    return View();
}
Koistya Navin
Very nice! I especially like the `{?param}` nomenclature for optional parameters.
Jarrod Dixon
A: 

I've combined these two approaches into a Frankensteinian version for anybody who wants it. (I liked the optional param notation, but also thought they should be separate attributes from default/constraints rather than all mixed into one).

http://github.com/djMax/AlienForce/tree/master/Utilities/Web/

Max Metral
A: 

I needed to get the ITCloud routing working in asp.net mvc 2 using an AsyncController -- to do so, just edit the RouteUtility.cs class in the source and recompile. You have to strip off the "Completed" from the action name on line 98

// Add to list of routes.
routeParams.Add(new MapRouteParams()
{
    RouteName = String.IsNullOrEmpty(routeAttrib.Name) ? null : routeAttrib.Name,
    Path = routeAttrib.Path,
    ControllerName = controllerName,
    ActionName = methodInfo.Name.Replace("Completed", ""),
    Order = routeAttrib.Order,
    Constraints = GetConstraints(methodInfo),
    Defaults = GetDefaults(methodInfo),
    ControllerNamespace = controllerClass.Namespace,
});

Then, in the AsyncController, decorate the XXXXCompleted ActionResult with the familiar UrlRoute and UrlRouteParameterDefault attributes:

[UrlRoute(Path = "ActionName/{title}")]
[UrlRouteParameterDefault(Name = "title", Value = "latest-post")]
public ActionResult ActionNameCompleted(string title)
{
    ...
}

Hope that helps someone with the same issue.

TimDog
FYI, convention is to have the MVC related attributes on the ActionNameAsync method and not the ActionNameCompleted method.
Erv Walter
Thanks -- did not realize that.
TimDog