Create minimal APIs using ASP.NET Core 6 and Visual Studio 2022
One of the features of ASP.NET Core 6 that has got much attention is
minimal APIs. You can
now create API endpoints without creating a controller and routing related
configuration. I have already discussed creation of
minimal APIs in
a few articles published earlier. The earlier articles were published during the
Preview days of ASP.NET Core 6 and use Visual Studio 2019. Now that the final
versions of ASP.NET Core 6 and Visual Studio 2022 are available, it's time
to revisit the subject and look into various aspects of minimal APIs in more
details. To that end this multipart article series is going to discuss just
that.
To begin with, we will create Employees CRUD endpoints using minimal APIs and
Visual Studio 2022 API project template.
Open Visual Studio 2022 and click on the Create a new project option on the
startup window.
Then pick the Web API project template as shown below:
This template allows you to create controller-based APIs or minimal APIs.
Once you click on the Next dialog you will be shown this project configuration
dialog:
Enter the project name as MinimalAPI, pick some project location and click on
the Next button.
Now you can specify some additional information as shown below:
Look at the checkbox that says Use controller. By default this checkbox is
selected indicating that the project will use controller-based APIs. If you
uncheck this checkbox (as I have done in this case) the project uses minimal
APIs. Uncheck this checkbox since we want to play with minimal APIs in this
article. Keep other settings to their default values and hit Create.
The following figure shows the default project loaded in the Solution
Explorer.
As you can see, there is no API controller since we unchecked that option
during project creation. The Program.cs will contains startup and minimal API
code.
Next, open Program.cs and delete all the code from it. We will write
everything needed for our CRUD APIs in step-by-step manner. We will also store
global usings in
GlobalUsings.cs file. So, add a new C# class file named GlobalUsings.cs.
Now add the ConnectionStrings section to the appsettings.json file.
"ConnectionStrings": {
"AppDb": "data source=.;initial catalog=Northwind;
integrated security=true"
}
Here, we stored a connection string for the Northwind database with key name
AppDb.
Then add the following code in the empty Program.cs file.
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.
GetConnectionString("AppDb");
This code creates a WebApplicationBuilder using the CreateBuilder() method.
It also reads the database connection string from the appsettings.json file and
stores it in a local variable. This is done using the Configuration property of
the WebApplicationBuilder and GetConnectionString() method.
Our Employees minimal API requires Employee entity class and a custom
DbContext class named AppDbContext. We will add these classes in Program.cs file
itself (you could have placed them in separate files) as shown below:
public class Employee
{
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext>
options) : base(options)
{
}
public DbSet<Employee> Employees { get; set; }
}
The Employee class contains three properties namely EmployeeID, FirstName,
and LastName.
The AppDbContext class inherits from DbContext class and contains Employees
DbSet.
Now register the AppDbContext with DI container by adding this line below the
GetConnectionString() call.
builder.Services.AddDbContext<AppDbContext>
(o => o.UseSqlServer(connectionString));
The AddDbContext() registers a custom DbContext with the DI container and
also specifies the connection string using the UseSqlServer() extension method.
You may need to add this namespaces in the GlobalUsings.cs file:
global using Microsoft.EntityFrameworkCore;
Now we can build the WebApplication object as shown below:
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
The Build() method creates a WebApplication instance. We also wire developer
exception page middleware and HTTPS redirection middleware using the
UseDeveloperExceptionPage() and UseHttpsRedirection() methods.
Now let's define the CRUD endpoints using various "Map" methods as shown
below.
app.MapGet("/minimalapi/employees",
(AppDbContext db) =>
{
return Results.Ok(db.Employees.ToList());
});
app.MapGet("/minimalapi/employees/{id}",
(AppDbContext db, int id) =>
{
return Results.Ok(db.Employees.Find(id));
});
app.MapPost("/minimalapi/employees",
(AppDbContext db, Employee emp) =>
{
db.Employees.Add(emp);
db.SaveChanges();
return Results.Created($"/minimalapi/employees/
{emp.EmployeeID}", emp);
});
app.MapPut("/minimalapi/employees/{id}",
(AppDbContext db, int id, Employee emp) =>
{
db.Employees.Update(emp);
db.SaveChanges();
return Results.NoContent();
});
app.MapDelete("/minimalapi/employees/{id}",
(AppDbContext db, int id) =>
{
var emp = db.Employees.Find(id);
db.Remove(emp);
db.SaveChanges();
return Results.NoContent();
});
The first MapGet() call handles GET requests made at /minimalapi/employees.
The handler function receives the AppDbContext parameter through DI
automatically. Inside, we simply return a List of all the Employee entities to
the caller after wrapping the data in Results.Ok() method. The Ok() method sets
the HTTP status code to 200.
The second MapGet() call handles GET requests that also carry an EmployeeID
in the route's id parameter. The handler method takes two parameters -
AppDbContext and id. Inside, we find an Employee matching the id parameter, wrap
it in Ok() and return it to the caller.
The MapPost() call handles POST requests made at /minimalapi/employees. The
handler function takes two parameters viz. AppDbContext and Employee object.
Note that AppDbContext is supplied by DI and Employee is constructed from
request's body. Inside, we add the Employee to the Employees DbSet and persist
it to the database by calling SaveChanges(). The Created() method sets the HTTP
status code to 201 and also indicates the URL at which the newly added resource
(Employee) will be avaulable.
The MapPut() call handles PUT requests made at /minimalapi/employees/{id}.
The hander function receives AppDbContext and it as before. Inside, we update an
existing Employee using the Update() method and SaveChanges() method. We return
HTTP status code 204 using NoContent() method.
Finally, the MapDelete() call handles DELETE requests made at /minimalapi/employees/{id}.
The handler function finds an Employee matching the EmployeeID, removes it from
Employees DbSet using Remove() method, and then deletes the Employee from the
database using SaveChanges() method. This time also we return HTTP status code
204 using NoContent() method.
Now that all minimal API endpoints have been defined, let's run the
application by calling:
app.Run();
To test the endpoints, run the application from Visual Studio by pressing F5.
Once the browser opens, go to the address bar and navigate to /minimalapi/employees.
As you can see, a list of all the employees is displayed as expected.
Now enter this URL - /minimalapi/employees/1.
This time only one Employee is returned to the client.
We can test only the GET verbs from the browser's address bar. It would be
better if we can test all the APIs before using them in a client app. In the
next part of this multipart article series, we will integrate
Swagger with our minimal APIs.
Swagger allows you to easily invoke the API endpoints from the browser without
creating a client application.
That's it for now! Keep coding!!