Sneller een API bouwen met .NET Minimal APIs: zo doe je dat

Minimal APIs van Microsoft laten je sneller en met veel minder code web-API’s bouwen. Minimal APIs hebben nog wel wat minder mogelijkheden, maar ze zijn nu al een goed alternatief voor de gebruikelijke opzet in ASP.net MVC. In dit artikel laat ik je zien hoe je een Minimal API maakt en gebruikt.

Met .NET Minimal APIs bouw je API’s met zo min mogelijk afhankelijkheden en zo min mogelijk bestanden. Ze zijn daardoor ideaal voor kleinere API’s, bijvoorbeeld binnen een microservice-architectuur. Met een minimum aan afhankelijkheden en een goed uitgedacht framework kun je zo functionaliteit bouwen met heel weinig regels code. 

.NET 6 is de eerste .NET-versie die Minimal APIs mogelijk maakt. Maar het idee is natuurlijk niet nieuw. Zo heeft NodeJS al langer de mogelijkheid om met een zeer klein aantal regels een web-API te maken. 

Een deel van de features voor Minimal APIs zaten al in .NET 5 en C# 9. Zoals top-level programs, waarmee je met 2 regels een Hello World-programma kon maken. Met global usings en implicit usings in C# 10 ging dit zelfs naar 1 regel.  

Auteur:

Jeroen de Knegt

Lead Cloud Software Engineering

Hello World

Console.WriteLine(“Hello, World!”);

Gerelateerd webinar</>

Op dinsdag 4 oktober organiseert Wiconic het Minimal API webinar!

<Het aanmaken van een Minimal API>

.NET 6 maakt automatisch een Minimal API aan als je een webproject maakt met .NET 6 als target framework. Dat doe je vanaf de command line met:

dotnet new web -n MinimalApi -o MinimalApi 

Dit commando maakt in de directory ‘MinimalApi’ een webproject aan met de naam ‘MinimalApi’. Het hele project bestaat, zoals je in de afbeelding ziet, maar uit 4 regels, onder andere door het gebruik van implicit usings. Daardoor ziet .NET veel usings als standaard. Dit maakt het geheel zeer compact en overzichtelijk. 

Minimal API Template

var builder = WebApplication.CreateBuilder(args); 

var app = builder.Build();

app.MapGet(“/”, () => “Hello World!”); 

app.Run();

De opzet lijkt erg op een Web API-project: we maken een WebBuilder aan en definiëren endpoints. Het verschil zit hem erin dat me nu direct een functionaliteit kunnen koppelen aan een endpoint zonder eerst bijvoorbeeld een controller aan te maken. 

Met dotnet run starten we de applicatie. In de browser zien we nu ‘Hello World’ verschijnen. 

Het bovenstaande resultaat word mogelijk gemaakt door de regel 

app.MapGet(“/”, () => “Hello World!”); 

Want daarmee maak je een nieuwe endpoint aan en koppel je functionaliteit. 

‘MapGet’ in deze regel betekent dat het gaat om een get-request. De eerste parameter is het path voor het endpoint. De tweede parameter is de daadwerkelijke opdracht. Dit kan zoals hier een lambda-expressie zijn. Maar je kunt er ook een lokale functie van maken of een static method uit een andere class. Ook is het mogelijk met dependency injection een method aan te roepen van een object. 

<Parameters gebruiken binnen je request>

Hello world is een goed begin, maar we willen natuurlijk ook data mee kunnen geven aan een request. Hier zijn meerdere mogelijkheden voor: via routeparameters, queryparameters of in de body. 

<Route Parameters>

Bij het meegeven van data via een route gebruik je een parameter die uit de opgegeven route komt. De routeparameter geef je op door hem tussen accolades in de route op te nemen. 

In het voorbeeld hieronder gebruiken we een parameter ‘name’ vanuit de route. We roepen deze route aan met ‘/HelloPerson/Jeroen’. Het resultaat is dat er ‘Hello Jeroen’ op het scherm komt. 

Parameters gebruiken  

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build(); 

app.MapGet(“/”, () => “Hello World!”);

app.MapGet(“/HelloPerson/{Name}”, (string name) => $”Hello {name}”); 

app.Run();

Je kunt de routeparameters ook beperken door route constraints (oftewel restricties) toe te voegen. Bijvoorbeeld als je een integer verwacht. In onderstaand voorbeeld is een parameter ‘age’ aangemaakt. De ‘:int’ zorgt dat er een integer verwacht wordt. Ga je naar ‘/AllowedToVote/18’ zal gaan, dan verschijnt de tekst ‘Allowed to vote’. 

Ga je naar ‘/AllowedToVote/Jeroen’, dan gebeurt er niets. Dit komt omdat deze route niet bekend is. Laat je de restrictie ‘:int’ weg, dan ontstaat er een exception omdat de .Net Runtime de tekst niet kan omzetten naar een integer. 

Allowed to vote:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build(); 

app.MapGet(“/AllowedToVote/{age:int}”,

         (int age) =>  age >= 18 ? “Allowed to vote” : “Not Allowed to vote”);

app.Run();

<Query Parameters >

Een andere optie is om data door te geven via de queryparameters. Dit doe je door een parameter aan je lambda toe te voegen. Het voorbeeld hieronder wordt uitgevoerd zodra je naar het endpoint /HelloPerson?name=Jeroen gaat. Laat je ?name=Jeroen weg, dan ontstaat er een foutmelding. 

Query parameters:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build(); 

app.MapGet(“/HelloPerson”, (string name) => $”Hello {name}”); 

