4

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

1
  • 1
    would a simpler solution be to have url's such as mywebsite/mycontroller/mycation/14/long-and-pretty-seo-name like stack overflow does? Commented May 15, 2009 at 12:21

5 Answers 5

1

The most obvious way to do this is to use constraints.

Since that your id is an integer, you can add a constraint which will look for an integer value:

new { id = @"\d+" }

and here is the whole route:

routes.MapRoute("myname","mycontroller/myaction/{id}-{seo_name}", 
        new { controller = "mycontroller", action = "myaction" }, 
        new { id = @"\d+"});
Sign up to request clarification or add additional context in comments.

1 Comment

You'd think this would work. But it doesn't work. At least, not for me.
1

My solution is define route as:

routes.MapRoute("myname","mycontroller/myaction/{id}",  
        new { controller = "mycontroller", action = "myaction"}); 

and parse id and seoname manualy using Regex in HTTP handler:

        var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(context));
        var match = System.Text.RegularExpressions.Regex.Match((string)routeData.Values["id"], @"^(?<id>\d+)-(?<seoname>[\S\s]*)$");
        if (!match.Success)
        {
            context.Response.StatusCode = 400;
            context.Response.StatusDescription = "Bad Request";
            return;
        }

        int id = Int32.Parse(match.Groups["id"].Value);
        string seoname = match.Groups["seoname"].Value;

Comments

0

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.

1 Comment

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
0

Define a specific route such as:

        routes.MapRoute(
            "TandC", // Route controllerName
            "CommonPath/{controller}/Terms-and-Conditions", // URL with parameters
            new { controller = "Home", action = "Terms_and_Conditions" } // Parameter defaults
        );

But this route has to be registered BEFORE your default route.

Comments

-1

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!

1 Comment

When you downvote my answer it would be nice to explain why, so I know what is wrong.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.