1

As the title says all about what I want but to be kind of specific I would like to have a URL pattern like localhost/Product/List/Category/Page but I couldn't succeed finding a solution for it. I am sure it's belong to routing and as it is difficult topic in MVC I would need your help to find a solution for it.

My route config is:

 public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(null, "",
                new
                {
                    controller = "Home",
                    action = "Shop",

                });
            routes.MapRoute(null, "",
                new
                {
                    controller = "Product",
                    action = "list",
                    category = (string)null,
                    page = 1
                }
                );
            routes.MapRoute(null, "Page{page}",
            new
            {
                controller = "Product",
                action = "List",
                category = (string)null,
                subcategory = (string)null
            },
            new { page = @"\d+" }
            );

            routes.MapRoute(null,
            "{category}",
            new { controller = "Product", action = "List", page = 1 }
            );
            routes.MapRoute(null,
            "{category}/Page{page}",
            new { controller = "Product", action = "List" },
            new { page = @"\d+" }
            );
            routes.MapRoute(null, "{controller}/{action}");
        }
    }

My Controller product is:

 public class ProductController : Controller
    {
        EFDbContext db = new EFDbContext();
        private IProductsRepository repository;
        public int PageSize = 4;

        public ProductController (IProductsRepository productrepository)
        {
            this.repository = productrepository;
        }
        public ViewResult List(string category, int page = 1)
        {
            ProductsListViewModel model = new ProductsListViewModel()
            {
                Products = repository.Products
                .Where(p => category == null || p.ProductCategory == category || p.MenSubCategory == category)
                    .OrderBy(p => p.ProductID)
                    .Skip((page - 1) * PageSize)
                    .Take(PageSize),
                PagingInfo = new PagingInfo
                {
                    CurrentPage = page,
                    ItemsPerPage = PageSize,
                    TotalItems = category == null ? repository.Products.Count():repository.Products.Where(e => e.ProductCategory == category).Count()
                },
                CurrentCategory = category
            };
            return View(model);
        }

        public PartialViewResult Menu(string subcategory = null )
        {
            ViewBag.SelectedCategory = subcategory;
            IEnumerable<string> categories = repository.MenSubCategories
                .Select(x => x.MenSubCategory)
                .Distinct()
                .OrderBy(x => x);
            return PartialView(categories);
        }
}

I hope I get answer for this as far as I really tried but couldn't find how to do it.

  • Remove the Home Controller code as it's irrelevant to the question. You are looking for answers for Product controller, right? – jpgrassi Jan 31 '16 at 17:32
  • Yes Exactly. @jpgrassi –  Jan 31 '16 at 17:35
  • But you removed also the Product controller code. Post it again, please. – jpgrassi Jan 31 '16 at 17:36
  • My bad sorry, updated it again. @jpgrassi –  Jan 31 '16 at 17:39
  • ok, what about the page parameter? Are you expecting it also from the URL? – jpgrassi Jan 31 '16 at 17:43
  • No but when a user tries to pagination the page number should be included in URL, Like localhost/Product/List/Category/Page1 @jpgrassi –  Jan 31 '16 at 17:44
  • So the page IS a parameter you will have in your url? Are you confortable with this URL: mysite/Product/List/Cars?page=10 ? – jpgrassi Jan 31 '16 at 17:49
  • That seems to be fine but can you code it this way? mysite/Product/List/Cars/Page10 @jpgrassi –  Jan 31 '16 at 17:50
  • I forgot to add another necessary segment which is SubCategory mysite/product/list/category/**subcategory**/page @jpgrassi –  Jan 31 '16 at 17:55

2 Answers2

0

With attribute routing, you just need to decorate your action method with your specific route pattern.

public class ProductController : Controller
 {
     EFDbContext db = new EFDbContext();
     private IProductsRepository repository;
     public int PageSize = 4;

     public ProductController (IProductsRepository productrepository)
     {
          this.repository = productrepository;
     }
     [Route("Product/list/{category}/{page}")]
     public ViewResult List(string category, int page = 1)
     {
       // to do  : Return something
     }
}

The above route definition will send the request like yourSite/Product/list/phones and yourSite/Product/list/phones/1 to the List action method and the url segments for category and page will be mapped to the method parameters.

To enable attribute routing, you need to call the method MapMvcAttributeRoutes method inside the RegisterRoutes method.

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

    routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Shyju
  • 214,206
  • 104
  • 411
  • 497
  • I get a 404 not found error if i type mysite/Product/List/Men –  Jan 31 '16 at 18:02
  • 1
    In your question i see you have other custom route definitions. Remove that. You need only the default route definition. – Shyju Jan 31 '16 at 18:25
  • Thanks for your efforts I'll try your answer by tomorrow as @Jpgrassi's answer is all what i want and simpler I'll try this too obviously. It is time to sleep now :) Shyju –  Jan 31 '16 at 18:38
0

To generate an URL like you want: localhost/Product/List/Cars you can create a custom route like this:

routes.MapRoute(
    name: "ProductList",
    url: "Product/List/{category}",
    defaults: new { controller = "Product", action = "List" }
);

Remember that custom routes have to come before the most general route (the one that comes with the default template).

Regarding your page parameter, if you are comfortable with this URL: localhost:3288/Product/List/teste?page=10 the above already work. But if you want this: localhost:3288/Product/List/teste/10 10 meaning the page number, then the simplest solution would be create two different routes:

routes.MapRoute(
   name: "ProductList",
   url: "Product/List/{category}",
   defaults: new { controller = "Product", action = "List" }
);

routes.MapRoute(
   name: "ProductListPage",
   url: "Product/List/{category}/{page}",
   defaults: new { controller = "Product", action = "List" , page = UrlParameter.Optional}
);

Another cleaner way, is to create a custom route constraint for your optional parameter. This question has a lot of answers to that: ASP.NET MVC: Route with optional parameter, but if supplied, must match \d+

Community
  • 1
  • 1
jpgrassi
  • 5,482
  • 2
  • 36
  • 55
  • i wouldn't accept any less from you! Sucha great Answer and to the point. One more point how can i add the subcategory to the Url? Like mysite/Product/List/Category/SubCategory/Page –  Jan 31 '16 at 18:08
  • 1
    If the subcategory will be a permanent parameter and not an optional and casual one, just change the route and include it: Product/List/{category}/{subcategory}. Or to make it clear, create another route for that, this enable you to have a "backup" for all endpoints. Just note that in your List action, you will have to include this new subCategory parameter, and if it will be optional, provide a value like = null. – jpgrassi Jan 31 '16 at 18:14
  • Thanks a lot, Got it! –  Jan 31 '16 at 18:17