app.Run();

Je kunt parameters optioneel maken met zogenaamde nullable reference type. Dat doe je door een vraagteken achter het type te zetten. Dit zorgt dat de parameter niet meer verplicht is. Hiermee voorkom je de foutmelding die in het voorbeeld optrad. 
 

<Body parameters >

Je kunt parameters ook meegeven in de body van het request. In een post-request doe je dat met een json-string. Deze string zet je om naar een object met System.Text.Json. 

Body-Parameters

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build(); 

app.MapPost(“/AddPerson”,

        (Person p) => $”Naam {p.Name} : age {p.Age}”); 

app.Run();

<Data teruggeven>

Tot nu toe gaven de voorbeelden alleen een string terug. Als we IResult implementeren, krijgen we veel meer opties, zoals Results.Ok(“Hello”) of bijvoorbeeld Results.StatusCode(400). De andere optie is om direct een object terug te geven. Dat wordt dan automatisch omgezet naar een jsonstring met application/json als contenttype.  

<Dependency injection>

Het is niet handig om alle code voor het afhandelen van je requests in program.cs te zetten. Daarom kun je, net als in ASP.net MVC, dependency injection gebruiken. Daarmee kun je bijvoorbeeld je businesslogica in een aparte class zetten. 

Om een class te registreren voor dependency injection gebruik je de IServiceCollection van onze WebApplicationBuilder. Hier kun je classes registeren zoals we ook bij ASP.Net MVC gewend zijn. Vervolgens kun je deze Service als parameter gebruiken in het endpoint en bijvoorbeeld een functie aanroepen. 

In het onderstaande voorbeeld registreren we de class ‘HelloService’ en roepen we vervolgens de method ‘Hello’ aan. 

Dependency injection:  

var builder = WebApplication.CreateBuilder(args); 

builder.Services.AddScoped<HelloService>();

var app = builder.Build(); 

app.MapGet(“/HelloPerson”, (string name, HelloService service) => service.Hello(name));

app.Run();

 

HelloService

public class HelloService

{

    public string Hello(string name ) => $”Hello {name}”;

}

<Swagger Middleware >

Een veel gebruikte tool bij het ontwikkelen van API’s is Swagger. Swagger maakt automatisch een beschrijving de API. Die kun je dan gebruiken in diverse andere tools, bijvoorbeeld om client libraries te genereren voor je API. Met de tool SwaggerUI kun je bijvoorbeeld in een simpele user interface snelle nieuwe endpoints aanmaken en testen. 

Als eerste voegen we de swagger services toe: 

builder.Services.AddEndpointsApiExplorer(); 

builder.Services.AddSwaggerGen(options => 

options.SwaggerDoc(“v1”, new() 

Title = “MinimalApi”

Version = “v1” 

}); 

}); 

Daarna moeten we de Swagger-middleware activeren: 

app.UseSwagger(); 

app.UseSwaggerUI(c =>
                 c.SwaggerEndpoint(“/swagger/v1/swagger.json”,“MinimalApi v1” )); 

Deze calls zorgen ervoor dat de compiler op het endpoint ‘/swagger/v1/swagger.json’ de beschrijving neerzet van je API. Deze beschrijving is (op het moment dat dit artikel online gaat) volgens de OpenApi Specification versie 3. 

Op het endpoint ‘/swagger’ vind je vervolgens de swaggerUI. Dit gebruik je om handmatig de API te testen. 

<Hoe gaan we Minimal APIs inzetten? >

In het voorbeeld hierboven kun je zien hoe met Minimal APIs snel en met weinig code een API kunt ontwikkelen. Maar Minimal APIs zijn geen vervanging voor het bouwen van Web-API’s. Het gaat hier om een aanvullende techniek, die vooral geschikt is voor kleine toepassingen en services. Of binnen een microservice-architectuur. Maar het is ook een perfecte manier om een proof of concept te maken en een demo te geven zonder veel randzaken te hoeven inregelen. Het grote voordeel van Minimal APIs is de compacte opzet. Dat maakt ze overzichtelijk (het betekent trouwens niet dat de code ook makkelijker te schrijven is). 

Houd er bij je keuze voor Minimal APIs rekening mee dat deze techniek nog niet zo lang bestaat. Er mist dus functionaliteit die wel in ASP.net MVC zit. Denk bijvoorbeeld aan het filteren van endpoints en typed results. Microsoft heeft veel van deze functionaliteiten al wel aangekondigd en beschikbaar gesteld in previewversies van .NET 7. Ik verwacht dus dat de mogelijkheden van Minimal APIs snel zullen gaan groeien. 

Het is voorlopig geen goed idee om bestaande projecten om te schrijven naar Minimal APIs. De voordelen daarvan zouden niet opwegen tegen de inspanning die het kost. De performance zou iets kunnen verbeteren, maar performance is afhankelijk van heel veel factoren, zoals de grootte en de complexiteit van de API, dus ook dat is niet zeker. 

Maar begin je aan een nieuwe API? Dan zou ik die zeker met de de Minimal API-functie schrijven. Wil je later toch overstappen naar de vertrouwde controller classes, dan is die wijziging relatief makkelijk te maken. 

<Meld je aan voor ons gratis webinar>

Op dinsdag 4 oktober organiseert Wiconic het Minimal API webinar! Tijdens het webinar laten we je zien hoe je een Minimal API integreert in je bestaande softwareomgeving, hoe je het veilig aanbiedt en welke problemen de lancering van .NET 7 oplost.