Minimal API helps to build HTTP APIs with less dependencies. These APIs is perfect for those microservices and application which includes minimal features, files, and other such dependencies in this new ASP.Net Core Web API.
In this ASP.Net Core tutorial, we bring you a step-by-step guide to building a minimal API for your application. There are different approaches to building ASP.Net API and one of which is creating API using controllers. If you are confused between controller-based API and minimal API, navigate to APIs Overview.
Overview
In this ASP.Net Core tutorial, you will get the below-mentioned API. The core produces responseType API which helps you analyze the type of data includes in the response.
API | Description | Request body | Response body |
GET /todoitems/complete | Get a completed to-do items | None | Array of to-do items |
POST /todoitems | Add a new item | To-do item | To-do item |
GET /todoitems | Receive all to-do items | None | Array of to-do items |
GET /todoitems/{id} | Receive an item by ID | None | To-do item |
DELETE /todoitems/{id} | Delete an item | None | None |
PUT /todoitems/{id} | Updating an existing item | To-do item | None |
Prerequisite
Install Visual Studio 2022 for web application and ASP.Net workload.
Source: learn.microsoft.com
Step 1: Build an API Project
Navigate to Visual Studio 2022 and build a new project.
Go to the ‘Create a new project’ dialog
Source: learn.microsoft.com
Give your project name TodoApi and then select the ‘Next’ button. This will be Dot Net new web API.
Now navigate to the Additional information dialog box.
Step 2: Examine the code
Below is the code of the Program.cs file includes. Right the C# code for minimal API.
C# |
var builder = WebApplication.CreateBuilder(args); var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
|
Source: learn.microsoft.com
Below is the preceding code. Build Web API with C# code in ASP.Net.
Step 3: Run the application
Source: learn.microsoft.com
Source: learn.microsoft.com
Now choose the ‘Yes’ option if you think the development certificate is trustable.
In the browser, you could see the message Hello Wolrd!. The Program.cs file includes minimal dependencies but a complete application.
Step 4: Add NuGet packages
These NuGet packages support the database and diagnostics which is why it is necessary to add these packages.
Go to the ‘Tools’ menu, choose ‘NuGet Package Manager’ and then ‘Manage NuGet Packages for Solution.’
Choose the ‘Browse’ tab.
Type ‘Microsoft.EntityFrameworkCore.InMemory’ in the search box and then choose ‘Microsoft.EntityFrameworkCore.InMemory.’
Choose the ‘Project’ checkbox at the right-hand side of the pane and select ‘Install.’
Now, follow the preceding information in order to add the ‘Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore’ package.
Step 5: The database and model context classes
C# |
class Todo { public int Id { get; set; } public string? Name { get; set; } public bool IsComplete { get; set; } } |
The aforementioned code builds the model for your app.
Create a TodoDB.cs file name using the below-mentioned code.
C# |
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext { public TodoDb(DbContextOptions<TodoDb> options) : base(options) { }
public DbSet<Todo> Todos => Set<Todo>(); } |
The above code describes the database context, a main class that coordinates functionality like Entity Framework for a data model.
Adding the API Code
Use the following code and replace the content in the Program.cs file.
C# |
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList")); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); var app = builder.Build();
app.MapGet("/todoitems", async (TodoDb db) => await db.Todos.ToListAsync());
app.MapGet("/todoitems/complete", async (TodoDb db) => await db.Todos.Where(t => t.IsComplete).ToListAsync());
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) => await db.Todos.FindAsync(id) is Todo todo ? Results.Ok(todo) : Results.NotFound());
app.MapPost("/todoitems", async (Todo todo, TodoDb db) => { db.Todos.Add(todo); await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo); });
app.MapPut("/todoitems/{id}", async (int id, Todo inputTodo, TodoDb db) => { var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name; todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent(); });
app.MapDelete("/todoitems/{id}", async (int id, TodoDb db) => { if (await db.Todos.FindAsync(id) is Todo todo) { db.Todos.Remove(todo); await db.SaveChangesAsync(); return Results.Ok(todo); }
return Results.NotFound(); });
app.Run(); |
The below-mentioned code adds the database context to the dependency injection (DI) container and allows displaying database-related exceptions.
C# |
var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList")); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); var app = builder.Build(); |
Test your app by installing Postman
Install Postman
Start your web application
Start Postman
And disable SSL certificate verification
In Windows, choose File> Setting (General tab)> disable SSL certificate verification.
Test posting data
The below-mentioned code in Program.cs is an HTTP POST endpoint /todoitems which adds up data to the database in-memory.
C# |
app.MapPost("/todoitems", async (Todo todo, TodoDb db) => { db.Todos.Add(todo); await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo); }); |
Now, run the application. The browser will display an error 404 because it does not have a ‘/’ endpoint.
Use the endpoint of POST in order to add the data to your application.
Create an HTTP request
HTTP method needs to be set to Post
Set the URL to ‘https://localhost:<port>/todoitems’. For instance: https://localhost:5001/todoitems
Choose the Body tab
Select raw
Choose the type to ‘JSON.’
Type JSON in the to-do item in the request body.
JSON |
{ "name":"walk dog", "isComplete":true } |
Choose the ‘Send’ option at the top right.
Analyze the GET endpoints
The following API you will get by calling ‘MapGet’.
API | Description | Request body | Request response |
GET /todoitems/complete | Get all completed to-do items | None | Array of to-do items |
GET /todoitems/{id} | Get an item by ID | None | To-do item |
GET /todoitems | Get all to-do items | None | Array of to-do items |
Use the MapGroup API
Many times API groups of endpoints have a common URL prefix. MapGroup allows you to organize these groups orderly. It customizes the whole groups of endpoints with just one call to methods like WithMetadata and RequireAuthorization.
Replace the Program.cs content with the following code.
C# |
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList")); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); var app = builder.Build();
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", async (TodoDb db) => await db.Todos.ToListAsync());
todoItems.MapGet("/complete", async (TodoDb db) => await db.Todos.Where(t => t.IsComplete).ToListAsync());
todoItems.MapGet("/{id}", async (int id, TodoDb db) => await db.Todos.FindAsync(id) is Todo todo ? Results.Ok(todo) : Results.NotFound());
todoItems.MapPost("/", async (Todo todo, TodoDb db) => { db.Todos.Add(todo); await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo); });
todoItems.MapPut("/{id}", async (int id, Todo inputTodo, TodoDb db) => { var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name; todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent(); });
todoItems.MapDelete("/{id}", async (int id, TodoDb db) => { if (await db.Todos.FindAsync(id) is Todo todo) { db.Todos.Remove(todo); await db.SaveChangesAsync(); return Results.Ok(todo); }
return Results.NotFound(); });
app.Run(); |
Test the TypedResults API
Call route handler methods with Map<HttpVerb> rather than using lambdas.
C# |
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList")); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); var app = builder.Build();
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", GetAllTodos); todoItems.MapGet("/complete", GetCompleteTodos); todoItems.MapGet("/{id}", GetTodo); todoItems.MapPost("/", CreateTodo); todoItems.MapPut("/{id}", UpdateTodo); todoItems.MapDelete("/{id}", DeleteTodo);
app.Run();
static async Task<IResult> GetAllTodos(TodoDb db) { return TypedResults.Ok(await db.Todos.ToArrayAsync()); }
static async Task<IResult> GetCompleteTodos(TodoDb db) { return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).ToListAsync()); }
static async Task<IResult> GetTodo(int id, TodoDb db) { return await db.Todos.FindAsync(id) is Todo todo ? TypedResults.Ok(todo) : TypedResults.NotFound(); }
static async Task<IResult> CreateTodo(Todo todo, TodoDb db) { db.Todos.Add(todo); await db.SaveChangesAsync();
return TypedResults.Created($"/todoitems/{todo.Id}", todo); }
static async Task<IResult> UpdateTodo(int id, Todo inputTodo, TodoDb db) { var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
todo.Name = inputTodo.Name; todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return TypedResults.NoContent(); }
static async Task<IResult> DeleteTodo(int id, TodoDb db) { if (await db.Todos.FindAsync(id) is Todo todo) { db.Todos.Remove(todo); await db.SaveChangesAsync(); return TypedResults.Ok(todo); }
return TypedResults.NotFound(); } |
Now, the Map<HttpVerb> code calls methods rather than lambdas.
C# |
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", GetAllTodos); todoItems.MapGet("/complete", GetCompleteTodos); todoItems.MapGet("/{id}", GetTodo); todoItems.MapPost("/", CreateTodo); todoItems.MapPut("/{id}", UpdateTodo); todoItems.MapDelete("/{id}", DeleteTodo); |
The aforementioned methods implement IResult and are described by TypedResults.
C# |
static async Task<IResult> GetAllTodos(TodoDb db) { return TypedResults.Ok(await db.Todos.ToArrayAsync()); }
static async Task<IResult> GetCompleteTodos(TodoDb db) { return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).ToListAsync()); }
static async Task<IResult> GetTodo(int id, TodoDb db) { return await db.Todos.FindAsync(id) is Todo todo ? TypedResults.Ok(todo) : TypedResults.NotFound(); }
static async Task<IResult> CreateTodo(Todo todo, TodoDb db) { db.Todos.Add(todo); await db.SaveChangesAsync();
return TypedResults.Created($"/todoitems/{todo.Id}", todo); }
static async Task<IResult> UpdateTodo(int id, Todo inputTodo, TodoDb db) { var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
todo.Name = inputTodo.Name; todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return TypedResults.NoContent(); }
static async Task<IResult> DeleteTodo(int id, TodoDb db) { if (await db.Todos.FindAsync(id) is Todo todo) { db.Todos.Remove(todo); await db.SaveChangesAsync(); return TypedResults.Ok(todo); }
return TypedResults.NotFound(); } |
Handler method return of Type OK<Todo[]> can be verified by the test code.
C# |
public async Task GetAllTodos_ReturnsOkOfTodosResult() { // Arrange var db = CreateDbContext();
// Act var result = await TodosApi.GetAllTodos(db);
// Assert: Check for the correct returned type Assert.IsType<Ok<Todo[]>>(result); } |
Step:6 JSON serialization option configuration
If you need more information, navigate to Configure JSON serialization option.
Follow the above steps to create ASP.Net Core API without hustling much.