Search code examples
asp.net-mvc-routingrouteconfig

Creating a Fall-Through Route Rule after the Default Rule


How can I create a fall-through (fallback) route for products after the default route that maps to a product controller?

Instead of example.com/product/laptop I want example.com/laptop

/product is an application that does all sorts of work. However, the product name is dynamic and new ones are added all the time.

If a route exists, then it should use the default:

example.com/about/

example.com/about/shipping

Otherwise, it is a product, and should fall through to the last route rule:

example.com/{dynamic product name fallback}

example.com/laptop

example.com/mouse

example.com/iphone

I have tried the fallback all, but it never gets to the Product controller, and it does not pass the product name which I need.

 url: "{*.}"

RouteConfig:

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

        routes.MapRoute(
            name: "Fall Through to Product",
            url: "{productname}/",
            defaults: new { controller = "Product", action = "Index" }
        );

Controller

 public class ProductController : Controller
 {
    public ActionResult Index(string productname)
    {
        return View();
    }
 }

Solution

  • You can't really. The default route is the default route because it catches just about everything. However, what you can do is handle the 404 by first trying to find a matching product.

    In your Web.config, add the following:

    <httpErrors errorMode="Custom" existingResponse="Auto">
      <remove statusCode="404" />
      <error statusCode="404" responseMode="ExecuteURL" path="/error/notfound" />
    </httpErrors>
    

    Then create ErrorController:

    public ActionResult NotFound()
    {
        // Get the requested URI
        var uri = new Uri(Regex.Replace(Request.Url.OriginalString, @"^(.*?);(.*)", "$2"));
    
        // You slug will be the `AbsolutePath`
        var slug = uri.AbsolutePath.Trim('/');
    
        // Attempt to find product
        var product = db.Products.SingleOrDefault(m => m.Slug == slug);
    
        // If no product, return 404 error page
        if (product == null)
        {
            return View("~/Views/404.cshtml");
        }
    
        // Otherwise, return product view
        Response.StatusCode = 200;
        return View("~/Views/Product/Product.cshtml", product);
    }