views:

76

answers:

3

I'm trying to implement routing such as the following:

posts/535434/This-is-a-post-title

posts/tagged/tags+here
// Matches {controller}/{action}/{id} - Default
// Displays all posts with the specified tags
// uses PostsController : ActionTagged(string tags)

posts?pageSize=50&pageIndex=4
// Matches {controller}/{action}/{id} - Default
// Displays all posts
// uses PostsController : Index(int? pageSize, int? pageIndex)

Here's the problem I want to do this:

posts/39423/this-is-a-post-title-here
// Typically this is implemented using an action like 'Details'
// and would normally look like : posts/details/5

I can't seem to get the routing working right. I tried something like this:

{controller}/{id}/{description}

and set the default action to be "Display" which works, but then won't allow me to navigate to other named actions like "Tagged".

What am I missing?

Thanks!

+1  A: 

Two things:

First, you should always order your routes in decreasing specificity (e.g. most specific case first, least specific case last) so that routes will "fall through", if one doesn't match it will try the next.

So we want to define {controller}/{postid}/... (must be a postid) before we define {controller}/{action}/... (could be anything else)

Next, we want to be able to specify that if the provided value for postid does not look like a Post ID, the route should fail and fall through to the next one. We can do this by creating an IRouteConstraint class:

public class PostIDConstraint : IRouteConstraint
{ 
  public bool Match(HttpContextBase httpContext,
    Route route,
    string parameterName, 
    RouteValueDictionary values, 
    RouteDirection routeDirection)
  {
    //if input looks like a post id, return true.
    //otherwise, false
  }
}

We can add it to the route definition like so:

routes.MapRoute(
    "Default",
    "{controller}/{postid}/{description}",
    new { controller = "Posts", action = "Display", id = 0 },
    new { postid = new PostIDConstraint() }
);
Rex M
Good answer. Question though. What do I need to do in the IRouteContraint? I've never used it, can you explain it a little more.
Micah
You can do whatever logic makes sense to verify the request it's trying to validate against the route. The Match() method gives you the route and the values provided, you could write if(values["PostID"] = "foo") return true;
Rex M
A: 

I'm not 100% I understand your question, but it sounds like you can just define a couple different routes.

routes.MapRoute("PostId", "posts/{id}/{title}",
    new { Controller = "Posts", Action = "DisplayPost", id = 0, title = "" },
    new { id = @"\d+" });

routes.MapRoute("TaggedPosts", "posts/tagged/{tags}",
    new { Controller = "Posts", Action = "DisplayTagged", tags = "" });

routes.MapRoute("Default", "posts",
    new { Controller = "Posts", Action = "Index" });

You can use regular expressions to validate parameters like I used for id in the first route, or if you want some better validation do something like Rex M posted. The querystring parameters pageSize and pageIndex don't need to be included in your route; they will just be passed in to your Index method as long as the parameter names match.

A: 

The part of the url that's the "description" actually isn't used. For example, this post is 519222 and I can still get to it using the url: http://stackoverflow.com/questions/519222/this-line-is-only-used-for-seo

ajma