---
title: "Building ASP.NET CRUD Web APIs"
description: "Learn how to build a CRUD Web API with ASP.NET through this practical tutorial."
authors:
  - name: "Andrea Chiarelli"
    url: "https://auth0.com/blog/authors/andrea-chiarelli/"
date: "Oct 15, 2021"
category: "Developers,Tutorial,.NET"
tags: ["dotnet", "asp.net", "webapi", ".net"]
url: "https://auth0.com/blog/building-aspnet-web-api/"
---

# Building ASP.NET CRUD Web APIs

This tutorial will guide you step by step in creating a Web API with ASP.NET. You will end up building a simple Web API project to manage a glossary of terms using the typical four CRUD (*Create*, *Retrieve*, *Update*, *Delete*) operations.

## Prerequisites

Before starting to build your Web API, you need to ensure you have installed the right tools on your machine. In particular, since you are going to use ASP.NET, you need to check if you have installed the latest .NET SDK by typing the following command in a terminal window:

```shell
dotnet --version
```

The sample project that accompanies this tutorial has been built with .NET 5.0. If the previous command provides you with a result lower than `5.0.100`, you should [download the latest .NET SDK](https://dotnet.microsoft.com/download/dotnet/current) and install it on your machine.

In this tutorial, you are going to use the [.NET CLI](https://docs.microsoft.com/en-us/dotnet/core/tools/) coming with the .NET SDK. If you are going to use Visual Studio, make sure to use the latest version of [Visual Studio](https://visualstudio.microsoft.com/downloads/).

> **Note**: If you update Visual Studio to the latest version, you will get the latest .NET SDK bundled.

## Creating an ASP.NET Web API Project

Once the SDK is installed, you are ready to build your Web API with ASP.NET. The Web API you are going to build will provide a few endpoints that allow you to manage a glossary of terms. So, you will be able to perform the typical [CRUD (*Create*, *Retrieve*, *Update*, *Delete*)](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) operations on a list of term definitions.

In order to create this project, type the following commands in a terminal window:

```shell
dotnet new webapi -o Glossary
```

This command creates a folder named `Glossary` and generates inside it the content for the ASP.NET project from the `webapi` template.

If you are using Visual Studio, you can create your project by selecting the *API* template as shown in the following picture:

![.NET Core App API template within Visual Studio](https://images.ctfassets.net/23aumh6u8s0i/7E9BdF65zxIUMY8Y5rjBkw/6b75636fb10ebc6e45e180954f959eb9/visual-studio-api-template.png)

Then follow the steps shown by the wizard to generate the project.

> **Note**: In the rest of the article, the .NET CLI will be used to manage the project, but you can run the same steps by using Visual Studio as well.

## Running the Sample Project

Once the project has been created, you should make sure everything is okay. So, in the terminal window, move to the `Glossary` folder and launch the sample application by typing the following commands:

```shell
cd Glossary
dotnet run
```

> If this is the very first time that you're running an ASP.NET application, you should trust the HTTPS development certificate included in the .NET Core SDK. This task depends on your operating system. Please, take a look at the [official documentation](https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-3.0&tabs=visual-studio#trust) to apply the proper procedure.

If everything is okay, point your browser to the address [https://localhost:5001/weatherforecast](https://localhost:5001/weatherforecast). You should see a page similar to the following:

![Sample Web API application output](https://images.ctfassets.net/23aumh6u8s0i/1itzHXXQbSPKqmqhm1pDMC/7637c10b07af3c118d246db82bcdc818/default-webapi-response.png)

This is the output of the sample Web API application that comes with the `webapi` project template.

You should also be able to access interactive documentation about the Web API by navigating to the address [https://localhost:5001/swagger]( https://localhost:5001/swagger). In this case, you would see the following page:

![Sample Web API application Swagger UI](https://images.ctfassets.net/23aumh6u8s0i/I7cNOSb4YKmAOaaiqEQD0/1cc90347b37f7f9ab3182fc429b2494e/sample-webapi-swagger-ui.png)

This page is automatically generated when the application runs in the development environment. The documentation generation is based on [Swagger](https://swagger.io/) and allows you to get information about the endpoints exposed by the Web API. You can also test each endpoint directly from that page, so you don't need a real client or a tool such as [curl](https://curl.se/), [Postman](https://www.getpostman.com/), or [Insomnia](https://insomnia.rest/).

You will see how to use this page in more detail later on.

To stop the Web API application, simply hit CTRL+C in the terminal window that it's running in.

## Anatomy of the Project

Regardless of whether you created your project with the .NET Core CLI or Visual Studio, you will get the same collection of files and folders, like the ones shown in the following picture:

![.NET Core API files and folder structure of the default project](https://images.ctfassets.net/23aumh6u8s0i/17oJ1he1dLvAoEPJzXOuUm/986d18e37170b5ce57eeb932391038d2/template-project-files)

Taking a quick look at a few of these files gives you an idea of how a generic ASP.NET application works. In particular, the `Program.cs` file contains the starting point of the application. Its content looks like the following:

```c#
//Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Glossary
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}
```

The static `Main()` method is the method that is automatically called when the application starts. It simply calls the `CreateHostBuilder()` method to create and configure a *host* for the application and eventually to run it. A *host* is an object that collects and provides services for the application. For example, in the ASP.NET context, it provides an HTTP server implementation, middleware components, configuration services, and so on.

As you can see, the `webBuilder.UseStartup()` method refers to the `Startup` type. This type is defined by the `Startup` class in the ` Startup.cs` file. The default content of this file is shown below:

```c#
//Startup.cs
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.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;

namespace Glossary
{
    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.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Glossary", Version = "v1" });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Glossary v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}
```

The `Startup` class allows you to set up services for the current application. In particular, it allows you to register services via the `ConfigureServices()` method and to configure the HTTP request pipeline via the `Configure()` method. In the body of the latter method, you'll find a few statements that declare the middleware to use. Please read [the official documentation to get more information about the built-in middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware). Notice how Swagger is registered in the `ConfigureServices()` method and configured in the `Configure()` method.

The rest of the application code is located in the `WeatherForecast.cs` and `WeatherForecastController.cs` files. These files implement respectively the model and the Web APIs controller for the sample application coming with the .NET template. You are going to get rid of them and create your glossary application.

## Setting up a CRUD API

Now that you have ensured that the whole development environment is working properly and have taken a tour to understand the project's main elements, start creating your Web API.

As said before, your API will implement the CRUD operations on a glossary. Like most Web APIs, your API will also use an approach inspired by [the REST architecture](https://auth0.com/blog/rest-architecture-part-1-building-api/).

To get started, remove the `WeatherForecast.cs` file from the root of the project and the `WeatherForecastController.cs` file from the `Controllers` folder.

### Creating the model

As a first step, create the model for the glossary Web API. It will be the representation of the resource that will be exposed to the clients. In particular, it will be implemented as a class representing the single glossary item. So, add a file named `GlossaryItem.cs` in the root folder of the project and put the following content in it:

```c#
//GlossaryItem.cs
namespace Glossary
{
    public class GlossaryItem
    {
        public string Term { get; set; }
        public string Definition { get; set; }
    }
}
```

This class simply defines a glossary item as a term and its definition.

### Creating the controller

Now, you need to create the API controller to handle HTTP requests. Create the `GlossaryController.cs` in the `Controllers` folder and put the following code inside it:

```c#
//Controllers/GlossaryController.cs
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;

namespace Glossary.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class GlossaryController: ControllerBase
    {
        private static List<GlossaryItem> Glossary = new List<GlossaryItem> {
            new GlossaryItem
            {
                Term= "Access Token",
                Definition = "A credential that can be used by an application to access an API. It informs the API that the bearer of the token has been authorized to access the API and perform specific actions specified by the scope that has been granted."
            },
            new GlossaryItem
            {
                Term= "JWT",
                Definition = "An open, industry standard RFC 7519 method for representing claims securely between two parties. "
            },
            new GlossaryItem
            {
                Term= "OpenID",
                Definition = "An open standard for authentication that allows applications to verify users are who they say they are without needing to collect, store, and therefore become liable for a user’s login information."
            }
        };
    }
}
```

A controller in ASP.NET Core is a class inheriting from `ControllerBase`. The base class provides properties and methods that are useful for handling HTTP requests, as you will see later on.

At this stage, the `GlossaryController` class simply defines the static variable `Glossary` as a small list of glossary items. In a real-world scenario, the glossary items should be persisted into a database, but to keep things simple, your application will rely on this volatile solution.

The class is decorated with two attributes: `ApiController` and `Route`.

The `ApiController` attribute enables a few handy behaviors, like the automatic HTTP 400 responses when the model is in error, the automatic binding of URL parameters to method parameters, and similar.

The `Route` attribute allows you to map a URL pattern to the controller. In this specific case, you are mapping the `api/[controller]` URL pattern to the controller. At runtime, the `[controller]` placeholder is replaced by the controller class name without the `Controller` suffix. That means that the `GlossaryController` will be mapped to the `api/glossary` URL path, and this will be the base path for all the actions implemented by the controller. The `Route` attribute can be also applied to the methods of the controller, as you will see.

## Getting a List of Items

Once you have the basic infrastructure for your controller, you need to add an *action* to it. An action is a public method of a controller mapped to an [HTTP verb](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods). So, add a new method to the `GlossaryController` class as shown in the following: :point_down:

```c#
//Controllers/GlossaryController.cs
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;

namespace Glossary.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class GlossaryController: ControllerBase
    {
        private static List<GlossaryItem> Glossary = new List<GlossaryItem> {
        //...leave the glossary untouched...
        };
        
        //👇 new code
        [HttpGet]
        public ActionResult<List<GlossaryItem>> Get()
        {
            return Ok(Glossary);
        }
        //👆 new code
    }
}
```

The method `Get()` allows the client to get the whole list of glossary items. It is decorated with the `HttpGet` attribute which maps the method to HTTP GET requests sent to the `api/glossary` URL path.

The return type of the method is `ActionResult<List<GlossaryItem>>`. This means that the method will return a `List<GlossaryItem>` type object or an object deriving from `ActionResult`. The `ActionResult` type represents the HTTP status codes to be returned as a response. As said before, the `ControllerBase` class provides a few features to deal with HTTP requests. Some of these features are methods that create HTTP status code in a readable way, like `Ok()`, `NotFound()`, `BadRequest()`, and so on. The `Ok(Glossary)` value returned by the `Get()` method represents the `200 OK` HTTP status code with the representation of the `Glossary` variable as the body of the response.

<include src="TweetQuote" quoteText="In ASP.NET, an action is a public method of a controller mapped to an HTTP verb."/>

> **Note**: An ASP.NET action may return different types: a specific type, `IActionResult`, or `ActionResult<T>`. Each return type has pros and cons. See the [documentation](https://docs.microsoft.com/en-us/aspnet/core/web-api/action-return-types) for more information.

To test this first action implementation, run the application by typing the following command in a terminal window:

```shell
dotnet run
```

> **Note**: If you are already running your web app, stop it and run it again. If you don't want manually to stop and start your application while doing changes to the source files, you can run the following command:
>
> ```shell
> dotnet watch run
> ```
>
> This causes your application to automatically restart whenever you change the source code.

Then, navigate to [https://localhost:5001/swagger](https://localhost:5001/swagger) with your browser. Now you should see your new endpoint as shown in the following picture:

![The glossary list endpoint in Swagger UI](https://images.ctfassets.net/23aumh6u8s0i/3NJcfNil25dyicHXnWgWjK/f33eba47181522fcc555c4a8780aad32/swagger-glossary-webapi.png)

By clicking the *Get* button and then the *Try it out* button that appears, you can test the new endpoint. You should see a screen like the following:

![Testing glossary list endpoint with Swagger UI](https://images.ctfassets.net/23aumh6u8s0i/2WYyaHXkLTJId004iv5zOW/d6258c4e31826e432fdeebe2810124e0/test-glossary-list-with-swagger-ui.png)

Here you can find some details about the endpoint. You know that it doesn't require parameters. Also, you see that the only response it provides is a successful one (status code 200) with an array of JSON objects with the `term` and `definition` string properties. 

All you need to do at this point is to click on the *Execute* button and get the result, as shown below:

![Web API response in Swagger UI](https://images.ctfassets.net/23aumh6u8s0i/50b5woEyhkPvTAUk1upOrJ/672f74465008a544789891ef2ac35166/webapi-response-swagger-ui.png)

You get the command you should use to make the request with curl, the absolute URL of the Web API endpoint, and the actual Web API response. You see the glossary list implemented by the `Glossary` static variable and the headers sent by the server.

## Getting a Single Item

The next action you are going to create will allow your client to get a single glossary item. The action will expect a term as input and will provide an instance of `GlossaryItem` as a result.

Add a new method definition to the controller class after the previous action definition, as in the following:

```c#
//Controllers/GlossaryController.cs
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;

namespace Glossary.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class GlossaryController: ControllerBase
    {
        //...existing code...

		//👇 new code
        [HttpGet]
        [Route("{term}")]
        public ActionResult<GlossaryItem> Get(string term)
        {
            var glossaryItem = Glossary.Find(item => 
                    item.Term.Equals(term, StringComparison.InvariantCultureIgnoreCase));

            if (glossaryItem == null)
            {
                return NotFound();
            } else
            {
                return Ok(glossaryItem);
            }
        }
        //👆 new code
    }
}
```

As you can see, you have an overloaded definition of the `Get()` method mapped again to the HTTP GET verb. However, in this case, you have a `Route` attribute for the action. This attribute appends a new variable element to the URL of the action. So, this action will respond to the HTTP request sent to the URL pattern `api/glossary/{term}`. The `{term}` part of the pattern can be replaced by any non-empty string and its value will be mapped to the `term` parameter.

This method checks if the requested term exists in the `Glossary` list and returns the glossary item. If the term doesn't exist, it returns a `404 Not Found` HTTP status code.

Now, restart the application and test this new action by navigating to [https://localhost:5001/swagger](https://localhost:5001/swagger).

This time, you have two endpoints on the page, as you can see in the following picture:

![Glossary term endpoint in Swagger UI](https://images.ctfassets.net/23aumh6u8s0i/zje7VCTUuutvlXZYg9SqC/f7cb1a43b0006fbd5e0e18227e90be76/glossary-term-endpoint-in_swagger-ui.png)

In this case, you have a mandatory parameter to provide before testing the endpoint. Try to insert the JWT value and you should get the definition of the JWT term as a response.

<include src="ebook-ads/JwtHandbook" />

## Creating a New Item

Going ahead with the CRUD operations implementation, you need to allow the client to add a new term to the glossary. As a first step, include the `System.IO` namespace in the controller class by adding the `using System.IO` statement. The *using* section of the file `GlossaryController.cs` should look like the following code:

```c#
//Controllers/GlossaryController.cs
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using System.IO;

namespace Glossary.Controllers
{
  //...existing code...
}
```

You need the `System.IO` namespace to use the `Path` object, as you will see very soon.

Then, append the `Post()` method to the controller class:

```c#
//Controllers/GlossaryController.cs
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using System.IO;

namespace Glossary.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class GlossaryController: ControllerBase
    {
        //...existing code...

        //👇 new code
        [HttpPost]
        public ActionResult Post(GlossaryItem glossaryItem)
        {
            var existingGlossaryItem = Glossary.Find(item =>
                    item.Term.Equals(glossaryItem.Term, StringComparison.InvariantCultureIgnoreCase));

            if (existingGlossaryItem != null)
            {
                return Conflict("Cannot create the term because it already exists.");
            }
            else
            {
                Glossary.Add(glossaryItem);
                var resourceUrl = Path.Combine(Request.Path.ToString(), Uri.EscapeUriString(glossaryItem.Term));
                return Created(resourceUrl, glossaryItem);
            }
        }
        //👆 new code
    }
}
```

In this case, the action is mapped to the HTTP POST verb via the `HttpPost` attribute. The `Post()` method has also a `glossaryItem` parameter whose value comes from the body of the HTTP POST request. Here, the method checks if the term to be created already exists. If it is so, a `409 Conflict` HTTP status code will be returned. Otherwise, the new item is appended to the `Glossary` list. By following the [REST guidelines](https://restfulapi.net/http-status-codes/), the action returns a `201 Created` HTTP status code. The response includes the newly created item in the body and its URL in the `Location` HTTP header.

Run the application again and add a new glossary item as shown in the following picture:

![Posting a new glossary item through Swagger UI](https://images.ctfassets.net/23aumh6u8s0i/2Wj5qH5QGMcZC82KC47HVM/758a0589e4254ff6a013deb48808cba7/post-webapi-swagger-ui.png)

As a result of executing this command, you should get a response as in the following:

![Response to posting a new glossary item through Swagger UI](https://images.ctfassets.net/23aumh6u8s0i/7oqS8NxrfrCx45HUtOR38w/5d09ab1a18e27c4416e02d8128d86e6f/post-response-swagger-ui.png)

Notice the `201 Created` HTTP status code, the value `/api/glossary/MFA` for the `location` header, and the content of the response body. This is what you said to return at the end of the action execution.

To verify that the new item has been actually added to the glossary, execute the GET verb on the `api/glossary/{term}` endpoint by providing the MFA term as a parameter.

You should get the newly added item.

> **Note**: Be sure to not stop the application between the item addition and the item existence check. Remember that the glossary is implemented as a static variable, so, when the application stops, you lose any changes.

## Updating an Item

After adding an item, allow the client of your Web API to update it by appending the `Put()` method to the controller class, as shown below:

```c#
//Controllers/GlossaryController.cs
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using System.IO;

namespace Glossary.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class GlossaryController: ControllerBase
    {
        //...existing code...

        //👇 new code
        [HttpPut]
    		public ActionResult Put(GlossaryItem glossaryItem)
    		{
        		var existingGlossaryItem = Glossary.Find(item =>
                item.Term.Equals(glossaryItem.Term, StringComparison.InvariantCultureIgnoreCase));

		        if (existingGlossaryItem == null)
    		    {
        		    return NotFound("Cannot update a nont existing term.");
		        } else
    		    {
        		    existingGlossaryItem.Definition = glossaryItem.Definition;
            		return Ok();
        		}
    		}
        //👆 new code
    }
}
```

The `Put()` method is decorated with the `HttpPut` attribute that maps it to the HTTP PUT verb. In short, it checks if the glossary item to be updated exists in the `Glossary` list. If the item doesn't exist, it returns a `404 Not Found` HTTP status code. Otherwise, it updates the item's definition and returns a `200 OK` HTTP status code.

Test this new action by using the new PUT button appearing on the Swagger page.

## Deleting an Item

Finally, you implement the deletion of an item from the glossary by appending the `Delete()` method to the controller class:

```c#
//Controllers/GlossaryController.cs
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using System.IO;

namespace Glossary.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class GlossaryController: ControllerBase
    {
        //...existing code...

        //👇 new code
        [HttpDelete]
        [Route("{term}")]
        public ActionResult Delete(string term)
        {
            var glossaryItem = Glossary.Find(item =>
                   item.Term.Equals(term, StringComparison.InvariantCultureIgnoreCase));

            if (glossaryItem == null)
            {
                return NotFound();
            }
            else
            {
                Glossary.Remove(glossaryItem);
                return NoContent();
            }
        }
        //👆 new code
    }
}
```

The `HttpDelete` attribute maps the method `Delete()` to the DELETE HTTP verb. The `Route` attribute appends a new element to the URL the same way you learned when implemented the action that gets a single glossary item. So, when a DELETE HTTP request hits the `api/glossary/{term}` URL pattern, the method checks if the item exists in the `Glossary` list. If it doesn't exist, a `404 Not Found` HTTP status code is returned. Otherwise, the `Delete()` method removes the item from the `Glossary` list and returns a `204 No Content` HTTP status code.

After you have added this code, run the application and test the new method by clicking the DELETE button.

Your CRUD Web API is ready!

<include src="ebook-ads/dotnetIdentity" />

## Recap

In this article, you learned to create a new Web API project with ASP.NET. You started by exploring the sample project generated by the CLI command `dotnet new` and continued by replacing the existing code with yours. You implemented the CRUD operations on a simple glossary and learned how to map methods to HTTP verbs by leveraging the proper attributes. Also, you discovered how ASP.NET lets you return HTTP responses in a readable way.

You can download the full source code of the application from [this GitHub repository](https://github.com/auth0-blog/glossary-web-api-aspnet/tree/starter).

To learn how to secure your Web API, continue reading [this article about authorization and ASP.NET](https://auth0.com/blog/aspnet-webapi-authorization).