ASP.NET Logo

ASP.NET blog engine

ButterCMS is an API-based ASP.NET blog engine that seamlessly integrates with ASP.NET applications. Use ButterCMS and ASP.NET MVC to rapidly build CMS-powerede blogs, pages, and more.

Table of Contents

Introduction

Learn how to quickly build a custom blog with great SEO. This guide uses C# with Microsoft's MVC web framework but Butter integrates with any .NET web app. Full working example code is available on Github

If you need help after reading this, contact us via email, livechat, or book a time with to pair with one of our developers.

Display posts

To display posts we create a simple /blog route in our app and fetch blog posts from the Butter API. See our API reference for additional options such as filtering by category or author. The response also includes some metadata we'll use for pagination.

Controllers/BlogController.cs:

public class BlogController : Controller
{
    private ButterCMSClient Client;

    private static string _apiToken = "b60a008584313ed21803780bc9208557b3b49fbb";

    public BlogController()
    {
        Client = new ButterCMSClient(_apiToken);
    }

    [Route("")]
    [Route("blog")]
    [Route("blog/p/{page}")]
    public async Task<ActionResult> ListAllPosts(int page = 1)
    {
        var response = await Client.ListPostsAsync(page, 10);
        ViewBag.Posts = response.Data;
        ViewBag.NextPage = response.Meta.NextPage;
        ViewBag.PreviousPage = response.Meta.PreviousPage;
        return View("Posts");
    }
}

Next we'll create a simple view at Views/Blog/Posts.cshtml that displays our posts and pagination links:

@{ 
    Page.Title = "Blog";
}

<h2>Posts</h2>

@foreach(var post in ViewBag.Posts)
{
    <a href="/blog/@Uri.EscapeDataString(post.Slug)">@post.Title</a> 
    @Html.Raw("by")
    <a href="/author/@Uri.EscapeDataString(post.Author.Slug)">@post.Author.FirstName @post.Author.LastName</a>
    <br/>
}

@if(ViewBag.PreviousPage != null)
{
    <a href="/blog/p/@ViewBag.PreviousPage">Prev</a>
}

@if(ViewBag.NextPage != null)
{
    <a href="/blog/p/@ViewBag.NextPage">Next</a>
}

We'll also create an additional route and controller method for displaying individual posts:

[Route("blog/{slug}")]
public async Task<ActionResult> ShowPost(string slug)
{
    var response = await Client.RetrievePostAsync(slug);
    ViewBag.Post = response.Data;
    return View("Post");
}

The view for displaying a full post includes information such as author, publish date, and categories. See a full list of available post properties in our API reference. We use the Page object for setting the HTML title and meta description in the <head> tag of the page.

@{ 
    Page.Title = @ViewBag.Post.SeoTitle;
    Page.MetaDescription = @ViewBag.Post.MetaDescription;
}

<h2>@ViewBag.Post.Title</h2>

Published @ViewBag.Post.Published.ToString("d/M/yyyy")
@if (@ViewBag.Post.Categories.Count > 0)
{
    @Html.Raw("in")
}
@foreach (var category in @ViewBag.Post.Categories)
{
    <a href="/category/@Uri.EscapeDataString(category.Slug)">@category.Name</a>
}

<br/>

<a href="/author/@Uri.EscapeDataString(ViewBag.Post.Author.Slug)">
    @ViewBag.Post.Author.FirstName @ViewBag.Post.Author.LastName
</a>

<br/>

<div>
    @Html.Raw(ViewBag.Post.Body)
</div>

Categories, Tags, and Authors

Use Butter's APIs for categories, tags, and authors to feature and filter content on your blog:

[Route("authors")]
public async Task<ActionResult> ListAllAuthors()
{
    var response = await Client.ListAuthorsAsync(false);
    ViewBag.Authors = response;
    return View("Authors");
}

[Route("author/{slug}")]
[Route("author/{slug}/p/{page}")]
public async Task<ActionResult> ShowAuthor(string slug, int page = 1)
{
    var authorResponse = await Client.RetrieveAuthorAsync(slug, false);
    var postsResponse = await Client.ListPostsAsync(page, 10, true, slug);
    ViewBag.Posts = postsResponse.Data;
    ViewBag.Author = authorResponse;
    ViewBag.NextPage = postsResponse.Meta.NextPage;
    ViewBag.PreviousPage = postsResponse.Meta.PreviousPage;
    return View("Author");
}

[Route("categories")]
public async Task<ActionResult> ListAllCategories()
{
    var response = await Client.ListCategoriesAsync(false);
    ViewBag.Categories = response;
    return View("Categories");
}

[Route("category/{slug}")]
[Route("category/{slug}/p/{page}")]
public async Task<ActionResult> ShowCategory(string slug, int page = 1)
{
    var categoryResponse = await Client.RetrieveCategoryAsync(slug, false);
    var postsResponse = await Client.ListPostsAsync(page, 10, true, null, slug);
    ViewBag.Posts = postsResponse.Data;
    ViewBag.Category = categoryResponse;
    ViewBag.NextPage = postsResponse.Meta.NextPage;
    ViewBag.PreviousPage = postsResponse.Meta.PreviousPage;
    return View("Category");
}

