Minimal APIs in ASP.NET Core 6: Web-APIs jetzt anders?

Mit der Veröffentlichung von .NET 6 hat Microsoft nicht nur neue Features und Performance-Verbesserungen umgesetzt, sondern uns auch ein neues Entwicklungsmodell für Web-APIs bereitgestellt.
Hinweis: Alle Code-Beispiele sind auf Github verfügbar!

In diesem Artikel:

Minimal APIs in ASP.NET Core 6: Web-APIs jetzt anders?
Boris Wilhelms ist Consultant bei Thinktecture und hat sich auf .NET Core and Identity Management fokussiert.
Das neue, zusätzliche Entwicklungsmodell nennt sich Minimal-APIs und soll es erlauben, mit möglichst wenigen Zeilen Code Web-APIs zu erstellen. Dazu hat Microsoft Veränderungen an der Sprache C# vorgenommen, sowie eine neue Builder-Klasse für Minimal-APIs erstellt. Dadurch sind wir in der Lage, ähnlich wie in anderen Sprachen (zum Beispiel Go oder NodeJS), Web-APIs in einer einzigen Datei und mit kompaktem Code zu kreieren.

Kompaktere Form für Web-APIs

Schauen wir uns dazu kurz ein Beispiel an. Hier sehen wir eine Minimal-API, basierend auf der Standard-Projektvorlage in .NET 6. Bei diesem Beispiel wurde nichts weg gelassen, es kommen genau diese vier Zeilen, ohne usings-statements, Namespace- oder Klassen-Definition als Ergebnis heraus.

				
					var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
				
			

Dies verdeutlicht, dass wir nun in der Lage sind, mit vier Zeilen Code eine Web-API zu erstellen, die einen HTTP(S)-Endpunkt bereitstellt.

Doch was genau wurde gemacht, um dies zu ermöglichen? Hier gehen wir etwas auf die Details ein und schauen uns die verschiedenen Features an.

Globale & implizite using-Statements

Mit globalen using-Statements können wir Namespaces an einer zentralen Stelle definieren und diese werden dann in allen Dateien automatisch importiert. Zum Beispiel können wir mit dieser Zeile den System-Namespace in alle Dateien importieren.

				
					global using System;
				
			

Durch Implicit usings werden Namespaces importiert, die in einem SDK definiert werden. Ab .NET 6 werden dadurch für verschiedene Projekttypen automatisch die relevanten Namespaces importiert. Web-Projekte importieren Namespaces wie zum Beispiel System.Net.Http.Json oder Microsoft.AspNetCore.Http automatisch.

Durch diese beiden Features konnten so schon einige Zeilen eingespart werden und der Zugriff auf beispielsweise den Builder, ist ohne ein explizites using möglich.

Globale und implizite usings können übrigens ebenfalls in anderen Projekttypen genutzt werden und uns auch dort Code ersparen.

Top Level Statements

Mit Top Level Statements können wir Code, den wir normalerweise in einer Main-Methode haben, direkt in einer Datei schreiben, ohne eine Klasse oder einen Namespace zu definieren. Top Level Statements gibt es schon seit C# 9. Es ist daher kein neues Feature in .NET 6, aber erhält im Zusammenhang mit Minimal-APIs nochmal eine neue Wichtigkeit.

Wichtig ist: Top Level Statements funktionieren nur mit dem Inhalt der Main-Methode! Der Compiler generiert hier automatisch eine Klasse Program mit einer Methode Main, welche den Code beinhaltet.

Lambda als Web-API-Endpunkt-Handler

Im Gegensatz zu bisherigen, klassischen Web-APIs, schreiben wir bei Minimal-APIs nun keine Controller und Actions mehr, sondern hier werden Lambda-Ausdrücke als Endpunkt-Handler genutzt. Im folgenden Beispiel wird ein Lambda genutzt, welches den String Hello World zurück gibt. Der Lambda-Ausdruck wird bei einem HTTP-Get auf / ausgeführt.

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

				
			

