tags:

views:

293

answers:

2

Hello,

I've got ASP.NET MVC routing question.

I prepared following routing table to map such url

mywebsite/mycontroller/myaction/14-longandprettyseoname

to parameters:

14 => id (integer)

longandprettyseoname -> seo_name (string)

    routes.MapRoute(
        "myname",
        "mycontroller/myaction/{id}-{seo_name}", 
        new { controller = "mycontroller", action = "myaction", id = 0, seo_name = (string)null });

    routes.MapRoute(
        "Default",  
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = "" });

It works for URL above but it has problems for following type of urls

mywebsite/mycontroller/myaction/14-long-and-pretty-seo-name

Is that possible to make it working?


EDIT:

"mycontroller/myaction/{seo_name}-{id}"

seems to be working

A: 

I don't think the route will be distinguishable as it will not be able to figure which "-" to split at to specify the {id} and the {seo-name}.

How about using underscores for your SEO name? Or you could just use the SEO name as the actual {id}. If the SEO name is something that is going to be unique, this is a very viable option you can use as a pseudo primary key to that entry in your db (assuming it's pulling something from a DB)

Also, utilize Phil Haack's route debugger to see what works and doesn't work.

MunkiPhD
I don't want seo_name to be globally unique so this is a reason of placing id field at the begininning.If it cannot distinguish dashes then I can change rule to mycontroller/myaction/{id}/{seo_name} and for me it's ok but I don't know if it violates any seo rules / best-practices
tomo
A: 

What you could do is create a custom controller factory. That way you can have custom code to decide which controller needs to be called when.

public class CustomControllerFactory : IControllerFactory
    {
        #region IControllerFactory Members

        public IController CreateController(RequestContext requestContext, string controllerName)
        {
            if (string.IsNullOrEmpty(controllerName))
                throw new ArgumentNullException("controllerName");

            //string language = requestContext.HttpContext.Request.Headers["Accept-Language"];
            //can be used to translate controller name and get correct controller even when url is in foreign language

            //format controller name
            controllerName = String.Format("MyNamespace.Controllers.{0}Controller",controllerName.Replace("-","_"));

            IController controller = Activator.CreateInstance(Type.GetType(controllerName)) as IController;
            controller.ActionInvoker = new CustomInvoker(); //only when using custominvoker for actionname rewriting
            return controller;
        }

        public void ReleaseController(IController controller)
        {
            if (controller is IDisposable)
                (controller as IDisposable).Dispose();
            else
                controller = null;
        }

        #endregion
    }

To use this custom controllerfactory, you should add this in your global.asax

protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
            ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory));
        }

Note that this only works for the controller, not for the actions... To hook up custom rewriting on actions before they get executed, use this code:

public class CustomInvoker : ControllerActionInvoker
{
    #region IActionInvoker Members

    public override bool InvokeAction(ControllerContext controllerContext, string actionName)
    {
        return base.InvokeAction(controllerContext, actionName.Replace("-", "_"));
    }

    #endregion
}

I got most of this code from this blog and adjusted it to my needs. In my case, I want dashes to separate words in my controller name but you can't create an action with a dash in the name.

Hope this helps!

Peter