views:

1358

answers:

2
+2  Q: 

MVC Dynamic Routes

I would like to create dynamic urls that route to controller actions with an Id value. I've created the following route using a catch-all parameter

routes.MapRoute(
            "RouteName",                                                 
            "{id}/{*Url}",
            new { controller = "Controller", action = "Action", id = "" }
        );

This works as expected and allows me to use the following Urls:

"http://website.com/1/fake/url/path" (1 being the id that gets passed to the action method)

Does anyone know a way to achieve it this way instead without creating my own http module?:

"http://website.com/fake/url/path/1"

Thanks - Mark

+1  A: 

That's a really difficult one, for me anyway.

Given the following route:

routes.MapRoute("Default", "{*token}", 
 new { controller = "Home", action = "Index", token = 0 });

Your controller and supporting classes would be something like this:

[HandleError]
public class HomeController : Controller
{
 public ActionResult Index([ModelBinder(typeof(IndexReqquestBinder))] IndexRequest request)
 {
  ViewData["Title"] = "Home Page";
  ViewData["Message"] = String.Format("We're looking at ID: {0}", request.ID);

  return View();
 }
}

public class IndexRequest
{
 public Int32 ID { get; set; }
 public IndexRequest(Int32 id)
 {
  this.ID = id;
 }
}

public class IndexReqquestBinder : IModelBinder
{
 public ModelBinderResult BindModel(ModelBindingContext bindingContext)
 {
  if ( null != bindingContext.RouteData.Values["token"] ) {
   foreach ( String v in bindingContext.RouteData.Values["token"].ToString().Split('/') ) {
    Int32 id = 0;
    if ( Int32.TryParse(v, out id) ) {
     return new ModelBinderResult(new IndexRequest(id));
    }
   }

  }
  return new ModelBinderResult(new IndexRequest(0));
 }
}
Hellfire
Thanks Hellfire. not had a chance to try it yet but looks like just what i'm looking for.
Now ModelBinderResult is not available in System.Web.Mvc 2.0.0.0
Sachin
I don't have time to test this, but can't you just: return new IndexReqeust(id);and bypass the ModelBinderResult completely?
Hellfire
A: 

I would suggest overriding of DefaultControllerFactory

> public class CustomControllerFactory : DefaultControllerFactory
>   {
> public override IController
> CreateController(System.Web.Routing.RequestContext
> requestContext, string controllerName)
>         {
>             try
>             {
>                 return base.CreateController(requestContext,
> controllerName);
>             }
>             catch (Exception exception)
>             {
>                 // collect route data
>                 string id = (string)requestContext.RouteData.Values["id"];
>                 string action = (string)requestContext.RouteData.Values["action"];
>                 // set full path as routing "page" parameter
>                 VirtualPathData path = requestContext.RouteData.Route.GetVirtualPath(requestContext,
> requestContext.RouteData.Values);
>                 requestContext.RouteData.Values["id"]
> = path.VirtualPath; // or anything you need
>                 // use controller page by default
>                 controllerName = "MyController";
>                 // set basuc routing data
>                 requestContext.RouteData.Values["controller"]
> = controllerName;
>                 requestContext.RouteData.Values["action"]
> = "index";
>                 // call base method to create controller
>                 return base.CreateController(requestContext,
> controllerName);
>             }
>         }
>}

And than just register this as default controller factory in global.asax.cs file

protected void Application_Start()
        {
            ControllerBuilder.Current.SetControllerFactory(new MyNameSpace.CustomControllerFactory ());
            RegisterRoutes(RouteTable.Routes); // this already exists by default
        }
Andrej Kaurin