Über die Argumente des Lambda-Ausdrucks können wir auf Route-Parameter, den Body des Requests oder uns über Dependency Injection auf Abhängigkeiten zugreifen.

				
					app.MapGet("/{name}", (string name) => $"Hello {name}!");
app.MapPost("/", (InputModel model) => $"Hello {model.Name}!");
app.MapGet("/greet/{name}", (string name, IGreeter greeter) => greeter.Greet(name));
				
			

Abhängigkeiten können auf einer ServiceCollection, die auf dem Builder liegt, registriert werden und dann über ein ganz normales Argument an den Lambda-Ausdruck genutzt werden.

				
					builder.Services.AddSingleton<IGreeter, Greeter>();
...
...
app.MapGet("/greet/{name}", (string name, IGreeter greeter) => greeter.Greet(name));
				
			

Mit C# 10 sind wir nun auch in der Lage, Lambdas mit Attributen zu annotieren. Wir können daher in Minimal-APIs, ähnlich wie bei klassischen Web-APIs, auch das Authorize-Attribut benutzen, um einen Endpunkt zu schützen. Wir können natürlich auch weitere Attribute hinzufügen, zum Beispiel, um unsere Swagger-Dokumentation mit weiteren Daten anzureichern.

				
					app.MapGet("/{name}", [Authorize] (string name) => $"Hello {name}!");
				
			

Statt einer Lambda-Funktion können wir den Code auch in eine separate Funktion auslagern und dann auf diese Funktion verweisen. Dies kann bei mehreren Endpunkten nützlich sein, um den Code besser zu strukturieren.

				
					app.MapGet("/{name}", Handler.Index);

public static class Handler
{
    public static string Index(string name, IGreeter greeter)
    {
        return greeter.Greet(name);
    }
}
				
			

Wenn wir aus unserem Endpunkt-Handler einen HTTP-Statuscode zurück geben wollen, so können wir die statische Results-Klasse verwenden, welche uns Zugriff auf diverse Standard-Statuscodes gibt oder aber auch die Möglichkeit bietet, einen benutzerdefinierten Statuscode zurückzugeben.

				
					app.MapGet("/TT", () => Results.Redirect("https://www.thinktecture.com/"));
app.MapGet("/Teapot", () => Results.StatusCode(StatusCodes.Status418ImATeapot));
				
			

Vor- und Nachteile von Minimal-APIs

Doch was sind denn nun die Vor- und Nachteile und wann soll ich Minimal-APIs nutzen und wann soll ich lieber eine klassische Web-API erstellen?

Minimal-APIs erlauben es uns sehr schnell und effektiv, einen neuen Service zu implementieren. Sie sind also optimal, wenn wir in einer Microservices-Architektur sind, bei der ein Service wirklich nur sehr wenige Endpunkte besitzt. Ist bereits absehbar, dass unsere API mehrere/viele Endpunkte haben wird, so sollte man lieber gleich mit einer normalen Web-API anfangen. Denn eine Minimal-API mit vielen Endpunkten kann sehr schnell sehr unübersichtlich werden.

Des Weiteren ist zu beachten, dass Minimal-APIs nicht auf der bisherigen ASP.NET-MVC-Architektur aufbauen und daher Features wie zum Beispiel Action-Filter oder Model-Binding entweder gar nicht vorhanden sind oder anders funktionieren. Hier sind wir also leider nicht in der Lage, bestehenden Code für Model-Binding wiederzuverwenden, sondern müssen diesen gegebenenfalls neu schreiben. Dies gilt besonders dann, wenn die API andere Formate als JSON unterstützen muss (z.B. Form-Data oder XML), da Minimal-APIs „out of the box“ nur JSON können.

Durch den Verzicht von MVC als Unterbau sind Minimal-APIs theoretisch auch etwas schneller und effizienter (kürzere Latenz, weniger Speicherallokation), da pro Request weniger Code ausgeführt werden muss. Praktisch ist der Geschwindigkeitsvorteil aber nicht relevant. Ein simpler Controller mit einer „Hello World“-Action kann lediglich 3-5% weniger Requests pro Sekunde verarbeiten als die „Hello World“-Minimal-API. Der wesentliche Faktor bei der Performance ist die Implementierung der Action/des Handlers und da helfen uns weder MVC noch Minimal-APIs.

