tomo
tomo

Reputation: 1950

ASP.NET MVC Route with dash

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

Upvotes: 4

Views: 3165

Answers (5)

Nexxas
Nexxas

Reputation: 893

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.

Upvotes: 0

Tomáš Holan
Tomáš Holan

Reputation: 321

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;

Upvotes: 1

Nikita Ignatov
Nikita Ignatov

Reputation: 7183

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+"});

Upvotes: 1

Peter
Peter

Reputation: 14518

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!

Upvotes: -1

MunkiPhD
MunkiPhD

Reputation: 3644

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.

Upvotes: 0

Related Questions