An In Depth Guide Into a Ridiculously Simple API Using .NET Core
.NET Core is great. It is one of the best products of Microsoft’s shift to a more open source, cross platform company and whilst still limited in comparison to the traditional .NET framework there are still plenty of avenues for powerful development using C# or F# thanks to libraries such as ASP.Net Core and Entity Framework Core. Whilst both of these products could be seen as less feature full variants of their original versions, the fact is that the ability to utilise even the core functionality of both on a cross platform, lightweight and open source runtime is more than enough reasoning for consideration. Microsoft’s Documentation for .NET Core is great but the relatively young runtime still has a bit to go and whilst undertaking a new project using ASP.Net Core’s Web API I found that a lot of my learning was result of sifting through various sites, tutorials and videos even for something as simple as a CORS enabled CRUD API.
This tutorial will not rely on any IDE and will work across Windows, Mac and Linux variants of .NET Core. Something that continues to surprise me about how Microsoft has approached the documentation for .NET Core is the reliance on the usage of Visual Studio. Yes it has definitely been the go to tool of .NET development for years and I myself learnt C# using VS (as most people would’ve) but to fully appreciate the benefits of .NET Core I much rather would use a text editor and the command line. Funnily enough, Visual Studio Code is an amazing text editor and when used with the OmniSharp extension, the experience is as enjoyable as something that you would find in an IDE. One other thing, you should know C#. Not to any major extent but you should know a good foundation’s worth of C#. I will explain MVC really briefly and how routing works in ASP.NET Core but the fact is this tutorial will mainly focus on things relating solely to setting up your simple Web API for use with a solution like a website or mobile app.
Assuming you have .NET Core installed (if for some reason you haven’t already, go install it here now so this tutorial is actually of some use) you better get NodeJS too. That was definitely a sentence, the reasoning for this being that the people at OmniSharp have made things even better for .NET Core development with the development of a yo generator for .NET Core. Despite it being called “generator-aspnet” it has support for other .NET projects such as command line programs and class libraries, providing both C# and F# templates for all options.
Whilst you could most definitely build something similar yourself, just get yo, or clone this repo. I don’t mind, but we should actually get some programming going rather than spending another two paragraphs shilling Microsoft.
With the release of .NET Core 1.x tooling there is now a built in generator for .NET Core applications which is the standard for using
The same effect of the
yo generator can now be achieved by using the command:
dotnet new webapi
If you downloaded the generator, run:
This will present you with the marvellous generator and we will select Web API Application. Call it whatever you want but if you want to follow word for word with me call it TomatoAPI because we’re going to make an API for tomato breeds (is that the right word? breeds? Like a dog?) so I feel this name is definitely fitting. You should get a project created in the a folder named after the project, yo will also tell you to run the restore command and if that doesn’t make any sense don’t worry because I’ll explain that now.
.NET Core has a ridiculously simple cycle of building and running your app but for the initial run of the app you’ll need to run this dotnet restore command. In the simplest of terms, this restore command retrieves the packages and dependencies that are required to build and run the .NET Core program, all of these being contained within the project’s folder. The reason that .NET core is so lightweight is because it has a “modular deployment model” in which every project is based on various packages and only those packages unless you explicitly install more. Best way to describe this is that .NET Core would rather you make dinner with the exact ingredients and amounts that are required rather than you buying heaps of extra vegetables and such that you may use one day but probably will end up throwing away. Actually that was an incredibly horrible example but basically you’ll need to run restore a lot during .NET Core development especially in the initial stages when you’re adding more and more packages to your project. Running dotnet restore in your project will restore your required assets and after that you can enter dotnet run and your project will be hosted on a local server.
Kestrel (the Web Server which comes with ASP.NET Core) is based on an asynchronous I/O library known as Libuv which was built for NodeJS. It has little to no shortcomings for development and definitely does everything you need it to do and more. Going to http://localhost:5000/api/Values in your browser will give you an array of values, literally. 2 of them to be precise, but it’s a start.
Routing and Controlling Basics
This is boring though and so if we go to our controllers folder we will be able to change just what the Values page does by editing the ValuesController.cs file…
Modern ASP.NET has very much told the WebForms style of development to bugger off and embraces an Model-View-Controller (MVC) style presenting content. This does not mean our sites have to use MVC, it is just the way in which we will structure our code and how many features of ASP.NET function; even extensions of the basic web application framework like the Web API. Controllers define routes and the code between these routes, better wording would be to say that Controllers define the logic of our web app. Up the top of this class we see the attribute of Route. You shouldn’t really need to change this up for the tutorial but if you want change the parameter in route from “api/[controller]” to “ayelmao/[controller]” or something of the like and rerun your web app. You’ll notice that your previous link will not work and instead you’ll need to go to http://localhost:5000/ayelmao/Values to get the same page as before. That [controller] should not really be taken out as you’re making more work for yourself at that point. ASP.NET follows a file naming convention for controllers that is just adding the word “Controller” onto the end of a controller class, eg: a controller for mushrooms would be a MushroomController, a controller for bikes? A BikeController. This [controller] attribute makes things a hell of a lot easier as it chops off that Controller suffix and uses whatever’s left for the route. If you want to manually set your routes go ahead, try
and try to figure out how to reach your endpoint.
After you’ve had your fun with the routing let’s actually get into the functionality of the controller. From this automatically generated controller, we see that 5 methods have been created to accommodate for the Create (Post), Read (Get), Update (Put) and Delete (Delete, intuitive I know)functionality of our API. Controllers are not required to cater for all these possible HTTP methods, our yo generator follows the conventions set by the scaffolding found in Visual Studio and so by default gives us methods for all of them.
We specify which methods respond to GET requests using either of the following attributes
The first of these is used to define the methods which result to a get without any routing parameters or values, such as when we go to http://localhost:5000/api/Values. This usually will return all the data relating to that class in the API. If we wanted a specific object however we would need to provide a parameter such as an id and so we can define this with the parameters of the method attribute. In the instance of our ValuesController class, we are providing a value known as id which results in a function definition that looks like
public string Get(int id)
If we are to access the element from within our controller it must have an associated parameter of the same identifier. This is super powerful and you very easy to implement as it just works as intended. If you wanted to have 2 parameters, maybe for implementing a route such as http://localhost:5000/api/Values/categoryid/id we could do:
public string Get(int categoryid, int id)
Optional route parameters are as easy as declaring nullable integers in C#, if we wanted the id in the above example to be optional all we would need to do is add a question mark after the id and make the id integer nullable like so:
public string Get(int categoryid, int? id)
if (id.HasValue) // Checking for the definition of id
The next thing we need to understand is how our controllers actually return data and if you know how to make a function in C# return data, this is self explanatory. Both our get definitions in the ValuesController return strings, relating to values as expected and we explicitly specify this. We could set this to be an int or a float or return nothing with void (as we see in Post, Put and Delete). In the case of our parameter-less Get function we return an IEnumerable of strings but you can most definitely use a list if you’d prefer. The structure of API endpoints in ASP.Net Web API reiterates that in the end this is the same C# code that you’re used to using, nothing is out of the ordinary in how you can write your API methods. All your database connections, mathematics, logical expressions and such can be kept within the methods and in the end whatever is returned is what will be returned from the HTTP Request. This is all good to read about but I’m sure much more will be learnt through making our own API.
Finding The Right Tools
For this tutorial you’ll actually be learning the basics of two amazing .NET features; ASP.NET and Entity Framework. We’ll be using Entity Framework (EF) for the database connectivity of our API. That’s right, no more statically defined values arrays. Entity Framework, whilst being an incredibly powerful library, must be installed as a nuget package with its relative tooling.
We can install Entity Framework by adding the dependencies to our project.json file. You will notice that by default the file includes a lot of packages and tools however we only need to add about 4 lines.
In our “dependencies” object we need to add references to the SQLite variant of EF and the associated designer for this database system:
Entity Framework has many “database providers” for use which means that different packages would need to be installed to cater for different database systems but for the purposes of this tutorial; SQLite does the job.
Whilst this is good, we also require the necessary tools so that we can create database migrations and run updates. On top of this, it is helpful to add the .NET Core Watcher as it will allow our solution to recompile itself with every change made to it, note that this tool is completely optional. To add both of these, find the “tools” object in project.json and append the following:
“Microsoft.EntityFrameworkCore.Tools.DotNet” : “1.1.0-preview4-final”,
“Microsoft.DotNet.Watcher.Tools” : “1.1.0-preview4-final”
Assuming you correctly pasted in the required dependencies and tools, your project.json should look like this:
Save your file and run dotnet restore, you shouldn’t receive any restoration errors which means we can now look into defining a Model for our database.
Modelling Our Entities
WebAPI (much like the rest of ASP.NET Core) is built on the foundations of the Model-View-Controller design pattern (MVC for short). The difference is that WebAPI, whilst heavily relying on Models and Controllers, does not really implement the view side of things as that is delegated to the applications utilising the API. Despite this what you are learning about creating Models and Controllers in this tutorial applies to all aspects of ASP.Net MVC and so will serve you well if you ever delve into MVC Web Applications with .NET.
Models are used to define the required data components within our solution, the entities and how the interact within the application and also the means in which the real world problem will be solved by the solution. A model is completely independent of the UI and interactions with models are facilitated by controllers.
Creating a model is as easy creating a class in ASP.NET MVC, it is common convention to place these models in a folder named “Models” in your solution so first we shall make this folder. In this folder we will define our model, the Tomato model in the file Tomato.cs:
Things to note about this model are:
- It is just a class
A model is just a class in C#. Whilst we do see the addition of Data annotations the core component of models are the Class, no need to extend any existing classes.
- There is no code, just properties
The model is a representation, there should be no functionality defined within it as that is the job of the controller. Instead all we have are properties with blank getters and setters.
- We can use our own data types
In this example I have used an enumeration to describe the taste of the tomato, EF will convert this to and from its numerical form within data transactions so there will be no issues using this with a database.
- Data Annotations help describe the data
Whilst optional for the most part, data annotations (additions like [Key] and [Required]) are attributes which tell EntityFramework how to treat the fields of each model. There are many that can be used but the ones I have included are:
[Key] : Primary Key attribute, defines which field must be the unique primary key. Will automatically increment by default
[Required] : Required field attribute, defines which fields must have data. Is used extensively by ASP.Net MVC for form validation as well ensuring data is errorless and complete.
[MaxLength(integerValue)] : Max Length attribute, like required is heavily used for validation and will restrict strings to a certain length.
[DataType(DataType.SomeTypeOfData)] : Data Type attribute, extensively used by ASP.NET MVC for generating form specific elements. In this example isn't too useful but helps by specifying what kind of common type of data it is in a web application sense (eg. Credit Card, Password, PostalCode...)
With our model defined, we need to provide EntityFramework a database context so that it knows to build a table using our TomatoModel. In the root of our project, create a file called TomatoDb.cs and fill it with the following code:
This is a bit more complex than the Model code but still not too much to handle nevertheless.
- We define a database context by creating a class that extends the DbContext class from EntityFramework.
- In this class we have an override void OnConfiguring which takes in some Context builder options as parameters. In this example we have told EntityFramework to use SQLite as the database provider and save all changes to a file named Tomatos.db. This is defined in a connection string of formatting which in most cases is much more complicated.
- We then create a DbSet for each our models and name it something that relates to it (eg. Tomatos, TableTomato, TomatoCollection). A DbSet is a generic collection which is treated as the database table which relates to our model. Its identifier is how we will be retrieving data from the database using EF.
This is a lot to remember but the fact is that your context won’t vary greatly with your projects and 9 times out of 10 you’ll use a structure identical to this one.
You’re probably wondering what a Database Context actually does and as I said, this is the link between your code and the database. Once we run our first migration and update, you will be able to access your SQLite database as if it were a disposable C# class. That means a connection can be made as simply as :
TomatoDb db = new TomatoDb();
Or in what I think is much cleaner:
using (Tomato db = new TomatoDb())
// Database queries and such
This will make much more sense when we create our tomato controller but we lastly need to update EntityFramework with the changes to our Database.
Migrations and Updates
Run the following command in your project’s root:
dotnet ef --version
This will confirm you have properly installed the required tools for Entity Framework Core and are ready to make your first migration.
In Entity Framework, a database migration is basically a version of your database which contains the necessary structure and changes to your schema to be considered up to date with the code associated with that database. This creates what Microsoft call a “Code First” solution rather than a solution which is built around a pre-existing database. If you use migrations correctly, your code and database will always be in sync and interacting with your database will be as simple as querying an IEnumerable.
EF will automatically take note of changes to your Model and such by using the database context so adding our first migration can be done with one command:
dotnet ef migrations add “First Migration”
The final text in quotes a description for the migration, you’ll notice that assuming that command ran fine there should be a Migrations folder in the root of your project.
Migrations work a lot like version control systems, especially Git. Adding a migration is like performing a git commit and whilst this adds a commit into the list of outstanding commits; we need to eventually push the changes. This push equates to an update in EF terminology and can be performed by running the command:
dotnet ef database update
You will now see the generated SQL script that has been used to create your database; all changes visible in the Tomatos.db file which has been added to your debug folder.
Great job! Your API now has a database which is generated based on the same code you’ve written your API in, pretty swish aye? Only problem is we don’t actually have an API endpoint for our database.
The Controller. You know? The Actual API
To get this endpoint we need to create an associated controller for the Tomato model. The structure of this can be generated using the yo asp-net generator; open a command prompt/terminal in your controllers folder and run the following:
yo aspnet:webapicontroller TomatosController
Whilst we do get what is basically a copy and paste of Values controller, all the associated namespaces and naming is taken care of which saves us a bit of meddling around.
The first method we need to account for is also the easiest, the GET method:
// GET api/Tomatos
public IEnumerable<Tomato> Get()
using (TomatoDb db = new TomatoDb())
There a couple of things to note about this:
- No need to worry about the return type
Whatever you return from the method will be readable by the client, an IEnumerable/List will be converted to a JSON array, a single object will be serialized as a JSON object and so on.
- Our database query is 2 lines
All that setup was worth it in the end. Using the disposable TomatoDb class, we are returning the contents of the Tomatos table in the database. This table is not queried using SQL, just LINQ.
Ensure your server is running and go to http://localhost:5000/api/Tomatos . You should see the contents of your tomatos table (plot twist: it’s empty):
Whilst there isn’t anything available to query, it would still be worth creating the singular GET endpoint. If you think you know what it would look like, give it a go otherwise here’s what I imagined:
// GET api/Tomatos/5
public Tomato Get(int id)
using (TomatoDb db = new TomatoDb())
return db.Tomatos.First(t => t.Id == id);
LINQ Expressions and Statements are your SQL when it comes to working with EF, this query could’ve been completed using a foreach but why would you want to do that when you have so much power with a lambda.
Now posting to the API is a bit trickier as we receive data in the body of the request. This is in the form of JSON usually and the JSON should be structured similarly to how it would be received in a GET.
Before we write up our post controller however, I found that there was a great deal of problems dealing with WebAPI originally due to Cross Origin Resource Sharing (CORS) policies. CORS allows browsers to figure out what requests from which origins are safe and whilst the intentions are good CORS can be a pest to work with, as you may have problems querying your API if it is not configured properly. There are many tutorials online on how to work with CORS in ASP.Net Core but the most reliable snippet of code I found is the following that must be placed in the Configure method of your Startup.cs file:
app.UseCors(options => options.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials());
Assuming you pasted correctly, Configure should look like this:
This snippet should enable CORS for requests from anywhere, this isn’t necessarily secure but for basic learning it will do. For production it is most definitely advised you edit your policies to restrict access for certain requests.
With that out of the way, let’s take a look at the post endpoint:
// POST api/Tomatos
public void Post([FromBody]JObject value)
Tomato posted = value.ToObject<Tomato>();
using (TomatoDb db = new TomatoDb())
Using JObject ensures that however the data is posted, we are able to serialize it to the related class. JObject is basically a JSON Object data type in C# so it is very flexible and can be assigned a specific data type with the ToObject method which requires the datatype to serialize to.
- Adding to the database is super easy
We don’t need to supply an id as that is automatically assigned and adding the class to the table utilises the simple Add method.
- Don’t forget to save your changes
EF requires you to commit your changes by default and we will oblige by that for stability and security reasons. Note there does exist an alternate async method for saving changes if the need arises.
Our post endpoint is probably the hardest endpoint to configure as we rely on JSON.Net and JObject for serialization. Please note that for this method we will need to also add…
to our references.
To test our post endpoint, I am using Postman although there are that many API testing clients and extensions available for browsers it’s your choice in the end really. Our controller is pretty flexible with what data it receives but note that:
- The Id does not need to be defined explicitly as EF will automatically increment it anyway
- Enumerations need to be defined using their numeric equivalent
With this in mind, this is fine test data for our POST endpoint:
After posting this data to the site, we will now see that the GET endpoint returns something besides an empty array.
What About Put and Delete?
Put and Delete are more of the same. There are many ways you can approach Put but a really simple way would be to use the built in Update method:
public void Put(int id, [FromBody]JObject value)
Tomato posted = value.ToObject<Tomato>();
posted.Id = id; // Ensure an id is attached
using (TomatoDb db = new TomatoDb())
The id can be used in many ways for the PUT request. Examples online I’ve found show it being used to compare if the id in the route is the same as the one in the data, others just ignore it.
The update method, like add, will only be applied when SaveChanges is called and basically trys to attach itself to the related record through what I assume is the usage of the primary key.
Delete is very easy, if we are aiming to remove the element from the database entirely we must ensure it exists first and if so then call the Remove function on the Tomatos DbSet:
// DELETE api/Tomatos/5
public void Delete(int id)
using (TomatoDb db = new TomatoDb())
if (db.Tomatos.Where(t => t.Id == id).Count() > 0) // Check if element exists
db.Tomatos.Remove(db.Tomatos.First(t => t.Id == id));
We use the Where LINQ function in this instance to check whether the element exists by checking if the Count is greater than nothing.
There we have it, our Tomato API Controller in all its glory
You should now be able to Create, Read, Update and Delete Tomato database entries through your REST API. There is still much to learn, such as returning an ActionResult relating to the method as well as ModelState validation but for a basic API with no corners cut I dare say that this is a good result.
All the source code for this project is available here.
I hope this tutorial has shown you the ease that comes with using ASP.NET and Entity Framework for a task like this as well as the power of .NET Core despite how young it is as a technology.
I’d be beyond thankful if you shared this article with anyone who may be interested in .NET Core technologies, please feel free to let me know about any of the things you liked or disliked about this piece.
~ Alex Billson, Student at Swinburne University and Software Developer