Create Your own blog: .NET Core

Kristaps Dzērve-Štrāls
11 min readJul 16, 2018

--

In this series I’ll explore various back end technologies, mainly to help someone decide what technology to choose in their next project. I plan on looking at various different back end frameworks for creating a RESTful api for a simple Blog application with the usual CRUD endpoints.

As the first one to tackle I’ve chosen the .NET Core WebAPI setup. Mainly, because I use C# daily and I am very familiar with this statically typed language. I’ll go over the framework setup and creating the Blog back end.

In case of .NET, which is a Microsoft technology, it is easiest to get started with creating a .NET Core WebAPI on Microsoft Windows by installing Visual Studio. To make this more fair for other OS users I won’t be using any of that and I’ll create the app using the Dotnet CLI.

Step 1: The setup

First, We have to get the relevant .NET Core SDK. This can be downloaded for any operating system from this page. Just be sure to grab the .NET Core variant to be able to run the app cross platform. After downloading and installing that you can check your installation in your favorite console environment:

I’m using Git Bash as my terminal emulator on Windows 10

Now that that’s done, We can simply set up an app using one of the provided generator functions using dotnet new . To check what is available, We can run dotnet new --list :

In this case I am interested in creating a REST API, which is specifically what dotnet new webapi command is used for. There are templates that can be used to create a full client-server app (like with React or Angular as the frontend), but we won’t bother with that. I plan on doing the front-end evaluation independently and I don’t want to tie that up with any one back-end technology. So for now I’ll just run dotnet new webapi -o "BlogApi" to get started.

You are greeted with this structure.

Looking at the generated filed, those that are most relevant are Startup.cs that configures the app, Program.cs that’s the entry point of the app and ValuesController.cs that is the example controller for the web api. If We now run the app using dotnet run command and we go to the URL that’s displayed in the console window plus /api/values , we can get to the endpoints defined in that controller. By default, the GET endpoint for all values.

To get this pretty print of the JSON data, I use JSONView Chrome extension.

Step 2: The actual work

How do we actually get these values here? To answer that question, all we have to do is take a look at ValuesController.cs file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

namespace BlogApi.Controllers
{
[Route("api/[controller]")]
[ApiController] public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}

// GET api/values/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return "value";
}

// POST api/values
[HttpPost]
public void Post([FromBody] string value)
{
}

// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}

// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}

Here We can see various attribute decorators, all of which relate to the relevant CRUD actions as well as the route attribute on top of the class that actually binds these actions to the endpoint. The controller class itself inherits from ControllerBase that allows it to do all of the RESTful magic underneath in the ASP.NET Core MVC stack.

As for the GET action itself, that just returns an array of strings! We will, of course, not return a string array like that for the blog itself, but rather an array of blog post models that we will set up next.

First, We need to set up a model to hold our post data. This can hold anything that you want/need for the front end. In this case we will set up the model to contain a Title, Summary, Body, Author, an array of Tags and an ID of sorts. Now, all of these can be references to other models, as will be the case for the Author, but I’ll just set the other stuff to be simple type values (like string, int etc.).

With that in mind, lets create a new folder in our root directory (alongside Controllers) that we call “Models”. Create two files in the new directory: Post.cs and Author.cs . Our Author model will just consist of string AuthorName and an Id field.

namespace BlogApi.Models
{
public class BlogPost
{
public int Id {get; set;}
public string Title { get; set; }
public string Summary { get; set; }
public string Body { get; set; }
public Author Author { get; set; }
public string[] Tags { get; set; }
}
}

As for the Author model:

namespace BlogApi.Models
{
public class Author
{
public int Id {get; set;}
public string Name {get; set;}
public string Description {get;set;}
}
}

For simpler project management, I added the models to a different Namespace than the controllers. Now, we can use these models in our ValuesController.cs file instead of some arbitrary string array.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using BlogApi.Models;

