A Developer’s Guide for Creating Web APIs with ASP.NET Core 5
Tags: aspnetcore, webapi, developer, guide
Published: 2021-08-25 18:47:14

The Web APIs (aka RESTful Services) are the type of applications that don’t return views in response, instead, they usually consume and return data in a machine-readable format such as XML or JSON. This concept is not new and the main goal of building Web APIs is to serve data to multiple client apps in a centralized way. As the number of the connected apps increase, the usage of the Web APIs can also grow significantly so it is very important to keep in mind the maintainability and scalability in mind. ASP.NET Core provides some powerful features to write Web APIs that comply with the REST standards. This tutorial will serve as a useful guide on how to build and consume Web APIs using the latest version of ASP.NET Core 5.

[Download Source Code](https://github.com/ezzylearning/AspNetCore5WebApisDemo)

## **Introducing ASP.NET Core Web API**

An ASP.NET Core Web API consists of one or more controller classes that derive from [**ControllerBase**](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase). The ControllerBase class provides many properties and methods that are useful for handling HTTP requests You can also derive your class from a [**Controller**](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controller) class which derives from **[ControllerBase](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase)** but that is only recommended if you want to create the same controller for Web APIs and Views.

![ASP.NET Core Web APIs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ihsgi2uv5wgavo9nq6ie.png)

Here are some common methods that ControllerBase provides.

| **Method** | **Notes** |
| --- | --- |
| BadRequest | Returns 400 status code. |
| NotFound | Returns 404 status code. |
| PhysicalFile | Returns a file. |
| TryUpdateModelAsync | Invokes model binding. |
| TryValidateModel | Invokes model validation. |

The [Microsoft.AspNetCore.Mvc](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc) namespace provides some attributes that can be used to configure the behavior of web API controllers and action methods. The following example uses attributes to specify the supported HTTP action verb and any known HTTP status codes that could be returned:
```
[HttpPost] 
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Book> Create(Book book)
{
    ...
}
```
Here are some common attributes available.

| **Attribute** | **Notes** |
| --- | --- |
| [Route] | Specifies URL pattern for a controller or action. |
| [Bind] | Specifies prefix and properties to include for model binding. |
| [HttpGet] | Identifies an action that supports the HTTP GET action verb. |
| [Consumes] | Specifies data types that an action accepts. |
| [Produces] | Specifies data types that an action returns. |

One of the important attributes is [[ApiController]](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.apicontrollerattribute) that can be applied to a controller class to enable some special features such as Attribute Routing, Automatic HTTP 400 response, etc. These additional features and behavior improve the developer experience for building APIs.

## **Creating ASP.NET Core 5 Web API Project**

If you are using Visual Studio 2019, then from the **File** menu, select **New** \> **Project**. Select the **ASP.NET Core Web Application** template and click **Next**. Give your project a name and click **Create**.

In the **Create a new ASP.NET Core Web Application** dialog, confirm that **.NET Core** and **ASP.NET Core 5.0** are selected. Select the **ASP.NET Core Web API** template and click **Create**.

![Creating ASP.NET Core 5 Web API Project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hzsn0573d37156gwhctm.png)

The project template will create a default weather forecast API for you. Press **Ctrl+F5** to run without the debugger. You will see the following URL in the browser address bar

[https://localhost:/WeatherForecast](https://localhost:/WeatherForecast)

and that JSON similar to the following will return from the API:
```
[
   {
      "date":"2020-12-26T16:57:09.1646732+05:00",
      "temperatureC":20,
      "temperatureF":67,
      "summary":"Cool"
   },
   {
      "date":"2020-12-27T16:57:09.1669352+05:00",
      "temperatureC":51,
      "temperatureF":123,
      "summary":"Hot"
   },
   {
      "date":"2020-12-28T16:57:09.1669397+05:00",
      "temperatureC":29,
      "temperatureF":84,
      "summary":"Hot"
   }
]
```
The default Web API project template has created the following **WeatherForecast** model class that has some basic weather related properties.
```
public class WeatherForecast
{
    public DateTime Date { get; set; }
    public int TemperatureC { get; set; }
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    public string Summary { get; set; }
}
```
The project template will also create a default **WeatherForecastController** class that is derived from the **ControllerBase** class. The class is also decorated with **[Route]** and **[ApiController]** attributes mentioned above.
```
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{

}
```
Another important thing to notice is that the **ConfigureServices** method of Startup.cs class is calling **AddControllers** method and not **AddMvc** method. This is because if you are not using MVC related features in your Web APIs and you want to keep your Web APIs lightweight with minimum required features then you don’t need to register additional MVC related features.
```
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
}
```
Another important piece of code in Startup.cs is where a feature called endpoint routing is configured using the following statements. This allows us to define endpoint routes using the **[Route]** attribute on top of Web API controllers and actions. We have already seen an example above where **[Route("[controller]")]** attribute is set on **WeatherForecastController** class.
```
app.UseEndpoints(endpoints =>
{
   endpoints.MapControllers();
});
```
The final noticeable thing in the project is the settings configured in **Properties\\launchSettings.json** file which specifies different launch profiles and **launchUrl** etc.
```
"IIS Express": {
    "commandName": "IISExpress",
    "launchBrowser": true,
    "launchUrl": "weatherforecast",
    "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
    }
}
```
## **Implement CRUD Operations using ASP.NET Core Web API**

Now you have the basic knowledge of Web APIs and you have also seen the default Web API created by you using the Visual Studio Web API project template. It is now time to implement our Web APIs and learn Web APIs in more detail. For this tutorial, I have decided to write a fictional web API for a library management system. We will create a RESTful service that allows client applications to manage the library books catalog. The API needs to expose endpoints to create, read, edit, and delete books.

| **API** | **Description** | **Request body** | **Response body** |
| --- | --- | --- | --- |
| **GET** /api/books | Get all books | None | Array of books |
| **GET** /api/books/{id} | Get a book by Id | None | Book |
| **POST** /api/books | Add a new book | Book | Book |
| **PUT** /api/books/{id} | Update an existing book   | Book | None |
| **DELETE** /api/books/{id}     | Delete a book     | None | None |

The first thing we need is the Book model class. A model class is a class that represents the data associated with an entity e.g. Book. Create a Models folder in the project and add the following class in the Models folder.
```
public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public int YearPublished { get; set; }
}
```
The Id property functions as the unique key in a relational database. A book model in a real library system can have dozens of other properties but to keep the example simple, I won’t add any other functionality in the Book class.

## **Creating Database Context**

To handle database operations, we need the Entity Framework data context class. This class represents a session with the database and can be used to query and save instances of your entities e.g. Book. This class is created by deriving from [Microsoft.EntityFrameworkCore.DbContext](https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext) class. Creating a real SQL Server database and managing our books inside the real database is also out of the scope for this tutorial so I have decided to use [Microsoft.EntityFrameworkCore.InMemory](https://docs.microsoft.com/en-us/ef/core/providers/in-memory/?tabs=dotnet-core-cli) data provider. This database provider allows Entity Framework Core to be used with an in-memory database. The in-memory database is useful when you want to test some basic database operations without the need of creating a real database. You can download and install both of these packages using the NuGet package manager.

If you don’t know how to use NuGet package manager to install third-party libraries and packages then I will recommend you to read my post [Working with Packages and Libraries in ASP.NET Core](https://www.ezzylearning.net/tutorial/working-with-packages-and-libraries-in-asp-net-core)

Once you have the above packages installed, create the following **LibraryContext** data context class in your project.
```
public class LibraryContext : DbContext
{
    public LibraryContext(DbContextOptions<LibraryContext> options)
        : base(options)
    {
    }

    public DbSet<Book> Books { get; set; }
}
```
Next, we need to register our database context object with the dependency injection container. This will allow us to inject and use our library context in controllers and services. Notice we also specified below that we want to use an in-memory database provider with the database name “**LibraryDb**”. This in-memory database will be used to store our books entities later in this tutorial.
```
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<LibraryContext>(opt => opt.UseInMemoryDatabase("LibraryDb"));

    services.AddControllers();
}
```
If you want to learn more about dependency injection then you can read my post [A Step by Step Guide to ASP.NET Core Dependency Injection](https://www.ezzylearning.net/tutorial/a-step-by-step-guide-to-asp-net-core-dependency-injection)

## **Scaffolding Web API Controller in Visual Studio**

You can either create Web API controllers manually and implement all those CRUD operations related actions manually or you can take advantage of Visual Studio built-in scaffolding feature and save some time. To create our **BookController**, right click on the “**Controllers**” folder and choose the **Add** \> **Controller**… option. You will see the following dialog in which you need to select “**API**” from the left-hand side pane. Next select the “**API Controller with actions, using Entity Framework**” option and click the “Add” button.

![Add New Scaffold Item Dialog in Visual Studio](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/evhd7sjrfcsljnybo5h3.png)

The following dialog will appear where you can provide the name of your controller along with the Model and Data context classes. Click the “**Add**” button when you have provided all details as shown below:

![Add API Controller with Actions using Entity Framework](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwg5oldl24iole8t9gee.png)

A standard API controller will be generated for you along with all the actions. Notice that the class is marked with the **[ApiController]** attribute mentioned above and a **LibraryContext** object is also injected in the constructor of the controller.
```
[Route("api/[controller]")]
[ApiController]
public class BooksController : ControllerBase
{
    private readonly LibraryContext _context;

    public BooksController(LibraryContext context)
    {
        _context = context;
    }
}
```
To save us some typing, the following actions are also generated automatically for you.

![ASP.NET Core Web API Action methods for CRUD operations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1er8egvmlqqjw0dyqos8.png)

## **Understanding Web API Action Methods**

The first action method we will explore is the **PostBook** action method. This method will be used to add new books to our in-memory database. The **\[HttpPost\]** attribute indicates that this action will be called using the HTTP POST method. The method has a parameter e.g. Book that will be passed to this action from the body of the HTTP request.

The first statement will add the book object in the Books collection available in our data context and the second statement will persist our changes to the database. The last statement is calling the **CreatedAtAction** method that will automatically return **HTTP 201 status code** and will also add a **Location** header to the response. This Location header specifies the URI of the newly created book.
```
// POST: api/Books
[HttpPost]
public async Task<ActionResult<Book>> PostBook(Book book)
{
    _context.Books.Add(book);
    await _context.SaveChangesAsync();

    return CreatedAtAction("GetBook", new { id = book.Id }, book);
}
```
We can use tools like [Postman](https://www.getpostman.com/downloads/) or [Insomnia](https://insomnia.rest/) to test our APIs quickly without implementing a client application. You can download and install Postman from their [website](https://www.getpostman.com/downloads/). Once it's installed, create a new **HTTP POST** request and set the **URI** to our API URL. In the **Body** tab, select the **raw** radio button and choose type as **JSON**. Finally, add the following JSON in the request body and click the **Send** button.
```
{
  "title":"ASP.NET Web APIs Developer Guide",
  "yearPublished":2020
}
```
Postman will call our **PostBook** API mentioned above which will create a new book and finally will send **201 Created** status code along with the newly created book as shown in the screenshot below:

![New Entity Created using Post API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lrwfzdnomckau78269lo.png)

If you will click the **Headers** tab in Postman, you will notice the **Location** header is showing the **URI** to access the newly created book.

![Location Header in New API Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9an2b8zdpy9eflfeuvwd.png)

You can copy-paste this URL in the browser address bar to view the newly created book.

![Get Single Entity using ASP.NET Core Web API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9xahv3g1bx9izftfktho.png)

The above URL is calling the following **GetBook** action that simply searches the book in our in-memory database and return the book.
```
// GET: api/Books/5
[HttpGet("{id}")]
public async Task<ActionResult<Book>> GetBook(int id)
{
    var book = await _context.Books.FindAsync(id);

    if (book == null)
    {
        return NotFound();
    }

    return book;
}
```
At this time, you can also test **GetBooks** action in the browser that will return an array of all the books object available in our in-memory database.
```
// GET: api/Books
[HttpGet]
public async Task<ActionResult<IEnumerable<Book>>> GetBooks()
{
    return await _context.Books.ToListAsync();
}
```
![Get all Entities from ASP.NET Core Web API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8de4dqgxhvk7jp01orya.png)

The next method is the **PutBook** method which can be used to update book entities. It uses the **HTTP PUT** method and the id of the entity we want to update will be provided as a parameter in the **URI**. The second parameter is the book entity that contains the updated data. The method will return **400 Bad Request** if the specified id is not the same as the id property of the book entity we passed as the parameter. The response of the method will be **204 No Content**.
```
// PUT: api/Books/5
[HttpPut("{id}")]
public async Task<IActionResult> PutBook(int id, Book book)
{
    if (id != book.Id)
    {
        return BadRequest();
    }

    _context.Entry(book).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!BookExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}
```
We are using the in-memory database in this tutorial, so each time the application will start the new database will be initialized. We need to make sure that there is a book entity available in the database before we test the above **PutBook** action method. The following screen shows the **PUT** request we made with an updated entity **JSON** in the body of the request.

![Update Entity using ASP.NET Core Web API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fquitp448abnad9q42pz.png)

Finally, we have the **DeleteBook** action method that accepts requests with the **HTTP DELETE** method and simply deletes the entity from the in-memory database.
```
// DELETE: api/Books/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteBook(int id)
{
    var book = await _context.Books.FindAsync(id);
    if (book == null)
    {
        return NotFound();
    }

    _context.Books.Remove(book);
    await _context.SaveChangesAsync();

    return NoContent();
}
```
The following screenshot shows how to test the **DeleteBook** action method in Postman.

![Delete Entity using ASP.NET Web API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qt2x69js2116rngwcnoo.png)

## Summary

ASP.NET Core is a great framework to use when you are planning to build clean, scalable, and maintainable Web APIs. It has lots of useful APIs and features related to Web APIs and we can write a whole book on just Web APIs. This tutorial is a basic guide for developers who want to get started with ASP.NET Core Web APIs. I hope you enjoyed this tutorial and I will appreciate it if you can leave your feedback below to let me know how can I improve my tutorials further.