Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I allow users to create a permalink in ASP.NET MVC?

I have a route defined as:

{theme}/{subtheme}/{urltitle} for listing article details, and I would like to give the possibility to other people (not developers) to create permalinks for an specific article, as example http://www.whateverdomain/article-about-cars/:

Question

How can I handle the requests to {theme}/{subtheme}/{urltitle} to be rewritten to a permalink if there is a permalink for this article?

like image 774
Jose3d Avatar asked Aug 12 '11 11:08

Jose3d


1 Answers

There are three things you have to do to make this happen:

  1. Define a vague enough route that can be used by an internal database call with some specific piece of information in it that is relevant to only that article.
  2. Make the database call in your controller action to not only allow for the normal route, but for this permalink as well (I'll show you the two varying controllers below)
  3. Create the 'CRUD' (or really, the 'CR' necessary for users to create their own permalinks and store this in a database field (I'll also go into this below).

First, let's start with what information our article needs for us to do this:

Database Table Structure

Article
-------
Id       <--------- 
Title              \
Slug               |
Theme              |
SubTheme           |
                   |
                   |
Permalink Table    |
---------------    |
PermalinkId        |
Name               |
Slug               /
ArticleId ---------

Routes

//normal route for article
routes.MapRoute("article",
    "{theme}/{subtheme}/{slug}",
    new {controller = "article", action = "show" }
    );

//Permalink route for article
//You may want to create a custom route constraint for this, or place at bottom of routes
routes.MapRoute("permalinkArticleRoute",
    "{PermaLinkName}",
    new {controller = "article", action = "showbypermalink"}
    );

Controller

public class ArticleController : Controller
{
    public ArticleRepository ArticleRepository {get; set;} //DI'd or constructor injected
    public ActionResult Show(Article article)
    {
        var article = ArticleRepository.GetBy(article.theme, article.subtheme, article.slug);
        ArticleViewModel avm = new ArticleViewModel(article);
        return View(avm);
    }

    public ActionResult ShowByPermalink(string PermalinkName)
    {
        var article = ArticleRepository.GetBy(PermalinkName);
        ArticleViewModel avm = new ArticleViewModel(article);
        return View(avm);
    }

}

Model

public class ArticleRepository
{
    //Uses Linq-to-SQL. Can be adapted for any other ORM. The retrieval logic is the same
    //It's the actual code that differs

    public Article GetBy(string theme, string subtheme, string slug)
    {
        return (from a in db.Articles where
            (a.Theme == theme && a.Subtheme == subtheme && a.Slug == slug)
            select a).FirstOrDefault();
    }

    public Article GetBy(string permalinkName)
    {

        return (from a in db.Articles 
            join p in Permalink on permaLink.ArticleId equals a.Id
            where p.permalinkName == permalinkName
            select a;
    }
}

Allow users to create Permalinks

The final piece is the Create/Read capability for users to create permalinks. Note that this is 'bad' from an SEO perspective (the dilution that occurs when more than one link resolves to the same page), but you may want to do it (for whatever reason).

With each approach, make sure you issue a 301 Redirect (RedirectToAction issues this) to the correct "current" URL. If you do not, you will be punished by the search gods.

Update your Permalink Action to redirect you to the Show action:

    public ActionResult ShowByPermalink(string PermalinkName)
    {
        var article = ArticleRepository.GetBy(PermalinkName);
        return RedirectToAction("Show", article);
    }

Now for creating the permalink. This involves adding CR (of the CRUD) to a Permalink repository, much like we did before with the article.

Here are some caveats:

  • If you do not use the Url route {permalinkId}/{permalinkName}, you must have logic to ensure that all permalinks are unique.
  • You need to include logic in your actions to deal with 404s or malformed URLs.
  • When creating an article, you need logic to properly slug the article and store that with the article
  • A custom route constraint (using IRouteConstraint) would be a way to check at request time whether or not a user is getting a valid slug right. However, this is more work and leads to more database hits (unless you have a good caching mechanism, but that leads to other potential problems)
  • Your permalink route should be at the bottom of your routes if there are other routes that define the {something} behavior, as in: http://example.com/something. Otherwise other routes will hit your permalink route when you don't want that to happen. Don't worry about this if you have a good route constraint in place.
like image 87
George Stocker Avatar answered Oct 12 '22 23:10

George Stocker