C#
C#

help

Root Question Message

holykent
holykent11/8/2022
❔ Help with MVC web shop

So, I'm slightly confused about MVC Core, mainly routing and when to make new controllers/actions/views.

I'm making a web shop/ecommerce project for gaming/PC stuff.

So, currently, my DB looks like this:

Category (PC Peripherals, Chairs...), ProductType (Mouse, Headset...), Brand and Product (Zowie FK 1, whatever). Category has a one to many relationship with type, and type has a one to many with product etc...

Right now, for the "catalog" (i.e. products) I only have one controller called Catalog, and it has two actions. Index get and index post. In the view, you can hover over category to display the product types and brands, and then you can click on a type or brand. This will post a form to the post action and then display the products of that type.

Here's problem 1: I want the URL to show www.website.com/1/2

1 being category, 2 being product type.

But it shows www.website.com/?categorySelected=1&productTypeSelected=1

Problem 2:

What if I introduce the actual product?

What I mean is, now, when you display the product types, I want the user to be able to click the product... and this will take you to the product page. This introduces multiple problems for me.

1. The url should show website.com/1/2/productname but I don't know how to achieve it

2. And should I be creating a new view for this? And wouldn't this necessitate another action in the catalog controller?

If not, what is the appropriate way of doing this?

Here's a github link to the project: https://github.com/kennyolmez/GamifyWebShop
Angius
Angius11/8/2022
You need to utilize the [HttpGet] attribute to achieve the routing you want. For example
[HttpGet("{catId}/{prodId}")]
public async Task<IActionResult> GetProduct(int catId, int prodId)
{
    // ...
}
holykent
holykent11/8/2022
@85903769203642368When I do that, the url doesnt change at all. It shows me the right view, but the URL doesnt change
holykent
holykent11/8/2022
Also, what is the difference between:

[HttpGet("{catId}/{prodId}")]

and [Route("{catId}/{prodId}")]
Angius
Angius11/8/2022
[Route] is more absolute and doesn't specify the method, it should only ever be used on controller classes to define the base route for the action methods
holykent
holykent11/8/2022
[HttpGet("{categorySelected:int}/{productTypeSelected:int}")]

I tried this, the view shows the right info but the URL doesnt change
holykent
holykent11/8/2022
I tried without :int as well
Angius
Angius11/8/2022
Weird
Angius
Angius11/8/2022
How do you visit this URL?
Angius
Angius11/8/2022
Do you generate some link?
holykent
holykent11/8/2022
It's just one view, PRG pattern. I don't generate any links. The user clicks a product type (which is effectively a submit button styled as text) > index post method > redirect to index get > generate whatever is needed for the view > display view
Angius
Angius11/8/2022
How do you get to that controller action, then?
holykent
holykent11/8/2022
so if they click mouse, it will take them to the same index but the products displayed to the left are mice instead
Angius
Angius11/8/2022
If there's nothing that links to it?
holykent
holykent11/8/2022
it's a form submission
Angius
Angius11/8/2022
Ah, well, that'd be why
Angius
Angius11/8/2022
Forms don't link to pages
Angius
Angius11/8/2022
They use queries
Angius
Angius11/8/2022
You can't expect a form to construct a URL according to your spec
holykent
holykent11/8/2022
how come it works when I do this?

[Route("Catalog/Index/{brandSelected:int}")]
holykent
holykent11/8/2022
u can see it shows the "right" url
holykent
holykent11/8/2022
and it's the exact same form
Angius
Angius11/8/2022
¯\_(ツ)_/¯
Angius
Angius11/8/2022
All I can tell is using forms as links for no reason cannot be a good idea
Angius
Angius11/8/2022
Maybe it works fine when there's just one param, dunno
Angius
Angius11/8/2022
I never used forms as links, so can't tell
holykent
holykent11/8/2022
it's not generating a link, it's just posting the form to post action > post action redirects to get > generates view
holykent
holykent11/8/2022
what's the appropriate way of doing it?
Angius
Angius11/8/2022
Ah, gotcha
Angius
Angius11/8/2022
Well, in that case, how do you redirect in your post handler?
Angius
Angius11/8/2022
That's where the URL to redirect to will be generated
holykent
holykent11/8/2022
[HttpGet]
        [Route("")]
        //[Route("Catalog/Index/{categorySelected:int}/{productTypeSelected:int}")] -- this does not work yet
        [Route("Catalog/Index/{brandSelected:int}")] // Brand belongs to nothing, we only want brand for the visual display of brands available in any given category
        public async Task<IActionResult> Index(int? categorySelected, int? productTypeSelected, int? brandSelected, int? page)
        {
            int pageSize = 3; // Page size, temporary. Not sure where to put this.
            int totalProductCount = (await _services.GetAllProducts()).Count();

            IndexViewModel viewModel = new IndexViewModel
            {
                Products = await _services.GetProducts(productTypeSelected, brandSelected, page ?? 1, pageSize),
                ProductType = await _services.GetAllProductTypes(),
                Brand = await _services.GetAllBrands(),
                Category = await _services.GetAllCategories(),
                PaginationHelper = new PaginationHelper()
                {
                    Page = page ?? 1,
                    ProductCount = totalProductCount,
                    PageCount = (int)Math.Ceiling(((decimal)totalProductCount / pageSize)),
                }
            };

            viewModel.PaginationHelper.NextIsEnabled = ((page ?? 1) < viewModel.PaginationHelper.PageCount) ? true : false; 
            viewModel.PaginationHelper.PreviousIsEnabled = (page > 1) ? true : false;

            return View(viewModel);
        }

        [HttpPost]
        public async Task<IActionResult> Index(IndexViewModel vm, int? page)
        {
            // This is where we do validation

            return RedirectToAction("Index",
                new { categorySelected = vm.CategorySelected, 
                    productTypeSelected = vm.ProductTypeSelected, 
                    brandSelected = vm.BrandSelected, 
                    page = page,
                });
        }
holykent
holykent11/8/2022
my controller looks like this
holykent
holykent11/8/2022
it's just one post and one get
holykent
holykent11/8/2022
for the same view
Angius
Angius11/8/2022
Why are you using two [Route] attributes?
Angius
Angius11/8/2022
And empty [HttpGet]?
holykent
holykent11/8/2022
according to the docs you could use multiple route attributes in case it wasn't displaying the right page or something
holykent
holykent11/8/2022
and im not sure how to construct the http get attributes
holykent
holykent11/8/2022
i've read a bit but i cant wrap my head around it
Angius
Angius11/8/2022
Change
[HttpGet]
        [Route("")]
        //[Route("Catalog/Index/{categorySelected:int}/{productTypeSelected:int}")] -- this does not work yet
        [Route("Catalog/Index/{brandSelected:int}")] 

to
        [HttpGet("Catalog/Index/{brandSelected:int}")] 
Angius
Angius11/8/2022
And maybe set the base route on the controller class too, with [Route] this time
holykent
holykent11/8/2022
[Route("[controller]/[action]")]

like this?
Angius
Angius11/8/2022
For example
Angius
Angius11/8/2022
And then you'd remove those segments from the HttpGet on the action
holykent
holykent11/8/2022
ok when I do that it doesnt even render the page at all
holykent
holykent11/8/2022
holykent
holykent11/8/2022
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Catalog}/{action=Index}/{id?}");


route looks like this in program.cs
Angius
Angius11/8/2022
Angius
Angius11/8/2022
holykent
holykent11/8/2022
ah
ContactFrequently Asked QuestionsJoin The DiscordBugs & Feature RequestsTerms & Privacy