namespace BlogApi.Controllers
{
[Route("api/[controller]")]
[ApiController] public class ValuesController : ControllerBase
{
List<BlogPost> Posts = new List<BlogPost>
{
new BlogPost
{
Id = 1,
Title = "Example post",
Summary = "This is an example post",
Body = "This is just some post. This part should contain all the markup generated by the WYSIWIG editor.",
Author = new Author
{
Id = 1,
Name = "Kristaps",
Description = "Hi, my name is Kristaps"
},
Tags = new string[]
{
"example",
"post"
}
}
};

// GET api/values
[HttpGet]
public ActionResult<IEnumerable<BlogPost>> Get()
{
return Posts;
}

// GET api/values/5
[HttpGet("{id}")]
public ActionResult<BlogPost> Get(int id)
{
return Posts.FirstOrDefault(post => post.Id == id);
}

// POST api/values
[HttpPost]
public void Post([FromBody] BlogPost value)
{
}

// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] BlogPost value)
{
}

// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}

Now when We launch the server using dotnet run and go the localhost:5000/api/values , we see the JSON serialized posts:

Congratulations! You now have a fully functional API to get predefined values from the server.

Step 3: Storage

I imagine that you don’t actually want to hard-code the post data in the application. To save the data in some sort of permanent store, We must use a database. Now, there are MANY options when it comes to databases. I opted for a SQL solution PostgreSQL. It’s an advanced open source database used for relational data storage. It has many drivers for various programming languages and it’s quite flexible when set up. I’ll be using PostgreSQL for all of the different back-end projects.

To get started using PostgreSQL, We first need to set it up. I won’t go into too much details in this post, but the general gist of it is download it, install it and start the server.

Now the part that will differ across our different projects. To set up PostgreSQL on our .NET Core project, we’ll be using Entity Framework Core (EF Core) object-relational mapper (ORM) to abstract our interaction with the DB. Luckily for us, the EF is baked into .NET Core apps 2.0 and up, so we only need to set up the appropriate driver for PostgreSQL. To do so, we need to add a new package to our project by running dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL — version 2.1.1 . Doing so will add a new reference in the .csproj file:

<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.1" />
</ItemGroup>

</Project>

Now that we have the necessary references, it’s time to set up the database context. To do that, I created a new file (in a new folder) Services/BlogContext.cs . That class needs to inherit from DbContext that is the base for EF Core database communication. The final context class then looks something like this:

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using BlogApi.Models;

namespace BlogApi.Services
{
public class BlogContext : DbContext
{
public BlogContext(DbContextOptions<BlogContext> options) : base(options) { }

public DbSet<BlogPost> BlogPosts { get; set; }

public DbSet<Author> Authors { get; set; }
}
}

To have access to this context in our API controllers, we need to use Dependency Injection and register the context in our Startup.cs class. Here is where we also set up the context options to use the PostgreSQL provider.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

using BlogApi.Services;

using Microsoft.EntityFrameworkCore;

namespace BlogApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

// Postgresql EF Core provider
var connectionString = Configuration.GetConnectionString("BlogContext");
services.AddEntityFrameworkNpgsql().AddDbContext<BlogContext>(options => options.UseNpgsql(connectionString));
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}

app.UseHttpsRedirection();
app.UseMvc();
}
}
}

Notice, that I’m providing the connection string for the database from configuration. To have this working, open the appsettings.json file and add the DB configuration key.

{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"BlogContext": "Server=localhost;Database=blog;Username=postgres;Password=admin"
}
}

Your username and password might differ based on your PostgreSQL setup, but these are the default values. We are almost ready to set up the database with EF Core, but first we need to change the models a bit. For EF migrations to work best, the models should have an Id field that has a name like ClassName + Id . Also, to reference our author we need to add another field AuthorId to our model so that the foreign key is properly set up.

BlogPost:

