0

I have an index page which displays Categories (which have attributes: id and name) with the URL request: http://localhost:62745/home/index.

When I click a category, I am taken to http://localhost:62745/Home/Products/6.

I want to make the URL more detailed and replace the Category Id property 6 in the previous URL with the name property of the Category which was just clicked.

My route config look like this:

public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );

            routes.MapRoute(
                name: "Categories",
                url: "{controller}/{action}/{categoryName}",
                defaults: new { controller = "Category", action = "Index", categoryName = UrlParameter.Optional }
            );

        }
    }

The first MapRoute() method was already implemented. I added the second one hoping to solve my problem, but it didn't.

This is my controller action for products:

// GET: Product
public async Task<ActionResult> Index(int? id, string categoryName)
{

    var products = (await db.Categories.Where(c => c.Id == id)
                                    .SelectMany(p => p.Products.Select(x => new ProductViewModel { Id = x.Id, Name = x.Name, ByteImage = x.Image, Price = x.Price}))
                                    .ToListAsync());

    categoryName = db.Categories.Where(c => c.Id == id).Select(c => c.Name).ToString();

    if (products == null)
    {
        return HttpNotFound();
    }

    return View(new ProductIndexViewModel{ Products = products, CategoryId = id });
}
naz786
  • 485
  • 1
  • 5
  • 22
  • 1
    I take it that the `id` is the numeric identifier for the `Category` name? If that is the case, I would also assume that `id` is unique, while `Category` does not have a unique constraint on it. Trying to use the human readable `Category` as a value you are trying to key off of will lead to problems. What is it that you are trying to accomplish? Is this simply to have a readable value in your query string? – gmiley Jan 21 '17 at 23:32
  • 1
    One point to note is when mapping routes you always want to go from most specific at the top to the most general at the bottom. So Default route needs to be last in the list, rather than first as you currently have it. Otherwise other routes won't get a chance – K Scandrett Jan 21 '17 at 23:52
  • 1
    See [Why map special routes first before common routes in asp.net mvc?](http://stackoverflow.com/a/35674633/181087) for an explanation of why this happens and how it can be fixed. – NightOwl888 Jan 22 '17 at 03:20

1 Answers1

1

Both your routes are identical in terms how they are interpreted - they accept 2 segments and an optional 3rd segment, so Home/Products/6 and Home/Products/CategoryName both match the first Default route (there is nothing unique to distinguish the 2nd Categories route so it will never be hit.

Your question refers to a Products() method in the 2nd url, but you have not shown that method, so its not clear if your referring to a product name or a category name, but the principal is the same.

Assuming you want the url to be Home/Products/ProductName to display a specific product, then in the view

@foreach(var item in Model.Products)
{
    // link for each product
    @Html.ActionLink("Details", "Products", "Home", new { id = item.Name }, null)

Then you can simply make the method

public ActionResult Products(string id)
{
    // the value of id is the the the product name
    ....
}

Alternatively, if you want the parameter to be string name rather that string id, then you can define a specific route and place it before the Default route.

routes.MapRoute(
    name: "ProductDetails",
    url: "Home/Products/{name}",
    defaults: new { controller = "Home", action = "Products", name = UrlParameter.Optional }
);

so that the method becomes

public ActionResult Products(string name)

Side note: The fact you have a query in the Index() method to get the category name suggests you might also be wanting a 'slug' route, in which case, refer how to implement url rewriting similar to SO.

Community
  • 1
  • 1