Klaus Pedersen
Klaus Pedersen

Reputation: 90

ASP.NET Core Routing with UrlRecord slug

I'm working on a commerce platform and want SEO friendly URLs and I have created the following routes

            routes.MapRoute(
                name: "Common",
                template: "{generic_se_name}",
                defaults: new { controller = "Common", action = "Show" }
            );
            routes.MapRoute(
                name: "Product",
                template: "{productName}",
                defaults: new {controller = "Product", action = "Show"}
            );
            routes.MapRoute(
                name: "Category",
                template: "{categoryName}",
                defaults: new { controller = "Catalog", action = "Show" }
            );
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");

However when I call http://localhost/creme it goes into the common and that's what I expect to happen. In that, i look into the database to figure out if its a product or category and if its a product I do a

 public IActionResult Show(string generic_se_name)
    {
        var type = db.UrlRecords.FirstOrDefault(x => x.Slug == generic_se_name);
        if (type != null)
        {
            switch (type.EntityType)
            {
                case "Product":
                    return RedirectToRoute("Product", new {productName = generic_se_name });
                case "Category":
                    return RedirectToRoute("Category", new { categoryName = generic_se_name });
            }
        }
        return BadRequest();
    }

But it keeps calling Common route even thou I say it should redirect to Product Route.

What am I doing wrong?

Regards Klaus

Upvotes: 1

Views: 1982

Answers (1)

Os1r1s110
Os1r1s110

Reputation: 47

The reason it keeps calling your Common route is that it always matches.

Whenever you use RedirectToRoute, the routing middleware will run again and the first route that matches will be the one used.

In your case, you have what seems to be a "non-greedy catch-all" route template as you have no constraint on it at all. So whatever the user puts after hostname (product/category/brand), it will always be mapped to your "generic_se_name" parameter. A real "catch-all" route would use the * in front of the parameter and that would catch all routes even for example host/cream/flavourand your generic_se_param would receive both of them combined as param="cream/flavour".

Basic solution:

If you want to redirect to the correct action handler, you should do as Phil Cooper said and use redirectToAction, where you can specify the controller and action to use. With that solution though, you would effectively be redirected to host/controller/action, which seems to be what you want to avoid.

Solution that might fit your requirements better:

You could simply redirect internally according to what type of entity you get from the route. So instead of calling RedirectToRoute, you could simply call you handler that would be in the controller, in a service or wherever you decide to put it. You would then only need a single "catch-all" route with your generic_se_param.

For reference, I would suggest reading the following pages:

Routing fundamentals: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing Conventional and attribute routing (to controller actions) https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing

(I guess you have already took a look and might already have figured out your problem but I wanted to let it here in case someone would not stumble upon official documentation before coming here when searching for similar things as you).

Upvotes: 1

Related Questions