[Route("tags")]
public async Task<ActionResult> ListAllTags()
{
    var response = await Client.ListTagsAsync(false);
    ViewBag.Tags = response;
    return View("Tags");
}

[Route("tag/{slug}")]
[Route("tag/{slug}/p/{page}")]
public async Task<ActionResult> ShowTag(string slug, int page = 1)
{
    var tagResponse = await Client.RetrieveTagAsync(slug, false);
    var postsResponse = await Client.ListPostsAsync(page, 10, true, null, null, slug);
    ViewBag.Posts = postsResponse.Data;
    ViewBag.Tag = tagResponse;
    ViewBag.NextPage = postsResponse.Meta.NextPage;
    ViewBag.PreviousPage = postsResponse.Meta.PreviousPage;
    return View("Tag");
}

See our API reference for more information about these objects:

RSS, Atom, and Sitemap

Butter generates RSS, Atom, and sitemap XML markup. To use these on your blog, return the generated XML from the Butter API with the proper content type headers.

[Route("feeds/rss")]
public async Task<ActionResult> ShowRss()
{
    var xmlDoc = await Client.GetRSSFeedAsync();
    using (var sw = new StringWriter())
    {
        using (var xw = XmlWriter.Create(sw))
        {
            xmlDoc.WriteTo(xw);
            xw.Flush();
            return Content(sw.GetStringBuilder().ToString(), "text/xml");
        }
    }
}

[Route("feeds/atom")]
public async Task<ActionResult> ShowAtom()
{
    var xmlDoc = await Client.GetAtomFeedAsync();
    using (var sw = new StringWriter())
    {
        using (var xw = XmlWriter.Create(sw))
        {
            xmlDoc.WriteTo(xw);
            xw.Flush();
            return Content(sw.GetStringBuilder().ToString(), "text/xml");
        }
    }
}

[Route("sitemap")]
public async Task<ActionResult> ShowSitemap()
{
    var xmlDoc = await Client.GetSitemapAsync();
    using (var sw = new StringWriter())
    {
        using (var xw = XmlWriter.Create(sw))
        {
            xmlDoc.WriteTo(xw);
            xw.Flush();
            return Content(sw.GetStringBuilder().ToString(), "text/xml");
        }
    }
}

Comments

Butter doesn't provide an API for comments due to the excellent existing options that integrate easily. Two popular servies we recommend are:

Both products are free, include moderation capabilities, and give your audience a familiar commenting experience. They can also provide some additional distribution for your content since users in their networks can see when people comment on your posts. For a minimalist alternative to Disqus, check out RemarkBox or for an open-source option, Isso.

CSS

Butter integrates into your front-end so you have complete control over the design of your blog. Use the following CSS as boilerplate for post content styling.

.post-container {
  h1, h2, h3, h4, h5 {
    font-weight: 600;
    margin-bottom: 1em;
    margin-top: 1.5em;
  }

  ul, ol {
    margin-bottom: 1.25em;

    li {
      margin-bottom: 0.25em;
    }
  }

  p {
    font-family: Georgia, Cambria, "Times New Roman", Times, serif;
    font-size: 1.25em;
    line-height: 1.58;
    margin-bottom: 1.25em;
    font-weight: 400;
    letter-spacing: -.003em;
  }

  /* Responsive default image width */
  img {
    max-width: 100%;
    height: auto;
  }

  /* Responsive floating */
  @media only screen and (min-width: 720px)  {
    .butter-float-left {
      float: left;
      margin: 0px 10px 10px 0px;
    }

    .butter-float-right {
      float: right;
      margin: 0px 0px 10px 10px;
    }
  }

  /* Image caption */
  figcaption {
    font-style: italic;
    text-align: center;
    color: #ccc;
  }

  p code {
    padding: 2px 4px;
    font-size: 90%;
    color: #c7254e;
    background-color: #f9f2f4;
    border-radius: 4px;
    font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
  }

  pre {
    display: block;
    padding: 1em;
    margin: 0 0 2em;
    font-size: 1em;
    line-height: 1.4;
    word-break: break-all;
    word-wrap: break-word;
    color: #333333;
    background-color: #f5f5f5;
    font-family: Menlo, Monaco,Consolas, "Courier New", monospace;
  }
}

Migration

To import content from another platform like WordPress or Medium, send us an email.

About ASP.NET

ASP.NET is an open-source server-side web application framework designed for web development to produce dynamic web pages. It was developed by Microsoft to allow programmers to build dynamic web sites, web applications and web services.

Blog Engine for these technologies and more

Ruby, Rails, Node.js, Python, ASP.NET, C#, Flask, Django, Go, PHP, React, Phoenix, Elixir

Get started now

Sign up with Google Sign up with Github
or