How To Build a RESTful API with ASP.NET Core
In this post, I will demonstrate how to build a RESTful Web API using ASP.NET Core and Entity Framework Core. If you are new to .NET Core, you can read my Introduction to .NET Core post first.
UPDATE 30.06.2022: This post has got a lot of views and I am very glad this has reached so many people and that it is found helpful. However, this is for an old version. I wrote a new article explaining this subject with the latest LTS version of ASP.NET Core and you can read it here:
Our API will manage movie records stored in a relational database as described in the table below:
The sections of this post will be as follows:
- What is REST?
- Creating a Web API Project
- Adding a Model
- Adding a Controller and Scaffolding
- Creating Database with Migrations
- GET Method
- POST Method
- PUT Method
- DELETE Method
For the application development and testing, I will use
- Visual Studio 2017 Community Edition (free)
- .Net Core SDK 2.2 (free)
- Microsoft SQL Server Express (free)
- Postman (free)
If you are ready, let’s get started.
What is REST?
RESTful APIs conform to the REST architectural style.
REST, or REpresentational State Transfer, is an architectural style for providing standards between computer systems on the web, making it easier for systems to communicate with each other.
REST relies on client-server relationship. This essentially means that client application and server application must be able to evolve separately without any dependency on each other.
REST is stateless. That means the communication between the client and the server always contains all the information needed to perform the request. There is no session state in the server, it is kept entirely on the client’s side.
REST provides a uniform interface between components. Resources expose directory structure-like URIs.
REST is not strictly related to HTTP, but it is most commonly associated with it. There are four basic HTTP verbs we use in requests to interact with resources in a REST system:
- GET — retrieve a specific resource (by id) or a collection of resources
- POST — create a new resource
- PUT — update a specific resource (by id)
- DELETE — remove a specific resource by id
In a REST system, representations transfer JSON or XML to represent data objects and attributes.
REST has had such a large impact on the Web that it has mostly displaced SOAP-based interface design because it’s a considerably simpler style to use.
Now that we have made a quick review about REST, we can continue to the implementation.
Creating a web API project
First, open File -> New-> Project in Visual Studio:
Select ASP.NET Core Web Application, give a name to the project and the solution and then click OK.
In the next dialog, select .Net Core and ASP.Net Core 2.2 as shown in the red box and select API and then click OK.
At this point we have a starter project as follows:
We can see from the Startup.cs that MVC (Model-View-Controller) service is added to and used in the starter project:
If you need to learn more about the project structure of an ASP.NET Core MVC application, you can refer to this post.
Routing and URL Paths
Now, let’s run the app (Ctrl+F5) and see the default output. I will use IIS Express as the web server and Mozilla Firefox as the web browser:
After running, we get the following page:
Default URL https://localhost:{port}/api/values
comes from the launchUrl
property in the Properties\launchSettings.json:
And the values that we see in the browser come from the Get
method of the ValuesController
:
The [HttpGet] attribute denotes a method that responds to an HTTP GET request. The URL path for each method is constructed as follows:
Start with the template string in the controller’s Route attribute (Route("api/[controller]")
). Then replace [controller]
with the name of the controller, which by convention is the controller class name minus the Controller suffix. For this sample, the controller class name is ValuesController
, so the controller name is values
.
ASP.NET Core routing is case insensitive.
Adding a Model
Now, we will implement our data model class (Model part of the MVC app).
In Solution Explorer, right-click the project. Select Add > New Folder and name the folder Models.
Then right-click the Models folder and select Add->Class. Name the class Movie.cs and click Add.
Next, add the following properties to the class.
The Id
field is required by the database for the primary key.
Data Annotations
As you see, data annotations are used in the Movie.cs.
Data Annotations provides a built-in set of validation attributes that you apply declaratively to any class or property.
The Required
attribute indicates that a property must have a value.
The RegularExpression
attribute is used to limit what characters can be input. In the code above, Genre
must use only letters (First letter uppercase, white space, numbers and special characters are not allowed).
The StringLength
attribute lets you set the maximum length of a string property, and optionally its minimum length.
[DataType(DataType.Date)]
: The DataType attribute specifies the type of the data (Date
). With this attribute, the user is not required to enter time information in the date field.
We will see the effect of these data annotations when we try to add records to our database via the API client.
At this point, we need to build our solution.
Adding a Controller and Scaffolding
In this section, we will create our controller (Controller part of the MVC app) and scaffold our Movie model. We will use our model with Entity Framework Core (EF Core) to work with a database.
EF Core is an object-relational mapping (ORM) framework that simplifies the data access code. Model classes don’t have any dependency on EF Core. They just define the properties of the data that will be stored in the database.
In this post, we will write the model classes first and EF Core will create the database. This is called Code First Approach.
Right-click the Controllers folder and select Add -> Controller
In the Add Scaffold dialog, select API Controller with actions, using Entity Framework and click Add.
In the next dialog, select Movie from the Model class drop-down list and then click + sign to add the Data context class. You can keep the default for the names and then select Add.
At this point, the scaffolding tool works.
The automatic creation of the database context and CRUD (create, read, update, and delete) action methods is known as scaffolding.
We will now examine what Visual Studio added to our project automatically as the result of scaffolding:
1. Controller class -> Controllers\MoviesController.cs
The following methods are the action methods of the MoviesController:
GetMovie
(GET method)PostMovie
(POST method)PutMovie
(PUT method)DeleteMovie
(DELETE method)
As you see, the class is decorated with the [ApiController]
attribute. This attribute indicates that the controller responds to web API requests.
MoviesController class inherits from ControllerBase (does not provide View support which is not needed in Web API implementation).
2. EF Core database context class -> Data\MovieAPIContext.cs
The MovieAPIContext coordinates EF Core functionality (Create, Read, Update, Delete, etc.) for the Movie model. The MovieAPIContext is derived from Microsoft.EntityFrameworkCore.DbContext. The data context specifies which entities are included in the data model:
The preceding code creates a DbSet<Movie>
property for the entity set.
In Entity Framework terminology, an entity set typically corresponds to a database table and an entity corresponds to a row in the table.
The name of the connection string is passed into the context by calling a method on a DbContextOptions
object. For local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file:
Dependency Injection
ASP.NET Core is built with Dependency Injection (DI). Services (such as the EF Core DB context) are registered with DI during application startup. Components that require these services are provided with these services via constructor parameters.
The scaffolding tool registered the DB context (MovieAPIContext
) with the DI container in the ConfigureServices
method of Startup.cs as below:
In the MoviesController, the constructor uses dependency injection to inject the database context (MovieAPIContext
) into the controller:
MovieAPIContext
is used in each of the CRUD methods in the MoviesController.
Creating Database using Migrations
Now, we will create the database using the EF Core Migrations feature.
Migrations lets us create a database that matches our data model and update the database schema when our data model changes.
First, we will add an initial Migration.
Open Tools -> NuGet Package Manager > Package Manager Console(PMC) and run the following command in the PMC:
Add-Migration Initial
The Add-Migration
command generates code to create the initial database schema which is based on the model specified in the MovieAPIContext class. The Initial argument is the migration name and any name can be used.
After running the command, a migration file is created under the Migrations folder:
As the next step, run the following command in the PMC:
Update-Database
The Update-Database
command runs the Up
method in the Migrations/{time-stamp}_Initial.cs file, which creates the database.
Now, we will check the database created. Open View -> SQL Server Object Explorer.
You will see the newly created database under the following path:
as below:
As you see, the Movie table and the migration history table are created automatically. Then a record is inserted to the migration history table to show the executed migrations on the database.
GET Method
GetMovie
method returns all the movies in the database and GetMovie(int id)
method returns the movie having the Id given as input. They are decorated with the [HttpGet] attribute which denotes that a method responds to an HTTP GET request.
These methods implement two GET endpoints:
- GET /api/Movies
- GET /api/Movies/{id}
We can test the app by calling the two endpoints from a browser as follows:
https://localhost:{port}/api/movies
https://localhost:{port}/api/movies/{id}
The return type of the GetMovie
methods is ActionResult<T> type. ASP.NET Core automatically serializes the object to JSON and writes the JSON into the body of the response message. The response code for this return type is 200, assuming there are no unhandled exceptions. Unhandled exceptions are translated into 5xx errors.
Testing the GetMovie Method
Now we will test these endpoints. Before that, let’s insert some movie records to our table.
Go to the SQL Server Object Explorer and right-click the Movie table and select View Data:
Then add some movie records manually to the table:
We do not need to add data for the Id column as SQL Server automatically handles this for us.
Now, we can run the application. Start the application and enter https://localhost:{port}/api/movies
in the browser. We get the following JSON result:
This shows all of the movies in the application.
To test the second GET endpoint, enter https://localhost:{port}/api/movies/2
in the browser which brings the movie with Id = 2 as below:
If no item matches the requested ID, the method returns a 404 NotFound error code.
For convenience, you can change launchSettings.json as follows so that you won’t need to enter https://localhost:{port}/api/movies
to the browser every time you launch the application.
POST Method
PostMovie
method creates a movie record in the database. The preceding code is an HTTP POST method, as indicated by the [HttpPost] attribute. The method gets the value of the movie record from the body of the HTTP request.
The CreatedAtAction
method:
- Returns an HTTP 201 status code, if successful. HTTP 201 is the standard response for an HTTP POST method that creates a new resource on the server.
- Adds a
Location
header to the response. TheLocation
header specifies the URI of the newly created movie record. - References the
GetMovie
action to create theLocation
header's URI.
Testing the PostMovie Method
Now, we will test this endpoint using Postman as our API client.
Start the application and start Postman.
Before testing the POST method, let’s use the GET method and see the result that we saw in the browser in the previous method.
Select GET method and enter the URL as below (after changing your port number) and then click Send. As the result, we get status code 200 and the JSON response as below:
To test the POST method,
- Set the HTTP method to POST.
- Enter the URL (
https://localhost:{port}/api/movies
) - Select the Body tab. Select the raw radio button and set the type to JSON (application/json) in this tab.
- In the request body, enter JSON for a movie record(You can get this part from the JSON response that we got in the above test and change it. Do not forget to remove the Id part.)
- Click Send.
If you select the Headers tab of the response pane, you will see the location URI of the newly created movie record.
You can test this URI after selecting GET method and pasting the URI in the Postman or directly from the browser:
Also we can check this record from the Movie table in our database:
Validations Test
As you may remember, we used data annotations for validation purposes in our model class:
We can see the effect of these annotations when we try to post an invalid record from Postman as below:
PUT Method
PutMovie
method updates the movie record with the given Id in the database. The preceding code is an HTTP PUT method, as indicated by the [HttpPut] attribute. The method gets the value of the movie record from the body of the HTTP request. You need to supply the Id both in the request URL and the body and they have to match. According to the HTTP specification, a PUT request requires the client to send the entire updated entity, not just the changes.
The response is 204 (No Content) if the operation is successful.
Testing the PutMovie Method
Start the application and start Postman.
- Set the HTTP method to PUT.
- Enter the URL (
https://localhost:{port}/api/movies/3
) - Select the Body tab. Select the raw radio button and set the type to JSON (application/json) in this tab.
- In the request body, enter JSON for the movie record(For convenience, you can get this part from the response of the GET request for the above URL and then change the part you want to update. I changed the Genre to Thriller.)
- Click Send.
Again we can see the result from both Postman GET request or the browser:
We can check from the Movie table in the database too:
If we try to update a record that does not exist in the database we get 404 Not Found error:
DELETE Method
DeleteMovie
method deletes the movie record with the given Id in the database. The preceding code is an HTTP DELETE method, as indicated by the [HttpDelete] attribute. This method expects Id in the URL to identify the movie record we want to delete.
Testing the DeleteMovie Method
Let’s test the method using Postman.
- Set the method to DELETE.
- Enter the URI of the object to delete (
https://localhost:{port}/api/movies/3
) - Select Send
We do not need to supply a request body as you may have noticed. The response is 200 OK and the response body includes the record we deleted if the operation is successful.
If we try to get this movie record using the browser we get 404 Not Found error as expected:
We can check from the database that the record was deleted too:
This is where this post ends. You can find the full project in this GitHub repository.
I hope you found this post helpful and easy to follow. Please let me know if you have any corrections and/or questions in the comments below.
And if you liked this post, please clap your hands 👏👏👏
Bye!
UPDATE (16/06/2019): If you are interested in .NET Core and want to find out more, you can check this .NET Core publication. The posts in the publication are as below:
- Introduction to .NET Core
- ASP.NET Core MVC Web Application (Project Structure)
- Build a Web App with ASP.NET Core MVC and EF Core
- How To Build a RESTful API with ASP.NET Core (this post)
- Build a Web App with ASP.NET Core and MongoDB
- Repository Pattern Implementation in ASP.NET Core
If you want, you can follow this publication and be informed when a new post arrives.
References
https://www.codecademy.com/articles/what-is-rest
https://developer.ibm.com/articles/ws-restful/
https://spring.io/understanding/REST
https://ninenines.eu/docs/en/cowboy/2.6/guide/rest_principles/