namespace BlogApi.Models
{
public class BlogPost
{
public int BlogPostId { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string Body { get; set; }
public string[] Tags { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
}

Author:

namespace BlogApi.Models
{
public class Author
{
public int AuthorId {get; set;}
public string Name {get; set;}
public string Description {get;set;}
}
}

With that all done, we can use the built in Entity Framework CLI to set up some migrations. Run dotnet ef migrations add InitialCreate in the project directory. If all worked out as it should, you should now have a new directory in the project root called “Migrations” that holds three files. The _InitialCreate.cs is the one that builds up the query. There you can see what tables are created and the constraints as well (for example, the foreign key on AuthorId). If You would now search the database for the relevant tables, You might be surprised to not find them yet. That is because the migration still needs to be synchronized with the database. To do this, We need to run dotnet ef database update command to actually execute the migrations. NOW the tables are there.

WHEW! With all of that done, we are closing in to the final stretch. The only thing left is to use the new context in our controller to work with the persistent storage. First, I’ll change the name of the controller to PostsController as that seems more appropriate. Then, We must inject BlogContext in the controller using DI and us that for the relevant controller actions. The final code for the controller looks something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

using BlogApi.Models;
using BlogApi.Services;

namespace BlogApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class PostsController : ControllerBase
{
readonly BlogContext _context;

public PostsController(BlogContext context)
{
_context = context;
}

// GET api/values
[HttpGet]
public ActionResult<IEnumerable<BlogPost>> Get()
{
return _context.BlogPosts;
}

// GET api/posts/5
[HttpGet("{id}")]
public ActionResult<BlogPost> Get(int id)
{
return _context.BlogPosts.FirstOrDefault(post => post.BlogPostId == id);
}

// POST api/posts
[HttpPost]
public void Post([FromBody] BlogPost value)
{
_context.BlogPosts.Add(value);
_context.SaveChanges();
}

// PUT api/posts/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] BlogPost value)
{
var post = _context.BlogPosts.FirstOrDefault(p => p.BlogPostId == id);
if (post == null)
return;

post.Title = value.Title;
post.Summary = value.Summary;
post.Body = value.Body;
post.Author = value.Author;
post.Tags = value.Tags;

_context.BlogPosts.Update(post);
_context.SaveChanges();
}

// DELETE api/posts/5
[HttpDelete("{id}")]
public void Delete(int id)
{
var post = _context.BlogPosts.FirstOrDefault(p => p.BlogPostId == id);
if (post == null)
return;

_context.BlogPosts.Remove(post);
_context.SaveChanges();
}
}
}

All of the actions are working! The _context.SaveChanges() call is what is actually updating the database, so don’t forget that.

Additionally to this controller I created another one, to provide me with some starting data. I called this SetupController .

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

using BlogApi.Models;
using BlogApi.Services;

namespace BlogApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class SetupController : ControllerBase
{
readonly BlogContext _context;

static Author Kriss = new Author
{
AuthorId = 1,
Name = "Kristaps",
Description = "Hi, my name is Kristaps"
};

static BlogPost Post = new BlogPost
{
BlogPostId = 1,
Title = "Example post",
Summary = "This is an example post",
Body = "This is just some post. This part should contain all the markup generated by the WYSIWIG editor.",
AuthorId = Kriss.AuthorId,
Author = Kriss,
Tags = new string[]
{
"example",
"post"
}
};

public SetupController(BlogContext context)
{
_context = context;
}

// GET api/values
[HttpGet]
public ActionResult<string> Get()
{
// _context.Authors.Add(Kriss);
// _context.BlogPosts.Add(Post);
if (!_context.Authors.Any())
{
_context.Authors.Add(Kriss);
_context.BlogPosts.Add(Post);

_context.SaveChanges();
}


if (_context.Authors.Any())
return $"There's an author there: {_context.Authors.First().Name}";

return "No author there...";
}
}
}

All this does is it sets up the database with one Author and one BlogPost if they are not present. Now, when we run our App using dontet run and go to localhost:5000/api/setup for the first time, We’ll add seed data to the database. After that, visit localhost:5000/api/posts to see the data in action!

Finally

You now have a fully functional API for simple blog posts! There are many things left to do, but this is the core. You can:

  1. Add authentication
  2. Use a tool like Postman to test your APIs (for instance, delete action)
  3. Add more models, for instance, Comments
  4. Add a front-end (this will come later in this series)
  5. Whatever else you might require

So keep on working and I’ll see you in the next episode, where We’ll tackle the same API using Ruby on Rails!

P.S. The final project files are available on my Github page.

--

--

Kristaps Dzērve-Štrāls

Software developer and Tech enthusiast looking to expand and share knowledge.