Creating dynamic data-driven landing pages in ASP.NET MVC

by alex 8. April 2013 19:26

Creating dynamic landing pages

I mentioned in the last post that No Dice Required was updated. The real experiment with this site is that I'm playing with scaling out SEO efforts by drastically increasing the number of landing pages.

A landing page is the first page of your website that a visitor lands on. A certain amount of visitors to nodicerequired.com will be looking for "language learning tools", but another group will be looking for "Japanese food words." Ideally, the visitor should land on the home page for the first phrase but the 2nd phrase, "Japanese food words", has no place on the homepage. That's where landing pages come in.

Landing pages are kind of like search fodder to give you more indexes in Google. There are two goals: one is to grab search phrases which are related to your problem domain but not necessarily related to your product, and the second is to then route visitors into your website from those phrases. Above all else, landing pages should resolve some need the visitor has.

It would be easy to create a dozen landing pages by hand, but this has scalability problems; hand-crafting a dozen similar pages isn't what you or I should be spending our on. As a programmer, this problem has an easy programming solution!

First off, you should remember that there isn't anything particularly unique about a landing page, other than that it generally has many crosslinks to other content on your site (and maybe to other landing pages); however the rest of your site generally won't have links to landing pages.

The rest of this post is techy and related to creating landing pages in ASP.NET MVC 4. If that isn't your cup of tea, then you should probably move on to the next article now =)

Defining your data structures

You need to consider the data that you'll need to populate a page. For No Dice, I planned to have each landing page be a list of translated vocabulary words, and it will include links to other lists, and the lists are grouped by categories.

I have POCO objects to support these goals. The POCO objects may look a little wonky, but I'm using them directly with Entity Framework to build my database via Code First, so I can deal with the weird virtual properties.

public class Word
{
    public int ID { get; set; }

    public DateTime DateCreated { get; set; }

    public int UserID { get; set; }

    [StringLength(256)]
    public String Text { get; set; }

    [StringLength(10)]
    public String Language { get; set; }

    public int? WordListID { get; set; }
    public WordList WordList { get; set; }
}

public class WordList
{
    public int ID { get; set; }

    public DateTime DateCreated { get; set; }

    public int UserID { get; set; }

    public String Name { get; set; }

    public virtual IList<WordListCategory> WordListCategory { get; set; }

    public virtual IList<Word> Words { get; set; }
}

public class WordListCategory
{
    public int ID { get; set; }

    public DateTime DateCreated { get; set; }

    public int UserID { get; set; }

    public String Name { get; set; }

    public virtual IList<WordList> WordLists { get; set; }
}

That's pretty simple. There's a one to many relationship between WordList and Word, and a many to many between WordList and WordListCategory. Or, in other words, every Word is in one WordList; every WordList has many Words and many WordListCategorys.

The data model I'm using isn't exactly like this, but it's similar enough for this discussion.

I don't want every WordList to be a landing page, so I also have a Page object:

public class Page
{
    public int ID { get; set; }
    public DateTime DateCreated { get; set; }

    [StringLength(256)]
    public String Name { get; set; }

    public String ViewName { get; set; }
    public String ViewMasterName { get; set; }

    [StringLength(256)]
    public virtual String Stub { get; set; }

    public int WordListID { get; set; }
    public WordList WordList { get; set; }

}

For every landing page that I want, I create a Page object and configure it to use a WordList, give it a stub and a name, and it's good to go.

You might also notice that I added a ViewName and ViewMasterName property. This allows me to configure which .cshtml file will be used to render the landing page, in addition to a Master Page or layout, so that every page doesn't necessarily have to have the same look and feel as the others or the rest of the site.

Persisting the objects

You'll need some place to persist all these objects, probably XML files or a database. Personally, it's quickest for me to drop them into an Entity Framework DbContext and use SQL Server.

public class DiceContext : DbContext
{
    public DbSet<Word> Words { get; set; }
    public DbSet<WordList> WordLists { get; set; }
    public DbSet<WordListCategory> WordListCategories { get; set; }

    public DbSet<Page> Pages { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Word>()
            .HasOptional(p => p.WordList)
            .WithMany(d => d.Words)
            .HasForeignKey(p => p.WordListID)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Page>()
            .HasRequired(p => p.WordList)
            .WithMany()
            .HasForeignKey(p => p.WordListID)
            .WillCascadeOnDelete(false);
    }
}

Landing page Controller

Create a new LandingPageController class, which will handle page requests.

Here's mine:

public class LandingPagesController : BaseController
{
    public ActionResult Index(string language1, string language2, string stub)
    {
        Page p = null;
        // [...]
        return View(page.ViewName, page.ViewMasterName); 
    }
}

I need to know the name, ID, or stub of the page to be rendered. I decided to use a stub instead of the page ID because stubs are more friendly to search engines and users. I also need to know which language the visitor knows and which language they want to study, language1 and language2. From these three data points, I can find a Page, which indicates a WordList. I can find the appropriate words using language1 and language2.

Routing URLs to your Controller

I've talked about routing in a few other posts. You want to make sure that your URLs look good. By that, I mean they should be shallow and keyword-rich. Having your keywords in the URL makes your page look more useful to search engines.

Create your new routes in the Register_Routes method (this might be in global.aspx.cs or RouteConfig.cs).

My route will interpret all 3-segmented URLs as a landing page: the first value is language1 (the visitor's native language), the 2nd is language2 (the visitor's study language), and the third value is the stub of the page to be displayed.

I will probably have to make some revisions to this routing structure if I get enough interest to make me revive this project.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Landing Pages",
        url: "{language1}/{language2}/{stub}",
        defaults: new { controller = "LandingPages", action = "Index" }
    );

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

After this, I can load URLs like http://www.example.com/english/japanese/food and it will be routed to the LandingPageController, with the parameters properly filled in.

Finally, the easy part: create a view and go crazy in the ways you normally would to create and populate a view. Mine looked like this in the end:

Dynamic Sitemap.xml files for bots to detect the pages

You might be asking yourself, "If my homepage doesn't link to any pages that eventually link to the landing pages, how will they ever show up in search results?" Good question.

Right now, they never will. We need to create a dynamic sitemap file and submit it to our search engines of choice so that they know about the landing pages and can index them properly.

This post has gone on long enough, so I'll cover this topic later tonight or tomorrow.

If you run into any issues or have any questions, feel free to share them in the comments below!

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

About the author

Something about the author

Month List