Benjamin Gale
Benjamin Gale

Reputation: 13177

ASP.NET MVC Routing using ID or action name

I have an ASP.Net application which has an area called 'Customers'. This area has a controller with the same name with a single method called Index.

I have the following route defined:

context.MapRoute(null,
    "Customers/{controller}/{action}",
    new { controller = "customers", action = "Index" }
);

This allows my to navigate to use the following URL to navigate to the index method on my Customers controller.

MyDomain/Customers

In my customer area I also have another controller called products. This has a number of methods that allow me to work with product entities (mostly auto-generated by Visual Studio at the moment).

With my current route I can navigate to the products controller using URL's like this:

MyDomain/Customers/Products (shows the index page of the products controller) MyDomain/Customers/Products/Create (Shows a page to add new products). MyDomain/Customers/Products/Details?id=1234 (Show the product with the id of 1234)

Now what I want to be able to do is navigate to the details page with a much more user friendly URL such as:

MyDomain/Customers/Products/1234

I have defined a new route that look like this:

context.MapRoute(null,
    "Customers/Products/{id}",
    new { controller = "Products", action = "Details" }
    );

The route is defined before the first route I demonstrated. This allows me to navigate to the products page as I want, however I can no longer navigate to other methods on my products controller.

E.G. The following URL

MyDomain/Customers/Products/Create

Gives me the following error:

The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ViewResult Details(Int32)'

If I change the order of the routes then I can navigate to all the methods on my products controller but my details URL reverts to the old format of having a query parameter.

If I update the route to look like this:

context.MapRoute(null,
    "Customers/Products/{id}",
    new { controller = "Products", action = "Details", id = UrlParameter.Optional }
    );

Then I still get the same problem.

Can anyone tell me how to structure my routes to get the result I want? In summary:

  1. If I navigate to the Customers area I want my URL to look like 'MyDomain/Customers'
  2. If I navigate to the product details page I want my URL to look like "MyDomain/Customers/Products/1234".
  3. If I navigate to any other product page I want my URL to look like 'MyDomain/Customers/Products/Create'

Upvotes: 5

Views: 4413

Answers (4)

Tomas Jansson
Tomas Jansson

Reputation: 23472

Try something like this:

public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
        "Products_Details",
        "Customers/Products/{id}",
        new { controller="Products", action="Details" },
        new { id = @"\d+" }
    );

    context.MapRoute(
        "Customers_default",
        "Customers/{controller}/{action}/{id}",
        new { action = "Index", id = UrlParameter.Optional }
    );
}

Update: added constraint on route.

Explanation: The first route maps to your details page and forces the user to provide an id that has the format of numbers (the last parameter in the first MapRoute). If the id doesn't have the format \d+ it will not match the route and the default will be used. This will give you the following routes:

Customers\Products (index)
Customers\Products\1234 (details of 1234)
Customers\Products\Create (create page for products)
Customers\Products\Edit\1234 (edit page for 1234)

Upvotes: 1

Tom Chantler
Tom Chantler

Reputation: 14951

If you want to keep your existing routes, try doing this:

context.MapRoute(null,
     "Customers/Products/{id}", // id is optional, so Customers/Products might reasonably return all products
     new { controller = "Products", action = "Details", id = UrlParameter.Optional },
     new {id = @"\d+"} // must be numeric if present
);

context.MapRoute(null,
     "Customers/Products/{action}/{id}", // Id is optional, so this needs an action.
     new { controller = "Products", action = "Details", id = UrlParameter.Optional }
     // maybe the id is not numeric for all actions? I don't know so I won't constrain it.
);

context.MapRoute(null,
     "Customers/{controller}/{action}", // Default Customers route
     new { controller = "customers", action = "Index" }
);

The first two deal with a URL like /Customers/Products/... with one or two other parts and the last one deals with anything else starting with /Customers/

Upvotes: 0

chris
chris

Reputation: 31

try this

    context.MapRoute(
                    "Customers_Products",
                    "Customers/Products/{productId}",
                    new { controller = "Products", action = "Details", productId= UrlParameter.Optional }
                );

    context.MapRoute(null,
                    "Customers/{controller}/{action}",
                     new { controller = "customers", action = "Index" }
);

Upvotes: 0

Richard Dalton
Richard Dalton

Reputation: 35803

If the ID is always going to be an int then you can add a constraint to the route like this:

context.MapRoute(null,
                 "Customers/Products/{id}",
                 new {controller = "Products", action = "Details", id = UrlParameter.Optional},
                 new {id = @"\d+"} // Constraint to only allow numbers
                );

Upvotes: 4

Related Questions