vote up 2 vote down star
1

For a scenario, I have a ASP.NET MVC application with pages that looks like follow:

http://example.com/Customer/List
http://example.com/Customer/List/Page/2
http://example.com/Customer/List
http://example.com/Customer/View/8372
http://example.com/Customer/Search/foo/Page/5

These pages are achieved with following routes in Global.asax.cs

routes.MapRoute(
    "CustomerSearch"
    , "Customer/Search/{query}/Page/{page}"
    , new { controller = "Customer", action = "Search" }
);

routes.MapRoute(
    "CustomerGeneric"
    , "Customer/{action}/{id}/Page/{page}"
    , new { controller = "Customer" }
);

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

These all have gone well until a new requirement arrived and wants to drop keyword 'Customer' off the URL, to make the pages looks like:

http://example.com/List
http://example.com/List/Page/2
http://example.com/List
http://example.com/View/8372
http://example.com/Search/foo/Page/5

Edit: corrected example links, thanks to @haacked.

I tried to add new MapRoutes to take {action} only and have default controller set to Customer. eg/

routes.MapRoute(
    "CustomerFoo"
    , "{action}"
    , new { controller = "Customer", action = "Index" }
);

This may seems to work, however now all links generated by Html.ActionLink() are weird and no longer URL friendly.

So, is this achievable? Am I approach the right direction?

flag

75% accept rate
Did you delete the old routes? – Baddie Nov 11 at 6:47
No, I had my new routes placed before the original routes. – rockacola Nov 11 at 7:44

2 Answers

vote up 2 vote down check

don't mix a rule like: "{action}/{id}" with one that's "{controller}/{action}/{id}" ... specially when id in the later has a default value i.e. is optional.

In that case you have nothing that allows routing to know which one is the right one to use.

A workaround, if that's what you need, would be to add a constrain (see this) to the action in the earlier to a set of values i.e. List, View. Of course that with these types of rules, you can't have a controller with the same name of an action.

Also remember that if you specify a default action & id in the "{action}/{id}" rule, that will be used when you hit the route of your site.

link|flag
Apologise that my reply isn't speedy, because I want to try out the answers before response with feedbacks. Sorry about this :P – rockacola Nov 23 at 12:31
@rockacola its k, there is no rush. – Freddy Rios Nov 23 at 16:25
This is what I'm looking for! Route constraints will give me the customisation I desired without jeopardise MapRoute. I need to reorganise all my existing routes tho. Thanks @Freddy! – rockacola Nov 24 at 3:26
vote up 5 vote down

Why does the first URL in the new list still have "Customer". I assume that's a typo and you meant:

The following routes work for me:

routes.MapRoute(
    "CustomerSearch"
    , "Search/{query}/Page/{page}"
    , new { controller = "Customer", action = "Search" }
);

routes.MapRoute(
    "CustomerGeneric"
    , "{action}/{id}/Page/{page}"
    , new { controller = "Customer" }
);

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

How are you generating your links. Since the Controller is no longer in the URL of your route (aka, you don't have "{controller}" in the route URL), but it's a default value, you need to make sure to specify the controller when generating routes.

Thus instead of

Html.ActionLink("LinkText", "ActionName")

do

Html.ActionLink("LinkText", "ActionName", "Customer")

Why? Suppose you had the following routes.

routes.MapRoute(
    "Default",
    "foo/{action}",
    new { controller = "Cool" }
);

routes.MapRoute(
    "Default",
    "bar/{action}",
    new { controller = "Neat" }
);

Which route did you mean when you call this?

<%= Html.ActionLink("LinkText", "ActionName") %>

You can differentiate by specifying the controller and we'll pick the one that has a default value that matches the specified one.

link|flag
Thanks for reply Phil. The routes work fine IF all pages are from Customer controller. Say if I have User controller with SignIn action: /User/SignIn/ request will be hijacked by CustomerDefault (which is {action}/{id}) where I want it to fall into Default (which is {controller}/{action}). Is there a way to teach your route which parameter meant to be action and which meant to be controller? Thanks again, love your blog. – rockacola Nov 23 at 1:48
@rockacola I just added an answer about this (before seeing your comment), add a constraint to limit {action} so it doesn't use that rule for {customer}. – Freddy Rios Nov 23 at 1:59
Use a constraint, just as Freddy said. – Haacked Nov 25 at 17:55

Your Answer

Get an OpenID
or

Not the answer you're looking for? Browse other questions tagged or ask your own question.