ASP.NET MVC: Routing custom slugs without affecting performance

后端 未结 2 544
执念已碎
执念已碎 2020-12-28 23:55

I would like to create custom slugs for pages in my CMS, so users can create their own SEO-urls (like Wordpress).

I used to do this in Ruby on Rails and PHP framewor

2条回答
  •  时光取名叫无心
    2020-12-29 00:09

    Even if your route registration code works as is, the problem will be that the routes are registered statically only on startup. What happens when a new post is added - would you have to restart the app pool?

    You could register a route that contains the SEO slug part of your URL, and then use the slug in a lookup.

    RouteConfig.cs

    routes.MapRoute(
        name: "SeoSlugPageLookup",
        url: "Page/{slug}",
        defaults: new { controller = "Page", 
                        action = "SlugLookup",
                      });
    

    PageController.cs

    public ActionResult SlugLookup (string slug)
    {
        // TODO: Check for null/empty slug here.
    
        int? id = GetPageId (slug);
    
        if (id != null) {    
            return View ("Show", new { id });
        }
    
        // TODO: The fallback should help the user by searching your site for the slug.
        throw new HttpException (404, "NotFound");
    }
    
    private int? GetPageId (string slug)
    {
        int? id = GetPageIdFromCache (slug);
    
        if (id == null) {
            id = GetPageIdFromDatabase (slug);
    
            if (id != null) {
                SetPageIdInCache (slug, id);
            }
        }
    
        return id;
    }
    
    private int? GetPageIdFromCache (string slug)
    {
        // There are many caching techniques for example:
        // http://msdn.microsoft.com/en-us/library/dd287191.aspx
        // http://alandjackson.wordpress.com/2012/04/17/key-based-cache-in-mvc3-5/
        // Depending on how advanced you want your CMS to be,
        // caching could be done in a service layer.
        return slugToPageIdCache.ContainsKey (slug) ? slugToPageIdCache [slug] : null;
    }
    
    private int? SetPageIdInCache (string slug, int id)
    {
        return slugToPageIdCache.GetOrAdd (slug, id);
    }
    
    private int? GetPageIdFromDatabase (string slug)
    {
        using (CMSContext db = new CMSContext()) {
            // Assumes unique slugs.
            Page page = db.Pages.Where (p => p.Slug == requestContext.Url).SingleOrDefault ();
    
            if (page != null) {
                return page.Id;
            }
        }
    
        return null;
    }
    
    public ActionResult Show (int id)
    {
        // Your existing implementation.
    }
    

    (FYI: Code not compiled nor tested - haven't got my dev environment available right now. Treat it as pseudocode ;)

    This implementation will have one search for the slug per server restart. You could also pre-populate the key-value slug-to-id cache at startup, so all existing page lookups will be cheap.

提交回复
热议问题