Resumee

Minimal-APIs bieten uns die Möglichkeit, sehr schnell und effektiv kleine APIs und Proof-of-Concepts zu erstellen. Dadurch, dass sich der Code in einer Datei befindet, ist dieser auch schnell und effektiv anpassbar. Wird die API jedoch größer und komplexer, muss man sich über eine geeignet Code-Struktur Gedanken machen und gegebenenfalls wieder auf klassische MVC basierende Web-APIs wechseln.

Kostenloser
Newsletter

Aktuelle Artikel, Screencasts, Webinare und Interviews unserer Experten für Sie

Verpassen Sie keine Inhalte zu Angular, .NET Core, Blazor, Azure und Kubernetes und melden Sie sich zu unserem kostenlosen monatlichen Dev-Newsletter an.

Diese Artikel könnten Sie interessieren
.NET
Blazor WebAssembly in Practice: Maturity, Success Factors, Showstoppers

Blazor WebAssembly in Practice: Maturity, Success Factors, Showstoppers

ASP.NET Core Blazor is Microsoft's framework for implementing web-based applications, aimed at developers with knowledge of .NET and C#. It exists alongside other frameworks such as ASP.NET Core MVC. About two and a half years after the release of Blazor WebAssembly and based on our experiences from many customer projects at Thinktecture, we want to have a close look at the following questions: What is the current state of the framework? How can you successfully use Blazor? And where does it have limitations?
24.11.2022
.NET
Blazor WebAssembly: Debugging gRPC-Web with Custom Chrome Developer Tools

Blazor WebAssembly: Debugging gRPC-Web with Custom Chrome Developer Tools

If you are working with Blazor, gRPC is a big issue for transferring data from APIs to clients. One issue of developing with gRPC-Web is debugging the transmitted data because the data is in an efficient binary message format. In this article, I will show you how to solve this problem with the help of my NuGet.
17.11.2022
.NET
Entity Framework Core: User-defined Fields and Tables

Entity Framework Core: User-defined Fields and Tables

The requirement to store additional fields, unknown at development time, in a relational database is not new. Nonetheless, none of the projects I know of are willing to change the database structure at runtime. What if there is a project which needs dynamically created fields and doesn't want or cannot use entity–attribute–value model or switch to No-SQL databases?
20.09.2022
.NET
Incremental Roslyn Source Generators in .NET 6: Adapt Code Generation Based on Project Dependencies – Part 5

Incremental Roslyn Source Generators in .NET 6: Adapt Code Generation Based on Project Dependencies – Part 5

The Roslyn Source Generator, implemented in the previous articles of the series, emits some C# code without looking at the dependencies of the current .NET (Core) project. In this article our DemoSourceGenerator should implement a JsonConverter, but only if the corresponding library (e.g. Newtonsoft.Json) is referenced by the project.
08.07.2022
Unterschiede
.NET
Blazor WebAssembly vs. Blazor Server – Welche Unterschiede gibt es und wann wähle ich was?

Blazor WebAssembly vs. Blazor Server – Welche Unterschiede gibt es und wann wähle ich was?

Das Blazor Framework von Microsoft gibt es inzwischen in drei "Geschmacksrichtungen". Die erste ist Blazor WebAssembly, die zweite Blazor Server, und zu guter Letzt gibt es noch Blazor Hybrid. In diesem Artikel wollen wir uns die zwei "echten", also Browser-basierten, Web-Anwendungs-Szenarien WebAssembly und Server anschauen.
04.07.2022
Three different textured walls
.NET
Dependency Injection Scopes in Blazor

Dependency Injection Scopes in Blazor

The dependency injection system is a big part of how modern ASP.NET Core works internally: It provides a flexible solution for developers to structure their projects, decouple their dependencies, and control the lifetimes of the components within an application. In Blazor - a new part of ASP.NET Core - however, the DI system feels a bit odd, and things seem to work a bit differently than expected. This article will explain why this is not only a feeling but indeed the case in the first place and how to handle the differences in order to not run into problems later on.
31.05.2022