Minimal-APIs – Übersicht

Dieses Dokument hat folgende Eigenschaften:

Die Minimal-APIs bestehen aus:

WebApplication

Der folgende Code wird von einer ASP.NET Core-Vorlage generiert:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

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

app.Run();

Der vorstehende Code kann über dotnet new web in der Befehlszeile oder durch Auswahl der leeren Webvorlage in Visual Studio erstellt werden.

Mit dem folgenden Code wird eine WebApplication (app) erstellt, ohne explizit einen WebApplicationBuilder zu erstellen:

var app = WebApplication.Create(args);

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

app.Run();

WebApplication.Create initialisiert eine neue Instanz der WebApplication-Klasse mit vorkonfigurierten Standardwerten.

Arbeiten mit Ports

Beim Erstellen einer Web-App mit Visual Studio oder dotnet new wird eine Datei Properties/launchSettings.json erstellt, die die Ports angibt, an denen die Anwendung antwortet. In den folgenden Beispielen für Porteinstellungen wird beim Ausführen der App in Visual Studio ein Fehlerdialogfeld Unable to connect to web server 'AppName' angezeigt. Führen Sie die folgenden Beispiele für Portänderungen über die Befehlszeile aus.

In den folgenden Abschnitten wird der Port festgelegt, auf den die App reagiert.

var app = WebApplication.Create(args);

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

app.Run("http://localhost:3000");

Im vorangehenden Code antwortet die App auf Port 3000.

Mehrere Ports

Im folgenden Code antwortet die App auf Port 3000 und 4000.

var app = WebApplication.Create(args);

app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");

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

app.Run();

Festlegen des Ports über die Befehlszeile

Mit dem folgenden Befehl antwortet die App auf Port 7777:

dotnet run --urls="https://localhost:7777"

Wenn der Endpunkt Kestrel ebenfalls in der Datei appsettings.json konfiguriert ist, wird die in der Datei appsettings.json angegebene URL verwendet. Weitere Informationen finden Sie unter Kestrel-Endpunktkonfiguration.

Lesen des Ports aus der Umgebung

Der folgende Code liest den Port aus der Umgebung:

var app = WebApplication.Create(args);

var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";

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

app.Run($"http://localhost:{port}");

Die bevorzugte Methode zur Festlegung des Ports über die Umgebung ist die Verwendung der Umgebungsvariablen ASPNETCORE_URLS, die im folgenden Abschnitt beschrieben wird.

Festlegen der Ports über die ASPNETCORE_URLS-Umgebungsvariable

Für die Festlegung des Ports steht die Umgebungsvariable ASPNETCORE_URLS zur Verfügung:

ASPNETCORE_URLS=http://localhost:3000

ASPNETCORE_URLS unterstützt mehrere URLs:

ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000

Lauschen an allen Schnittstellen

Die folgenden Beispiele veranschaulichen das Lauschen an allen Schnittstellen.

http://*:3000

var app = WebApplication.Create(args);

app.Urls.Add("http://*:3000");

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

app.Run();

http://+:3000

var app = WebApplication.Create(args);

app.Urls.Add("http://+:3000");

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

app.Run();

http://0.0.0.0:3000

var app = WebApplication.Create(args);

app.Urls.Add("http://0.0.0.0:3000");

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

app.Run();

Lauschen an allen Schnittstellen mit ASPNETCORE_URLS

In den vorherigen Beispielen kann ASPNETCORE_URLS verwendet werden.

ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005

Angeben von HTTPS mit Entwicklungszertifikat

var app = WebApplication.Create(args);

app.Urls.Add("https://localhost:3000");

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

app.Run();

Weitere Informationen über das Entwicklungszertifikat finden Sie unter Vertrauen Sie dem ASP.NET Core-HTTPS-Entwicklungszertifikat unter Windows und macOS.

Angeben von HTTPS mithilfe eines benutzerdefinierten Zertifikats

Die folgenden Abschnitte zeigen, wie das benutzerdefinierte Zertifikat mithilfe der Datei appsettings.json und über die Konfiguration angegeben wird.

Angeben des benutzerdefinierten Zertifikats mit appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Kestrel": {
    "Certificates": {
      "Default": {
        "Path": "cert.pem",
        "KeyPath": "key.pem"
      }
    }
  }
}

Angeben des benutzerdefinierten Zertifikats über die Konfiguration

var builder = WebApplication.CreateBuilder(args);

// Configure the cert and the key
builder.Configuration["Kestrel:Certificates:Default:Path"] = "cert.pem";
builder.Configuration["Kestrel:Certificates:Default:KeyPath"] = "key.pem";

var app = builder.Build();

app.Urls.Add("https://localhost:3000");

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

app.Run();

Verwenden der Zertifikat-APIs

using System.Security.Cryptography.X509Certificates;

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(options =>
{
    options.ConfigureHttpsDefaults(httpsOptions =>
    {
        var certPath = Path.Combine(builder.Environment.ContentRootPath, "cert.pem");
        var keyPath = Path.Combine(builder.Environment.ContentRootPath, "key.pem");

        httpsOptions.ServerCertificate = X509Certificate2.CreateFromPemFile(certPath, 
                                         keyPath);
    });
});

var app = builder.Build();

app.Urls.Add("https://localhost:3000");

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

app.Run();

Lesen der Umgebung

var app = WebApplication.Create(args);

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/oops");
}

app.MapGet("/", () => "Hello World");
app.MapGet("/oops", () => "Oops! An error happened.");

app.Run();

Weitere Informationen zur Verwendung der Umgebung finden Sie unter Verwenden mehrerer Umgebungen in ASP.NET Core.

Konfiguration

Der folgende Code liest Informationen aus dem Konfigurationssystem:

var app = WebApplication.Create(args);

var message = app.Configuration["HelloKey"] ?? "Hello";

app.MapGet("/", () => message);

app.Run();

Weitere Informationen finden Sie unter Konfiguration in ASP.NET Core.

Protokollierung

Der folgende Code schreibt eine Meldung in das Anwendungsstartprotokoll:

var app = WebApplication.Create(args);

app.Logger.LogInformation("The app started");

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

app.Run();

Weitere Informationen finden Sie unter Protokollieren in .NET Core und ASP.NET Core.

Zugreifen auf den Container für Abhängigkeitsinjektion

Der folgende Code zeigt, wie Dienste während des Anwendungsstarts aus dem Abhängigkeitsinjektionscontainer abzurufen sind:


var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddScoped<SampleService>();

var app = builder.Build();

app.MapControllers();

using (var scope = app.Services.CreateScope())
{
    var sampleService = scope.ServiceProvider.GetRequiredService<SampleService>();
    sampleService.DoSomething();
}

app.Run();

Weitere Informationen finden Sie unter Abhängigkeitsinjektion in ASP.NET Core.

WebApplicationBuilder

Dieser Abschnitt enthält Beispielcode unter Verwendung von WebApplicationBuilder.

Ändern von Inhaltsstamm, Anwendungsname und Umgebung

Der folgende Code legt den Inhaltsstamm, den Anwendungsnamen und die Umgebung fest:

var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
    ApplicationName = typeof(Program).Assembly.FullName,
    ContentRootPath = Directory.GetCurrentDirectory(),
    EnvironmentName = Environments.Staging,
    WebRootPath = "customwwwroot"
});

Console.WriteLine($"Application Name: {builder.Environment.ApplicationName}");
Console.WriteLine($"Environment Name: {builder.Environment.EnvironmentName}");
Console.WriteLine($"ContentRoot Path: {builder.Environment.ContentRootPath}");
Console.WriteLine($"WebRootPath: {builder.Environment.WebRootPath}");

var app = builder.Build();

WebApplication.CreateBuilder Initialisiert eine neue Instanz der WebApplicationBuilder-Klasse mit vorkonfigurierten Standardwerten.

Weitere Informationen finden Sie unter ASP.NET Core – Grundlagenübersicht.

Ändern von Inhaltsstamm, App-Name und Umgebung über Umgebungsvariablen oder Befehlszeile

Die folgende Tabelle zeigt die Umgebungsvariablen und Befehlszeilenargumente, die zum Ändern von Inhaltsstamm, Anwendungsname und Umgebung verwendet werden:

Feature Umgebungsvariable Befehlszeilenargument
Anwendungsname ASPNETCORE_APPLICATIONNAME --applicationName
Umgebungsname ASPNETCORE_ENVIRONMENT --environment
Inhaltsstammverzeichnis ASPNETCORE_CONTENTROOT --contentRoot

Hinzufügen von Konfigurationsanbietern

Im folgenden Beispiel wird der INI-Konfigurationsanbieter hinzugefügt:

var builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddIniFile("appsettings.ini");

var app = builder.Build();

Ausführliche Informationen finden Sie unter Dateikonfigurationsanbieter in Konfiguration in ASP.NET Core.

Lesen der Konfiguration

Standardmäßig liest die WebApplicationBuilder die Konfiguration aus mehreren Quellen, darunter:

  • appSettings.json und appSettings.{environment}.json
  • Umgebungsvariablen
  • Die Befehlszeile

Eine vollständige Liste der gelesenen Konfigurationsquellen finden Sie unter Standardkonfiguration in Konfiguration in ASP.NET Core.

var builder = WebApplication.CreateBuilder(args);

var message = builder.Configuration["HelloKey"] ?? "Hello";

var app = builder.Build();

app.MapGet("/", () => message);

app.Run();

Lesen der Umgebung

Der folgende Code liest HelloKey aus der Konfiguration und zeigt den Wert am Endpunkt / an. Wenn der Konfigurationswert NULL ist, wird „Hello“ message zugewiesen:

var builder = WebApplication.CreateBuilder(args);

if (builder.Environment.IsDevelopment())
{
    Console.WriteLine($"Running in development.");
}

var app = builder.Build();

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

app.Run();

Hinzufügen von Protokollierungsanbietern

var builder = WebApplication.CreateBuilder(args);

// Configure JSON logging to the console.
builder.Logging.AddJsonConsole();

var app = builder.Build();

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

app.Run();

Hinzufügen von Diensten

var builder = WebApplication.CreateBuilder(args);

// Add the memory cache services.
builder.Services.AddMemoryCache();

// Add a custom scoped service.
builder.Services.AddScoped<ITodoRepository, TodoRepository>();
var app = builder.Build();

Anpassen von IHostBuilder

Vorhandene Erweiterungsmethoden für IHostBuilder können über die Host-Eigenschaft aufgerufen werden:

var builder = WebApplication.CreateBuilder(args);

// Wait 30 seconds for graceful shutdown.
builder.Host.ConfigureHostOptions(o => o.ShutdownTimeout = TimeSpan.FromSeconds(30));

var app = builder.Build();

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

app.Run();

Anpassen von IWebHostBuilder

Erweiterungsmethoden für IWebHostBuilder können über die Eigenschaft WebApplicationBuilder.WebHost aufgerufen werden.

var builder = WebApplication.CreateBuilder(args);

// Change the HTTP server implemenation to be HTTP.sys based
builder.WebHost.UseHttpSys();

var app = builder.Build();

app.MapGet("/", () => "Hello HTTP.sys");

app.Run();

Ändern des Webstamms

Standardmäßig ist der Webstamm relativ zum Inhaltsstamm im Ordner wwwroot angegeben. Im Webstamm sucht die Middleware für statische Dateien nach statischen Dateien. Der Webstamm kann mit WebHostOptions, der Befehlszeile oder mit der Methode UseWebRoot geändert werden:

var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
    Args = args,
    // Look for static files in webroot
    WebRootPath = "webroot"
});

var app = builder.Build();

app.Run();

Container für benutzerdefinierte Abhängigkeitsinjektion

Im folgenden Beispiel wird Autofac verwendet:

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());

// Register services directly with Autofac here. Don't
// call builder.Populate(), that happens in AutofacServiceProviderFactory.
builder.Host.ConfigureContainer<ContainerBuilder>(builder => builder.RegisterModule(new MyApplicationModule()));

var app = builder.Build();

Hinzufügen von Middleware

Für die WebApplication kann eine beliebige vorhandene ASP.NET Core-Middleware konfiguriert werden:

var app = WebApplication.Create(args);

// Setup the file server to serve static files.
app.UseFileServer();

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

app.Run();

Weitere Informationen finden Sie unter ASP.NET Core-Middleware.

Seite mit Ausnahmen für Entwickler

WebApplication.CreateBuilder initialisiert eine neue Instanz der WebApplicationBuilder-Klasse mit vorkonfigurierten Standardwerten. Die Seite mit Ausnahmen für Entwickler ist in den vorkonfigurierten Standardwerten aktiviert. Durch Ausführung des folgende Codes in der Entwicklungsumgebung wird beim Navigieren zu / eine benutzerfreundliche Seite geöffnet, auf der die Ausnahme angezeigt wird.

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/", () =>
{
    throw new InvalidOperationException("Oops, the '/' route has thrown an exception.");
});

app.Run();

ASP.NET Core-Middleware

In der folgenden Tabelle werden einige der Middlewarekomponenten aufgeführt, die häufig mit Minimal-APIs verwendet wird.

Middleware Beschreibung API
Authentifizierung Bietet Unterstützung für Authentifizierungen. UseAuthentication
Autorisierung Bietet Unterstützung für Authentifizierungen UseAuthorization
CORS Konfiguriert die Ressourcenfreigabe zwischen verschiedenen Ursprüngen (Cross-Origin Resource Sharing, CORS). UseCors
Ausnahmehandler Behandelt global Ausnahmen, die von der Middlewarepipeline ausgelöst werden. UseExceptionHandler
Weitergeleitete Header Leitet Proxyheader an die aktuelle Anforderung weiter. UseForwardedHeaders
HTTPS-Umleitung Leitet alle HTTP-Anforderungen an HTTPS um. UseHttpsRedirection
HTTP Strict Transport Security (HSTS) Middleware für erweiterte Sicherheit, die einen besonderen Antwortheader hinzufügt. UseHsts
Anforderungsprotokollierung Bietet Unterstützung für die Protokollierung von HTTP-Anforderungen und -Antworten. UseHttpLogging
W3C-Anforderungsprotokollierung Bietet Unterstützung für die Protokollierung von HTTP-Anforderungen und -Antworten im W3C-Format. UseW3CLogging
Zwischenspeichern von Antworten Bietet Unterstützung für das Zwischenspeichern von Antworten. UseResponseCaching
Antwortkomprimierung Bietet Unterstützung für das Komprimieren von Antworten. UseResponseCompression
Sitzung Bietet Unterstützung für das Verwalten von Benutzersitzungen. UseSession
Statische Dateien Bietet Unterstützung für das Verarbeiten statischer Dateien und das Durchsuchen des Verzeichnisses. UseStaticFiles, UseFileServer
WebSockets Aktiviert das WebSockets-Protokoll. UseWebSockets

Anforderungsverarbeitung

In den folgenden Abschnitten werden Routing, Parameterbindung und Antworten behandelt.

Routing

Eine konfigurierte WebApplication unterstützt Map{Verb} und MapMethods:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "This is a GET");
app.MapPost("/", () => "This is a POST");
app.MapPut("/", () => "This is a PUT");
app.MapDelete("/", () => "This is a DELETE");

app.MapMethods("/options-or-head", new[] { "OPTIONS", "HEAD" }, 
                          () => "This is an options or head request ");

app.Run();

Routenhandler

Routenhandler sind Methoden, die ausgeführt werden, wenn die Route übereinstimmt. Routenhandler können eine Funktion oder eine beliebige Form sein, sowohl synchron als auch asynchron. Als Routenhandler kann ein Lambdaausdruck, eine lokale Funktion, eine Instanzmethode oder eine statische Methode verwendet werden.

Lambdaausdruck

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/inline", () => "This is an inline lambda");

var handler = () => "This is a lambda variable";

app.MapGet("/", handler);

app.Run();

Lokale Funktion

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

string LocalFunction() => "This is local function";

app.MapGet("/", LocalFunction);

app.Run();

für eine Instanzmethode

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

var handler = new HelloHandler();

app.MapGet("/", handler.Hello);

app.Run();

class HelloHandler
{
    public string Hello()
    {
        return "Hello Instance method";
    }
}

für eine statische Methode

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", HelloHandler.Hello);

app.Run();

class HelloHandler
{
    public static string Hello()
    {
        return "Hello static method";
    }
}

Routen können Namen erhalten, um URLs für die Route zu generieren. Durch die Verwendung einer benannten Route entfällt das Hartcodieren von Pfaden in einer App:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/hello", () => "Hello named route")
   .WithName("hi");

app.MapGet("/", (LinkGenerator linker) => 
        $"The link to the hello route is {linker.GetPathByName("hi", values: null)}");

app.Run();

Der vorangehende Code zeigt The link to the hello route is /hello vom Endpunkt / an.

Routennamen werden aus Methodennamen abgeleitet, sofern angegeben:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

string Hi() => "Hello there";
app.MapGet("/hello", Hi);

app.MapGet("/", (LinkGenerator linker) => 
        $"The link to the hello route is {linker.GetPathByName("Hi", values: null)}");

app.Run();

INFO: {linker.GetPathByName("Hi", values: null)} lautet im vorstehenden Code NULL.

HINWEIS: Für Routennamen muss die Groß-/Kleinschreibung beachtet werden.

Routennamen:

  • Dieser muss global eindeutig sein.
  • Werden als OpenAPI-Vorgangs-ID verwendet, wenn die OpenAPI-Unterstützung aktiviert ist. Weitere Informationen finden Sie im Abschnitt zu OpenAPI.

Routenparameter

Routenparameter können als Teil der Routenmusterdefinition erfasst werden:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/users/{userId}/books/{bookId}", 
    (int userId, int bookId) => $"The user id is {userId} and book id is {bookId}");

app.Run();

Der vorstehende Code gibt The user id is 3 and book id is 7 aus dem URI /users/3/books/7 zurück.

Der Routenhandler kann die zu erfassende Parameter deklarieren. Wenn eine Anforderung über eine Route mit Parametern gestellt wird, die zur Erfassung deklariert sind, werden die Parameter analysiert und an den Handler übergeben. Dadurch können die Werte problemlos typsicher erfasst werden. Im vorangegangenen Code sind userId und bookId beide vom Typ int.

Wenn im vorstehenden Code einer der beiden Routenwerte nicht in den Typ int umgewandelt werden kann, wird eine Ausnahme ausgelöst. Die GET-Anforderung /users/hello/books/3 löst die folgende Ausnahme aus:

BadHttpRequestException: Failed to bind parameter "int userId" from "hello".

Platzhalterzeichen und Abfangen aller Routen

Der folgende Code zum Abfangen aller Routen gibt Routing to hello vom Endpunkt „/posts/hello“ zurück:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");

app.Run();

Routeneinschränkungen

Routeneinschränkungen schränken das Abgleichsverhalten einer Route ein.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/todos/{id:int}", (int id) => db.Todos.Find(id));
app.MapGet("/todos/{text}", (string text) => db.Todos.Where(t => t.Text.Contains(text));
app.MapGet("/posts/{slug:regex(^[a-z0-9_-]+$)}", (string slug) => $"Post {slug}");

app.Run();

Die folgende Tabelle zeigt die vorangegangenen Routenvorlagen und ihr Verhalten:

Routenvorlage Beispiel-URI für Übereinstimmung
/todos/{id:int} /todos/1
/todos/{text} /todos/something
/posts/{slug:regex(^[a-z0-9_-]+$)} /posts/mypost

Weitere Informationen finden Sie unter Referenz für Routeneinschränkungen in Routing in ASP.NET Core.

Parameterbindung

Die Parameterbindung ist der Prozess der Umwandlung von Anforderungsdaten in stark typisierte Parameter, die durch Routenhandler ausgedrückt werden. Eine Bindungsquelle bestimmt, von wo aus Parameter gebunden werden. Bindungsquellen können basierend auf der HTTP-Methode und dem Parametertyp explizit sein oder abgeleitet werden.

Unterstützte Bindungsquellen:

  • Routenwerte
  • Abfragezeichenfolge
  • Header
  • Text (als JSON)
  • Von der Abhängigkeitsinjektion bereitgestellte Dienste
  • Benutzerdefiniert

Hinweis

Die Bindung aus Formularen wird in .NET 6 nicht nativ unterstützt.

Im folgenden Beispiel verwendet der GET-Routenhandler einige dieser Parameterbindungsquellen:

var builder = WebApplication.CreateBuilder(args);

// Added as service
builder.Services.AddSingleton<Service>();

var app = builder.Build();

app.MapGet("/{id}", (int id,
                     int page,
                     [FromHeader(Name = "X-CUSTOM-HEADER")] string customHeader,
                     Service service) => { });

class Service { }

Die folgende Tabelle zeigt die Beziehung zwischen den im vorherigen Beispiel verwendeten Parametern und den zugeordneten Bindungsquellen.

Parameter Bindungsquelle
id Routenwert
page Abfragezeichenfolge
customHeader header
service Von der Abhängigkeitsinjektion bereitgestellt

Bei den HTTP-Methoden GET, HEAD, OPTIONS und DELETE erfolgt keine implizite Bindung aus dem Text. Um eine Bindung vom Textkörper (als JSON) für diese HTTP-Methoden zu verwenden, führen Sie explizit eine Bindung mit [FromBody] oder einen Lesevorgang aus HttpRequest durch.

Im folgenden Beispiel verwendet der POST-Routenhandler eine Bindungsquelle des Textkörpers (als JSON) für den Parameter person:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapPost("/", (Person person) => { });

record Person(string Name, int Age);

Die Parameter in den vorherigen Beispielen werden alle automatisch über Anforderungsdaten gebunden. Um die Benutzerfreundlichkeit der Parameterbindung zu veranschaulichen, zeigen die folgenden Beispielroutenhandler, wie Anforderungsdaten direkt aus der Anforderung gelesen werden:

app.MapGet("/{id}", (HttpRequest request) =>
{
    var id = request.RouteValues["id"];
    var page = request.Query["page"];
    var customHeader = request.Headers["X-CUSTOM-HEADER"];

    // ...
});

app.MapPost("/", async (HttpRequest request) =>
{
    var person = await request.ReadFromJsonAsync<Person>();

    // ...
});

Explizite Parameterbindung

Attribute können verwendet werden, um explizit zu deklarieren, von wo Parameter gebunden werden.

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

// Added as service
builder.Services.AddSingleton<Service>();

var app = builder.Build();


app.MapGet("/{id}", ([FromRoute] int id,
                     [FromQuery(Name = "p")] int page,
                     [FromServices] Service service,
                     [FromHeader(Name = "Content-Type")] string contentType) 
                     => {});

class Service { }

record Person(string Name, int Age);
Parameter Bindungsquelle
id Routenwert mit dem Namen id
page Abfragezeichenfolge mit dem Namen "p"
service Von der Abhängigkeitsinjektion bereitgestellt
contentType Header mit dem Namen "Content-Type"

Hinweis

Die Bindung aus Formularen wird in .NET 6 nicht nativ unterstützt.

Optionale Parameter

In Routenhandlern deklarierte Parameter werden als erforderlich behandelt:

  • Wenn eine Anforderung der Route entspricht, wird der Routenhandler nur ausgeführt, wenn alle erforderlichen Parameter in der Anforderung angegeben sind.
  • Sind nicht alle erforderlichen Parameter enthalten, kommt es zu einem Fehler.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/products", (int pageNumber) => $"Requesting page {pageNumber}");

app.Run();
URI result
/products?pageNumber=3 3 zurückgegeben
/products BadHttpRequestException: Der erforderliche Parameter „int pageNumber“ wurde nicht von der Abfragezeichenfolge bereitgestellt.
/products/1 HTTP-Fehler vom Typ 404, keine übereinstimmende Route

Um pageNumber als optional festzulegen, definieren Sie den Typ als optional, oder geben Sie einen Standardwert an:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");

string ListProducts(int pageNumber = 1) => $"Requesting page {pageNumber}";

app.MapGet("/products2", ListProducts);

app.Run();
URI result
/products?pageNumber=3 3 zurückgegeben
/products 1 zurückgegeben
/products2 1 zurückgegeben

Der vorangehende Nullwerte zulassende und Standardwert gilt für alle Quellen:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/products", (Product? product) => { });

app.Run();

Der stehende Code ruft die Methode mit einem NULL-Produkt auf, wenn kein Anforderungstext gesendet wird.

HINWEIS: Wenn ungültige Daten angegeben werden und der Parameter Nullwerte zulässt, wird der Routenhandler nicht ausgeführt.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");

app.Run();
URI result
/products?pageNumber=3 3 zurückgegeben
/products 1 zurückgegeben
/products/two BadHttpRequestException: Fehler beim Binden von Parameter "Nullable<int> pageNumber" aus „two“.

Weitere Informationen finden Sie im Abschnitt Bindungsfehler.

Sondertypen

Die folgenden Typen werden ohne explizite Attribute gebunden:

  • HttpContext: Der Kontext, der alle Informationen zur aktuellen HTTP-Anforderung oder -Antwort enthält:

    app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));
    
  • HttpRequest und HttpResponse: die HTTP-Anforderung und HTTP-Antwort:

    app.MapGet("/", (HttpRequest request, HttpResponse response) =>
        response.WriteAsync($"Hello World {request.Query["name"]}"));
    
  • CancellationToken: das mit der aktuellen HTTP-Anforderung verknüpfte Abbruchtoken:

    app.MapGet("/", async (CancellationToken cancellationToken) => 
        await MakeLongRunningRequestAsync(cancellationToken));
    
  • ClaimsPrincipal: der mit der Anforderung verknüpfte Benutzer, gebunden aus HttpContext.User:

    app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
    

Benutzerdefinierte Bindung

Es gibt zwei Möglichkeiten zum Anpassen der Parameterbindung:

  1. Binden Sie für Routen-, Abfrage- und Headerbindungsquellen benutzerdefinierte Typen, indem Sie eine statische TryParse-Methode für den Typ hinzufügen.
  2. Steuern Sie den Bindungsprozess, indem Sie eine BindAsync-Methode für einen Typ implementieren.

TryParse

TryParse umfasst zwei APIs:

public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);

Der folgende Code zeigt Point: 12.3, 10.1 mit dem URI /map?Point=12.3,10.1 an:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// GET /map?Point=12.3,10.1
app.MapGet("/map", (Point point) => $"Point: {point.X}, {point.Y}");

app.Run();

public class Point
{
    public double X { get; set; }
    public double Y { get; set; }

    public static bool TryParse(string? value, IFormatProvider? provider,
                                out Point? point)
    {
        // Format is "(12.3,10.1)"
        var trimmedValue = value?.TrimStart('(').TrimEnd(')');
        var segments = trimmedValue?.Split(',',
                StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        if (segments?.Length == 2
            && double.TryParse(segments[0], out var x)
            && double.TryParse(segments[1], out var y))
        {
            point = new Point { X = x, Y = y };
            return true;
        }

        point = null;
        return false;
    }
}

BindAsync

BindAsync umfasst die folgenden APIs:

public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);

Der nachstehende Code zeigt SortBy:xyz, SortDirection:Desc, CurrentPage:99 mit dem URI /products?SortBy=xyz&SortDir=Desc&Page=99 an:

using System.Reflection;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// GET /products?SortBy=xyz&SortDir=Desc&Page=99
app.MapGet("/products", (PagingData pageData) => $"SortBy:{pageData.SortBy}, " +
       $"SortDirection:{pageData.SortDirection}, CurrentPage:{pageData.CurrentPage}");

app.Run();

public class PagingData
{
    public string? SortBy { get; init; }
    public SortDirection SortDirection { get; init; }
    public int CurrentPage { get; init; } = 1;

    public static ValueTask<PagingData?> BindAsync(HttpContext context,
                                                   ParameterInfo parameter)
    {
        const string sortByKey = "sortBy";
        const string sortDirectionKey = "sortDir";
        const string currentPageKey = "page";

        Enum.TryParse<SortDirection>(context.Request.Query[sortDirectionKey],
                                     ignoreCase: true, out var sortDirection);
        int.TryParse(context.Request.Query[currentPageKey], out var page);
        page = page == 0 ? 1 : page;

        var result = new PagingData
        {
            SortBy = context.Request.Query[sortByKey],
            SortDirection = sortDirection,
            CurrentPage = page
        };

        return ValueTask.FromResult<PagingData?>(result);
    }
}

public enum SortDirection
{
    Default,
    Asc,
    Desc
}

Bindungsfehler

Wenn die Bindung fehlschlägt, protokolliert das Framework eine Debugmeldung und gibt abhängig vom Fehlermodus verschiedene Statuscodes an den Client zurück.

Fehlermodus Parametertypen, die Nullwerte zulassen Bindungsquelle Statuscode
{ParameterType}.TryParse gibt false zurück. ja Route/Abfrage/Header 400
{ParameterType}.BindAsync gibt null zurück. ja custom 400
{ParameterType}.BindAsync wird ausgelöst Nicht relevant custom 500
Fehler beim Deserialisieren des JSON-Texts Nicht relevant body 400
Falscher Inhaltstyp (nicht application/json) Nicht relevant body 415

Bindungsrangfolge

Die Regeln zum Bestimmen einer Bindungsquelle anhand eines Parameters:

  1. Explizites Attribut, das für den Parameter (From*-Attribute) in der folgenden Reihenfolge definiert ist:
    1. Routenwerte: [FromRoute]
    2. Abfragezeichenfolge: [FromQuery]
    3. Header: [FromHeader]
    4. Hauptteil: [FromBody]
    5. Service: [FromServices]
  2. Sondertypen
    1. HttpContext
    2. HttpRequest
    3. HttpResponse
    4. ClaimsPrincipal
    5. CancellationToken
  3. Der Parametertyp umfasst eine gültige BindAsync-Methode.
  4. Der Parametertyp lautet „string“ oder umfasst eine gültige TryParse-Methode.
    1. Wenn der Parametername in der Routenvorlage vorhanden ist, z. B. app.Map("/todo/{id}", (int id) => {});, wird er über die Route gebunden.
    2. Bindung über die Abfragezeichenfolge.
  5. Wenn der Parametertyp ein durch die Abhängigkeitsinjektion bereitgestellter Dienst ist, wird dieser Dienst als Quelle verwendet.
  6. Der Parameter stammt aus dem Text.

Anpassen der JSON-Bindung

Die Textbindungsquelle verwendet System.Text.Json für die Deserialisierung. Es ist nicht möglich, diese Standardeinstellung zu ändern, aber die Bindung kann mithilfe von anderen zuvor beschriebenen Techniken angepasst werden. Verwenden Sie zum Anpassen von JSON-Serialisierungsoptionen Code ähnlich dem folgenden:

using Microsoft.AspNetCore.Http.Json;

var builder = WebApplication.CreateBuilder(args);

// Configure JSON options.
builder.Services.Configure<JsonOptions>(options =>
{
    options.SerializerOptions.IncludeFields = true;
});

var app = builder.Build();

app.MapPost("/products", (Product product) => product);

app.Run();

class Product
{
    // These are public fields, not properties.
    public int Id;
    public string? Name;
}

Der vorangehende Code:

  • Konfiguriert die JSON-Standardoptionen sowohl für die Eingabe als auch für die Ausgabe.
  • Gibt folgenden JSON-Code zurück:
    {
      "id": 1,
      "name": "Joe Smith"
    }
    
    Wenn Folgendes gepostet wird:
    {
      "Id": 1,
      "Name": "Joe Smith"
    }
    

Lesen des Anforderungstexts

Lesen Sie den Anforderungstext direkt mithilfe des Parameters HttpContext oder HttpRequest:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/uploadstream", async (IConfiguration config, HttpRequest request) =>
{
    var filePath = Path.Combine(config["StoredFilesPath"], Path.GetRandomFileName());

    await using var writeStream = File.Create(filePath);
    await request.BodyReader.CopyToAsync(writeStream);
});

app.Run();

Der obige Code:

  • Greift mit HttpRequest.BodyReader auf den Anforderungstext zu.
  • Kopiert den Anforderungstext in eine lokale Datei.

Antworten

Routenhandler unterstützen die folgenden Typen von Rückgabewerten:

  1. IResult-basiert: Dies schließt Task<IResult> und ValueTask<IResult> ein.
  2. string: Dies schließt Task<string> und ValueTask<string> ein.
  3. T (ein beliebiger weiterer Typ): Dies schließt Task<T> und ValueTask<T> ein.
Rückgabewert Verhalten Content-Type
IResult Das Framework ruft IResult.ExecuteAsync auf. Richtet sich nach der IResult-Implementierung
string Das Framework schreibt die Zeichenfolge direkt in die Antwort. text/plain
T (beliebiger anderer Typ) Das Framework serialisiert die Antwort im JSON-Format. application/json

Beispielrückgabewerte

Rückgabewerte vom Typ „string“

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

JSON-Rückgabewerte

app.MapGet("/hello", () => new { Message = "Hello World" });

IResult-Rückgabewerte

app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));

Im folgenden Beispiel werden die integrierten Ergebnistypen verwendet, um die Antwort anzupassen:

app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
         await db.Todos.FindAsync(id) 
         is Todo todo
         ? Results.Ok(todo) 
         : Results.NotFound())
   .Produces<Todo>(StatusCodes.Status200OK)
   .Produces(StatusCodes.Status404NotFound);

JSON

app.MapGet("/hello", () => Results.Json(new { Message = "Hello World" }));

Benutzerdefinierter Statuscode

app.MapGet("/405", () => Results.StatusCode(405));

Text

app.MapGet("/text", () => Results.Text("This is some text"));

STREAM

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () => 
{
    var stream = await proxyClient.GetStreamAsync("http://consoto/pokedex.json");
    // Proxy the response as JSON
    return Results.Stream(stream, "application/json");
});

app.Run();

Umleiten

app.MapGet("/old-path", () => Results.Redirect("/new-path"));

Datei

app.MapGet("/download", () => Results.File("myfile.text"));

Integrierte Ergebnisse

Die statische Klasse Microsoft.AspNetCore.Http.Results enthält allgemeine Ergebnishilfen.

Beschreibung Antworttyp Statuscode API
Schreiben einer JSON-Antwort mit erweiterten Optionen Anwendung/json 200 Results.Json
Schreiben einer JSON-Antwort Anwendung/json 200 Results.Ok
Schreiben einer Textantwort text/plain (Standard), konfigurierbar 200 Results.Text
Schreiben der Antwort in Byte application/octet-stream (Standard), konfigurierbar 200 Results.Bytes
Schreiben eines Bytestreams in die Antwort application/octet-stream (Standard), konfigurierbar 200 Results.Stream
Streamen einer Datei in die Antwort zum Herunterladen mit dem content-disposition-Header application/octet-stream (Standard), konfigurierbar 200 Results.File
Festlegen des Statuscodes auf 404 mit optionaler JSON-Antwort 404 Results.NotFound
Festlegen des Statuscodes auf 204 204 Results.NoContent
Festlegen des Statuscodes auf 422 mit optionaler JSON-Antwort 422 Results.UnprocessableEntity
Festlegen des Statuscodes auf 400 mit optionaler JSON-Antwort 400 Results.BadRequest
Festlegen des Statuscodes auf 409 mit optionaler JSON-Antwort 409 Results.Conflict
Schreiben eines JSON-Objekts mit Problemdetails in die Antwort 500 (Standard), konfigurierbar Results.Problem
Schreiben eines JSON-Objekts mit Problemdetails in die Antwort, mit Validierungsfehlern –, konfigurierbar Results.ValidationProblem

Anpassen von Ergebnissen

Anwendungen können Antworten steuern, indem sie einen benutzerdefinierten IResult-Typ implementieren. Der folgende Code ist ein Beispiel für einen HTML-Ergebnistyp:

using System.Net.Mime;
using System.Text;
static class ResultsExtensions
{
    public static IResult Html(this IResultExtensions resultExtensions, string html)
    {
        ArgumentNullException.ThrowIfNull(resultExtensions);

        return new HtmlResult(html);
    }
}

class HtmlResult : IResult
{
    private readonly string _html;

    public HtmlResult(string html)
    {
        _html = html;
    }

    public Task ExecuteAsync(HttpContext httpContext)
    {
        httpContext.Response.ContentType = MediaTypeNames.Text.Html;
        httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(_html);
        return httpContext.Response.WriteAsync(_html);
    }
}

Es wird empfohlen, Microsoft.AspNetCore.Http.IResultExtensions eine Erweiterungsmethode hinzuzufügen, um diese benutzerdefinierten Ergebnisse leichter auffindbar zu machen.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/html", () => Results.Extensions.Html(@$"<!doctype html>
<html>
    <head><title>miniHTML</title></head>
    <body>
        <h1>Hello World</h1>
        <p>The time on the server is {DateTime.Now:O}</p>
    </body>
</html>"));

app.Run();

Autorisierung

Routen können mithilfe von Autorisierungsrichtlinien geschützt werden. Diese können mit dem Attribut [Authorize] oder der Methode RequireAuthorization angegeben werden.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebRPauth.Data;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization(o => o.AddPolicy("AdminsOnly", 
                                  b => b.RequireClaim("admin", "true")));

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

var app = builder.Build();

app.UseAuthorization();

app.MapGet("/auth", [Authorize] () => "This endpoint requires authorization.");
app.MapGet("/", () => "This endpoint doesn't require authorization.");
app.MapGet("/Identity/Account/Login", () => "Sign in page at this endpoint.");

app.Run();

Der vorangehende Code kann mit RequireAuthorization geschrieben werden:

app.MapGet("/auth", () => "This endpoint requires authorization")
   .RequireAuthorization();

Im folgenden Beispiel wird die richtlinienbasierte Autorisierung verwendet:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebRPauth.Data;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization(o => o.AddPolicy("AdminsOnly", 
                                  b => b.RequireClaim("admin", "true")));

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

var app = builder.Build();

app.UseAuthorization();

app.MapGet("/admin", [Authorize("AdminsOnly")] () => 
                             "The /admin endpoint is for admins only.");

app.MapGet("/admin2", () => "The /admin2 endpoint is for admins only.")
   .RequireAuthorization("AdminsOnly");

app.MapGet("/", () => "This endpoint doesn't require authorization.");
app.MapGet("/Identity/Account/Login", () => "Sign in page at this endpoint.");

app.Run();

Nicht authentifizierten Benutzern den Zugriff auf einen Endpunkt gestatten

Durch [AllowAnonymous] können nicht authentifizierte Benutzer auf Endpunkte zugreifen:

app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");


app.MapGet("/login2", () => "This endpoint also for all roles.")
   .AllowAnonymous();

CORS

Routen können mithilfe von CORS-Richtlinien für CORS aktiviert werden. CORS kann über das Attribut [EnableCors] oder mit der Methode RequireCors deklariert werden. In den folgenden Beispielen wird CORS aktiviert:

const string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      builder =>
                      {
                          builder.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

var app = builder.Build();
app.UseCors();

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

app.Run();
using Microsoft.AspNetCore.Cors;

const string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      builder =>
                      {
                          builder.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

var app = builder.Build();
app.UseCors();

app.MapGet("/cors", [EnableCors(MyAllowSpecificOrigins)] () => 
                           "This endpoint allows cross origin requests!");
app.MapGet("/cors2", () => "This endpoint allows cross origin requests!")
                     .RequireCors(MyAllowSpecificOrigins);

app.Run();

Weitere Informationen finden Sie unter Aktivieren ursprungsübergreifender Anforderungen (Cross-Origin Requests, CORS) in ASP.NET Core.

OpenAPI

Eine App kann die OpenAPI-Spezifikation für Routenhandler unter Verwendung von Swashbuckle beschreiben.

Der folgende Code ist eine typische ASP.NET Core-App mit OpenAPI-Unterstützung:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new() { Title = builder.Environment.ApplicationName,
                               Version = "v1" });
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json",
                                    $"{builder.Environment.ApplicationName} v1"));
}

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

app.Run();

Ausschließen der Open API-Beschreibung

Im folgenden Beispiel wird der /skipme-Endpunkt von der Generierung einer OpenAPI-Beschreibung ausgeschlossen:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.MapGet("/swag", () => "Hello Swagger!");
app.MapGet("/skipme", () => "Skipping Swagger.")
                    .ExcludeFromDescription();

app.Run();

Beschreiben von Antworttypen

Im folgenden Beispiel werden die integrierten Ergebnistypen verwendet, um die Antwort anzupassen:

app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
         await db.Todos.FindAsync(id) 
         is Todo todo
         ? Results.Ok(todo) 
         : Results.NotFound())
   .Produces<Todo>(StatusCodes.Status200OK)
   .Produces(StatusCodes.Status404NotFound);

Hinzufügen von Vorgangs-IDs zu Open API

app.MapGet("/todoitems2", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithName("GetToDoItems");

Hinzufügen von Tags zur Open API-Beschreibung

Im folgenden Code wird ein OpenAPI-Gruppierungstag verwendet:

app.MapGet("/todoitems", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithTags("TodoGroup");

Dieses Dokument hat folgende Eigenschaften:

Die Minimal-APIs bestehen aus:

WebApplication

Der folgende Code wird von einer ASP.NET Core-Vorlage generiert:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

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

app.Run();

Der vorstehende Code kann über dotnet new web in der Befehlszeile oder durch Auswahl der leeren Webvorlage in Visual Studio erstellt werden.

Mit dem folgenden Code wird eine WebApplication (app) erstellt, ohne explizit einen WebApplicationBuilder zu erstellen:

var app = WebApplication.Create(args);

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

app.Run();

WebApplication.Create initialisiert eine neue Instanz der WebApplication-Klasse mit vorkonfigurierten Standardwerten.

Arbeiten mit Ports

Beim Erstellen einer Web-App mit Visual Studio oder dotnet new wird eine Datei Properties/launchSettings.json erstellt, die die Ports angibt, an denen die Anwendung antwortet. In den folgenden Beispielen für Porteinstellungen wird beim Ausführen der App in Visual Studio ein Fehlerdialogfeld Unable to connect to web server 'AppName' angezeigt. Führen Sie die folgenden Beispiele für Portänderungen über die Befehlszeile aus.

In den folgenden Abschnitten wird der Port festgelegt, auf den die App reagiert.

var app = WebApplication.Create(args);

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

app.Run("http://localhost:3000");

Im vorangehenden Code antwortet die App auf Port 3000.

Mehrere Ports

Im folgenden Code antwortet die App auf Port 3000 und 4000.

var app = WebApplication.Create(args);

app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");

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

app.Run();

Festlegen des Ports über die Befehlszeile

Mit dem folgenden Befehl antwortet die App auf Port 7777:

dotnet run --urls="https://localhost:7777"

Wenn der Endpunkt Kestrel ebenfalls in der Datei appsettings.json konfiguriert ist, wird die in der Datei appsettings.json angegebene URL verwendet. Weitere Informationen finden Sie unter Kestrel-Endpunktkonfiguration.

Lesen des Ports aus der Umgebung

Der folgende Code liest den Port aus der Umgebung:

var app = WebApplication.Create(args);

var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";

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

app.Run($"http://localhost:{port}");

Die bevorzugte Methode zur Festlegung des Ports über die Umgebung ist die Verwendung der Umgebungsvariablen ASPNETCORE_URLS, die im folgenden Abschnitt beschrieben wird.

Festlegen der Ports über die ASPNETCORE_URLS-Umgebungsvariable

Für die Festlegung des Ports steht die Umgebungsvariable ASPNETCORE_URLS zur Verfügung:

ASPNETCORE_URLS=http://localhost:3000

ASPNETCORE_URLS unterstützt mehrere URLs:

ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000

Lauschen an allen Schnittstellen

Die folgenden Beispiele veranschaulichen das Lauschen an allen Schnittstellen.

http://*:3000

var app = WebApplication.Create(args);

app.Urls.Add("http://*:3000");

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

app.Run();

http://+:3000

var app = WebApplication.Create(args);

app.Urls.Add("http://+:3000");

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

app.Run();

http://0.0.0.0:3000

var app = WebApplication.Create(args);

app.Urls.Add("http://0.0.0.0:3000");

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

app.Run();

Lauschen an allen Schnittstellen mit ASPNETCORE_URLS

In den vorherigen Beispielen kann ASPNETCORE_URLS verwendet werden.

ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005

Angeben von HTTPS mit Entwicklungszertifikat

var app = WebApplication.Create(args);

app.Urls.Add("https://localhost:3000");

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

app.Run();

Weitere Informationen über das Entwicklungszertifikat finden Sie unter Vertrauen Sie dem ASP.NET Core-HTTPS-Entwicklungszertifikat unter Windows und macOS.

Angeben von HTTPS mithilfe eines benutzerdefinierten Zertifikats

Die folgenden Abschnitte zeigen, wie das benutzerdefinierte Zertifikat mithilfe der Datei appsettings.json und über die Konfiguration angegeben wird.

Angeben des benutzerdefinierten Zertifikats mit appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Kestrel": {
    "Certificates": {
      "Default": {
        "Path": "cert.pem",
        "KeyPath": "key.pem"
      }
    }
  }
}

Angeben des benutzerdefinierten Zertifikats über die Konfiguration

var builder = WebApplication.CreateBuilder(args);

// Configure the cert and the key
builder.Configuration["Kestrel:Certificates:Default:Path"] = "cert.pem";
builder.Configuration["Kestrel:Certificates:Default:KeyPath"] = "key.pem";

var app = builder.Build();

app.Urls.Add("https://localhost:3000");

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

app.Run();

Verwenden der Zertifikat-APIs

using System.Security.Cryptography.X509Certificates;

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(options =>
{
    options.ConfigureHttpsDefaults(httpsOptions =>
    {
        var certPath = Path.Combine(builder.Environment.ContentRootPath, "cert.pem");
        var keyPath = Path.Combine(builder.Environment.ContentRootPath, "key.pem");

        httpsOptions.ServerCertificate = X509Certificate2.CreateFromPemFile(certPath, 
                                         keyPath);
    });
});

var app = builder.Build();

app.Urls.Add("https://localhost:3000");

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

app.Run();

Lesen der Umgebung

var app = WebApplication.Create(args);

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/oops");
}

app.MapGet("/", () => "Hello World");
app.MapGet("/oops", () => "Oops! An error happened.");

app.Run();

Weitere Informationen zur Verwendung der Umgebung finden Sie unter Verwenden mehrerer Umgebungen in ASP.NET Core.

Konfiguration

Der folgende Code liest Informationen aus dem Konfigurationssystem:

var app = WebApplication.Create(args);

var message = app.Configuration["HelloKey"] ?? "Hello";

app.MapGet("/", () => message);

app.Run();

Weitere Informationen finden Sie unter Konfiguration in ASP.NET Core.

Protokollierung

Der folgende Code schreibt eine Meldung in das Anwendungsstartprotokoll:

var app = WebApplication.Create(args);

app.Logger.LogInformation("The app started");

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

app.Run();

Weitere Informationen finden Sie unter Protokollieren in .NET Core und ASP.NET Core.

Zugreifen auf den Container für Abhängigkeitsinjektion

Der folgende Code zeigt, wie Dienste während des Anwendungsstarts aus dem Abhängigkeitsinjektionscontainer abzurufen sind:


var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddScoped<SampleService>();

var app = builder.Build();

app.MapControllers();

using (var scope = app.Services.CreateScope())
{
    var sampleService = scope.ServiceProvider.GetRequiredService<SampleService>();
    sampleService.DoSomething();
}

app.Run();

Weitere Informationen finden Sie unter Abhängigkeitsinjektion in ASP.NET Core.

WebApplicationBuilder

Dieser Abschnitt enthält Beispielcode unter Verwendung von WebApplicationBuilder.

Ändern von Inhaltsstamm, Anwendungsname und Umgebung

Der folgende Code legt den Inhaltsstamm, den Anwendungsnamen und die Umgebung fest:

var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
    ApplicationName = typeof(Program).Assembly.FullName,
    ContentRootPath = Directory.GetCurrentDirectory(),
    EnvironmentName = Environments.Staging,
    WebRootPath = "customwwwroot"
});

Console.WriteLine($"Application Name: {builder.Environment.ApplicationName}");
Console.WriteLine($"Environment Name: {builder.Environment.EnvironmentName}");
Console.WriteLine($"ContentRoot Path: {builder.Environment.ContentRootPath}");
Console.WriteLine($"WebRootPath: {builder.Environment.WebRootPath}");

var app = builder.Build();

WebApplication.CreateBuilder Initialisiert eine neue Instanz der WebApplicationBuilder-Klasse mit vorkonfigurierten Standardwerten.

Weitere Informationen finden Sie unter ASP.NET Core – Grundlagenübersicht.

Ändern von Inhaltsstamm, App-Name und Umgebung über Umgebungsvariablen oder Befehlszeile

Die folgende Tabelle zeigt die Umgebungsvariablen und Befehlszeilenargumente, die zum Ändern von Inhaltsstamm, Anwendungsname und Umgebung verwendet werden:

Feature Umgebungsvariable Befehlszeilenargument
Anwendungsname ASPNETCORE_APPLICATIONNAME --applicationName
Umgebungsname ASPNETCORE_ENVIRONMENT --environment
Inhaltsstammverzeichnis ASPNETCORE_CONTENTROOT --contentRoot

Hinzufügen von Konfigurationsanbietern

Im folgenden Beispiel wird der INI-Konfigurationsanbieter hinzugefügt:

var builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddIniFile("appsettings.ini");

var app = builder.Build();

Ausführliche Informationen finden Sie unter Dateikonfigurationsanbieter in Konfiguration in ASP.NET Core.

Lesen der Konfiguration

Standardmäßig liest die WebApplicationBuilder die Konfiguration aus mehreren Quellen, darunter:

  • appSettings.json und appSettings.{environment}.json
  • Umgebungsvariablen
  • Die Befehlszeile

Eine vollständige Liste der gelesenen Konfigurationsquellen finden Sie unter Standardkonfiguration in Konfiguration in ASP.NET Core.

var builder = WebApplication.CreateBuilder(args);

var message = builder.Configuration["HelloKey"] ?? "Hello";

var app = builder.Build();

app.MapGet("/", () => message);

app.Run();

Lesen der Umgebung

Der folgende Code liest HelloKey aus der Konfiguration und zeigt den Wert am Endpunkt / an. Wenn der Konfigurationswert NULL ist, wird „Hello“ message zugewiesen:

var builder = WebApplication.CreateBuilder(args);

if (builder.Environment.IsDevelopment())
{
    Console.WriteLine($"Running in development.");
}

var app = builder.Build();

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

app.Run();

Hinzufügen von Protokollierungsanbietern

var builder = WebApplication.CreateBuilder(args);

// Configure JSON logging to the console.
builder.Logging.AddJsonConsole();

var app = builder.Build();

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

app.Run();

Hinzufügen von Diensten

var builder = WebApplication.CreateBuilder(args);

// Add the memory cache services.
builder.Services.AddMemoryCache();

// Add a custom scoped service.
builder.Services.AddScoped<ITodoRepository, TodoRepository>();
var app = builder.Build();

Anpassen von IHostBuilder

Vorhandene Erweiterungsmethoden für IHostBuilder können über die Host-Eigenschaft aufgerufen werden:

var builder = WebApplication.CreateBuilder(args);

// Wait 30 seconds for graceful shutdown.
builder.Host.ConfigureHostOptions(o => o.ShutdownTimeout = TimeSpan.FromSeconds(30));

var app = builder.Build();

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

app.Run();

Anpassen von IWebHostBuilder

Erweiterungsmethoden für IWebHostBuilder können über die Eigenschaft WebApplicationBuilder.WebHost aufgerufen werden.

var builder = WebApplication.CreateBuilder(args);

// Change the HTTP server implemenation to be HTTP.sys based
builder.WebHost.UseHttpSys();

var app = builder.Build();

app.MapGet("/", () => "Hello HTTP.sys");

app.Run();

Ändern des Webstamms

Standardmäßig ist der Webstamm relativ zum Inhaltsstamm im Ordner wwwroot angegeben. Im Webstamm sucht die Middleware für statische Dateien nach statischen Dateien. Der Webstamm kann mit WebHostOptions, der Befehlszeile oder mit der Methode UseWebRoot geändert werden:

var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
    Args = args,
    // Look for static files in webroot
    WebRootPath = "webroot"
});

var app = builder.Build();

app.Run();

Container für benutzerdefinierte Abhängigkeitsinjektion

Im folgenden Beispiel wird Autofac verwendet:

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());

// Register services directly with Autofac here. Don't
// call builder.Populate(), that happens in AutofacServiceProviderFactory.
builder.Host.ConfigureContainer<ContainerBuilder>(builder => builder.RegisterModule(new MyApplicationModule()));

var app = builder.Build();

Hinzufügen von Middleware

Für die WebApplication kann eine beliebige vorhandene ASP.NET Core-Middleware konfiguriert werden:

var app = WebApplication.Create(args);

// Setup the file server to serve static files.
app.UseFileServer();

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

app.Run();

Weitere Informationen finden Sie unter ASP.NET Core-Middleware.

Seite mit Ausnahmen für Entwickler

WebApplication.CreateBuilder initialisiert eine neue Instanz der WebApplicationBuilder-Klasse mit vorkonfigurierten Standardwerten. Die Seite mit Ausnahmen für Entwickler ist in den vorkonfigurierten Standardwerten aktiviert. Durch Ausführung des folgende Codes in der Entwicklungsumgebung wird beim Navigieren zu / eine benutzerfreundliche Seite geöffnet, auf der die Ausnahme angezeigt wird.

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/", () =>
{
    throw new InvalidOperationException("Oops, the '/' route has thrown an exception.");
});

app.Run();

ASP.NET Core-Middleware

In der folgenden Tabelle werden einige der Middlewarekomponenten aufgeführt, die häufig mit Minimal-APIs verwendet wird.

Middleware Beschreibung API
Authentifizierung Bietet Unterstützung für Authentifizierungen. UseAuthentication
Autorisierung Bietet Unterstützung für Authentifizierungen UseAuthorization
CORS Konfiguriert die Ressourcenfreigabe zwischen verschiedenen Ursprüngen (Cross-Origin Resource Sharing, CORS). UseCors
Ausnahmehandler Behandelt global Ausnahmen, die von der Middlewarepipeline ausgelöst werden. UseExceptionHandler
Weitergeleitete Header Leitet Proxyheader an die aktuelle Anforderung weiter. UseForwardedHeaders
HTTPS-Umleitung Leitet alle HTTP-Anforderungen an HTTPS um. UseHttpsRedirection
HTTP Strict Transport Security (HSTS) Middleware für erweiterte Sicherheit, die einen besonderen Antwortheader hinzufügt. UseHsts
Anforderungsprotokollierung Bietet Unterstützung für die Protokollierung von HTTP-Anforderungen und -Antworten. UseHttpLogging
W3C-Anforderungsprotokollierung Bietet Unterstützung für die Protokollierung von HTTP-Anforderungen und -Antworten im W3C-Format. UseW3CLogging
Zwischenspeichern von Antworten Bietet Unterstützung für das Zwischenspeichern von Antworten. UseResponseCaching
Antwortkomprimierung Bietet Unterstützung für das Komprimieren von Antworten. UseResponseCompression
Sitzung Bietet Unterstützung für das Verwalten von Benutzersitzungen. UseSession
Statische Dateien Bietet Unterstützung für das Verarbeiten statischer Dateien und das Durchsuchen des Verzeichnisses. UseStaticFiles, UseFileServer
WebSockets Aktiviert das WebSockets-Protokoll. UseWebSockets

Anforderungsverarbeitung

In den folgenden Abschnitten werden Routing, Parameterbindung und Antworten behandelt.

Routing

Eine konfigurierte WebApplication unterstützt Map{Verb} und MapMethods:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "This is a GET");
app.MapPost("/", () => "This is a POST");
app.MapPut("/", () => "This is a PUT");
app.MapDelete("/", () => "This is a DELETE");

app.MapMethods("/options-or-head", new[] { "OPTIONS", "HEAD" }, 
                          () => "This is an options or head request ");

app.Run();

Routenhandler

Routenhandler sind Methoden, die ausgeführt werden, wenn die Route übereinstimmt. Routenhandler können eine Funktion oder eine beliebige Form sein, sowohl synchron als auch asynchron. Als Routenhandler kann ein Lambdaausdruck, eine lokale Funktion, eine Instanzmethode oder eine statische Methode verwendet werden.

Lambdaausdruck

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/inline", () => "This is an inline lambda");

var handler = () => "This is a lambda variable";

app.MapGet("/", handler);

app.Run();

Lokale Funktion

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

string LocalFunction() => "This is local function";

app.MapGet("/", LocalFunction);

app.Run();

für eine Instanzmethode

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

var handler = new HelloHandler();

app.MapGet("/", handler.Hello);

app.Run();

class HelloHandler
{
    public string Hello()
    {
        return "Hello Instance method";
    }
}

für eine statische Methode

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", HelloHandler.Hello);

app.Run();

class HelloHandler
{
    public static string Hello()
    {
        return "Hello static method";
    }
}

Routen können Namen erhalten, um URLs für die Route zu generieren. Durch die Verwendung einer benannten Route entfällt das Hartcodieren von Pfaden in einer App:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/hello", () => "Hello named route")
   .WithName("hi");

app.MapGet("/", (LinkGenerator linker) => 
        $"The link to the hello route is {linker.GetPathByName("hi", values: null)}");

app.Run();

Der vorangehende Code zeigt The link to the hello route is /hello vom Endpunkt / an.

Routennamen werden aus Methodennamen abgeleitet, sofern angegeben:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

string Hi() => "Hello there";
app.MapGet("/hello", Hi);

app.MapGet("/", (LinkGenerator linker) => 
        $"The link to the hello route is {linker.GetPathByName("Hi", values: null)}");

app.Run();

INFO: {linker.GetPathByName("Hi", values: null)} lautet im vorstehenden Code NULL.

HINWEIS: Für Routennamen muss die Groß-/Kleinschreibung beachtet werden.

Routennamen:

  • Dieser muss global eindeutig sein.
  • Werden als OpenAPI-Vorgangs-ID verwendet, wenn die OpenAPI-Unterstützung aktiviert ist. Weitere Informationen finden Sie im Abschnitt zu OpenAPI.

Routenparameter

Routenparameter können als Teil der Routenmusterdefinition erfasst werden:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/users/{userId}/books/{bookId}", 
    (int userId, int bookId) => $"The user id is {userId} and book id is {bookId}");

app.Run();

Der vorstehende Code gibt The user id is 3 and book id is 7 aus dem URI /users/3/books/7 zurück.

Der Routenhandler kann die zu erfassende Parameter deklarieren. Wenn eine Anforderung über eine Route mit Parametern gestellt wird, die zur Erfassung deklariert sind, werden die Parameter analysiert und an den Handler übergeben. Dadurch können die Werte problemlos typsicher erfasst werden. Im vorangegangenen Code sind userId und bookId beide vom Typ int.

Wenn im vorstehenden Code einer der beiden Routenwerte nicht in den Typ int umgewandelt werden kann, wird eine Ausnahme ausgelöst. Die GET-Anforderung /users/hello/books/3 löst die folgende Ausnahme aus:

BadHttpRequestException: Failed to bind parameter "int userId" from "hello".

Platzhalterzeichen und Abfangen aller Routen

Der folgende Code zum Abfangen aller Routen gibt Routing to hello vom Endpunkt „/posts/hello“ zurück:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");

app.Run();

Routeneinschränkungen

Routeneinschränkungen schränken das Abgleichsverhalten einer Route ein.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/todos/{id:int}", (int id) => db.Todos.Find(id));
app.MapGet("/todos/{text}", (string text) => db.Todos.Where(t => t.Text.Contains(text));
app.MapGet("/posts/{slug:regex(^[a-z0-9_-]+$)}", (string slug) => $"Post {slug}");

app.Run();

Die folgende Tabelle zeigt die vorangegangenen Routenvorlagen und ihr Verhalten:

Routenvorlage Beispiel-URI für Übereinstimmung
/todos/{id:int} /todos/1
/todos/{text} /todos/something
/posts/{slug:regex(^[a-z0-9_-]+$)} /posts/mypost

Weitere Informationen finden Sie unter Referenz für Routeneinschränkungen in Routing in ASP.NET Core.

Parameterbindung

Die Parameterbindung ist der Prozess der Umwandlung von Anforderungsdaten in stark typisierte Parameter, die durch Routenhandler ausgedrückt werden. Eine Bindungsquelle bestimmt, von wo aus Parameter gebunden werden. Bindungsquellen können basierend auf der HTTP-Methode und dem Parametertyp explizit sein oder abgeleitet werden.

Unterstützte Bindungsquellen:

  • Routenwerte
  • Abfragezeichenfolge
  • Header
  • Text (als JSON)
  • Von der Abhängigkeitsinjektion bereitgestellte Dienste
  • Benutzerdefiniert

Hinweis

Die Bindung aus Formularen wird in .NET 6 nicht nativ unterstützt.

Im folgenden Beispiel verwendet der GET-Routenhandler einige dieser Parameterbindungsquellen:

var builder = WebApplication.CreateBuilder(args);

// Added as service
builder.Services.AddSingleton<Service>();

var app = builder.Build();

app.MapGet("/{id}", (int id,
                     int page,
                     [FromHeader(Name = "X-CUSTOM-HEADER")] string customHeader,
                     Service service) => { });

class Service { }

Die folgende Tabelle zeigt die Beziehung zwischen den im vorherigen Beispiel verwendeten Parametern und den zugeordneten Bindungsquellen.

Parameter Bindungsquelle
id Routenwert
page Abfragezeichenfolge
customHeader header
service Von der Abhängigkeitsinjektion bereitgestellt

Bei den HTTP-Methoden GET, HEAD, OPTIONS und DELETE erfolgt keine implizite Bindung aus dem Text. Um eine Bindung vom Textkörper (als JSON) für diese HTTP-Methoden zu verwenden, führen Sie explizit eine Bindung mit [FromBody] oder einen Lesevorgang aus HttpRequest durch.

Im folgenden Beispiel verwendet der POST-Routenhandler eine Bindungsquelle des Textkörpers (als JSON) für den Parameter person:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapPost("/", (Person person) => { });

record Person(string Name, int Age);

Die Parameter in den vorherigen Beispielen werden alle automatisch über Anforderungsdaten gebunden. Um die Benutzerfreundlichkeit der Parameterbindung zu veranschaulichen, zeigen die folgenden Beispielroutenhandler, wie Anforderungsdaten direkt aus der Anforderung gelesen werden:

app.MapGet("/{id}", (HttpRequest request) =>
{
    var id = request.RouteValues["id"];
    var page = request.Query["page"];
    var customHeader = request.Headers["X-CUSTOM-HEADER"];

    // ...
});

app.MapPost("/", async (HttpRequest request) =>
{
    var person = await request.ReadFromJsonAsync<Person>();

    // ...
});

Explizite Parameterbindung

Attribute können verwendet werden, um explizit zu deklarieren, von wo Parameter gebunden werden.

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

// Added as service
builder.Services.AddSingleton<Service>();

var app = builder.Build();


app.MapGet("/{id}", ([FromRoute] int id,
                     [FromQuery(Name = "p")] int page,
                     [FromServices] Service service,
                     [FromHeader(Name = "Content-Type")] string contentType) 
                     => {});

class Service { }

record Person(string Name, int Age);
Parameter Bindungsquelle
id Routenwert mit dem Namen id
page Abfragezeichenfolge mit dem Namen "p"
service Von der Abhängigkeitsinjektion bereitgestellt
contentType Header mit dem Namen "Content-Type"

Hinweis

Die Bindung aus Formularen wird in .NET 6 nicht nativ unterstützt.

Optionale Parameter

In Routenhandlern deklarierte Parameter werden als erforderlich behandelt:

  • Wenn eine Anforderung der Route entspricht, wird der Routenhandler nur ausgeführt, wenn alle erforderlichen Parameter in der Anforderung angegeben sind.
  • Sind nicht alle erforderlichen Parameter enthalten, kommt es zu einem Fehler.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/products", (int pageNumber) => $"Requesting page {pageNumber}");

app.Run();
URI result
/products?pageNumber=3 3 zurückgegeben
/products BadHttpRequestException: Der erforderliche Parameter „int pageNumber“ wurde nicht von der Abfragezeichenfolge bereitgestellt.
/products/1 HTTP-Fehler vom Typ 404, keine übereinstimmende Route

Um pageNumber als optional festzulegen, definieren Sie den Typ als optional, oder geben Sie einen Standardwert an:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");

string ListProducts(int pageNumber = 1) => $"Requesting page {pageNumber}";

app.MapGet("/products2", ListProducts);

app.Run();
URI result
/products?pageNumber=3 3 zurückgegeben
/products 1 zurückgegeben
/products2 1 zurückgegeben

Der vorangehende Nullwerte zulassende und Standardwert gilt für alle Quellen:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/products", (Product? product) => { });

app.Run();

Der stehende Code ruft die Methode mit einem NULL-Produkt auf, wenn kein Anforderungstext gesendet wird.

HINWEIS: Wenn ungültige Daten angegeben werden und der Parameter Nullwerte zulässt, wird der Routenhandler nicht ausgeführt.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");

app.Run();
URI result
/products?pageNumber=3 3 zurückgegeben
/products 1 zurückgegeben
/products/two BadHttpRequestException: Fehler beim Binden von Parameter "Nullable<int> pageNumber" aus „two“.

Weitere Informationen finden Sie im Abschnitt Bindungsfehler.

Sondertypen

Die folgenden Typen werden ohne explizite Attribute gebunden:

  • HttpContext: Der Kontext, der alle Informationen zur aktuellen HTTP-Anforderung oder -Antwort enthält:

    app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));
    
  • HttpRequest und HttpResponse: die HTTP-Anforderung und HTTP-Antwort:

    app.MapGet("/", (HttpRequest request, HttpResponse response) =>
        response.WriteAsync($"Hello World {request.Query["name"]}"));
    
  • CancellationToken: das mit der aktuellen HTTP-Anforderung verknüpfte Abbruchtoken:

    app.MapGet("/", async (CancellationToken cancellationToken) => 
        await MakeLongRunningRequestAsync(cancellationToken));
    
  • ClaimsPrincipal: der mit der Anforderung verknüpfte Benutzer, gebunden aus HttpContext.User:

    app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
    

Benutzerdefinierte Bindung

Es gibt zwei Möglichkeiten zum Anpassen der Parameterbindung:

  1. Binden Sie für Routen-, Abfrage- und Headerbindungsquellen benutzerdefinierte Typen, indem Sie eine statische TryParse-Methode für den Typ hinzufügen.
  2. Steuern Sie den Bindungsprozess, indem Sie eine BindAsync-Methode für einen Typ implementieren.

TryParse

TryParse umfasst zwei APIs:

public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);

Der folgende Code zeigt Point: 12.3, 10.1 mit dem URI /map?Point=12.3,10.1 an:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// GET /map?Point=12.3,10.1
app.MapGet("/map", (Point point) => $"Point: {point.X}, {point.Y}");

app.Run();

public class Point
{
    public double X { get; set; }
    public double Y { get; set; }

    public static bool TryParse(string? value, IFormatProvider? provider,
                                out Point? point)
    {
        // Format is "(12.3,10.1)"
        var trimmedValue = value?.TrimStart('(').TrimEnd(')');
        var segments = trimmedValue?.Split(',',
                StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        if (segments?.Length == 2
            && double.TryParse(segments[0], out var x)
            && double.TryParse(segments[1], out var y))
        {
            point = new Point { X = x, Y = y };
            return true;
        }

        point = null;
        return false;
    }
}

BindAsync

BindAsync umfasst die folgenden APIs:

public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);

Der nachstehende Code zeigt SortBy:xyz, SortDirection:Desc, CurrentPage:99 mit dem URI /products?SortBy=xyz&SortDir=Desc&Page=99 an:

using System.Reflection;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// GET /products?SortBy=xyz&SortDir=Desc&Page=99
app.MapGet("/products", (PagingData pageData) => $"SortBy:{pageData.SortBy}, " +
       $"SortDirection:{pageData.SortDirection}, CurrentPage:{pageData.CurrentPage}");

app.Run();

public class PagingData
{
    public string? SortBy { get; init; }
    public SortDirection SortDirection { get; init; }
    public int CurrentPage { get; init; } = 1;

    public static ValueTask<PagingData?> BindAsync(HttpContext context,
                                                   ParameterInfo parameter)
    {
        const string sortByKey = "sortBy";
        const string sortDirectionKey = "sortDir";
        const string currentPageKey = "page";

        Enum.TryParse<SortDirection>(context.Request.Query[sortDirectionKey],
                                     ignoreCase: true, out var sortDirection);
        int.TryParse(context.Request.Query[currentPageKey], out var page);
        page = page == 0 ? 1 : page;

        var result = new PagingData
        {
            SortBy = context.Request.Query[sortByKey],
            SortDirection = sortDirection,
            CurrentPage = page
        };

        return ValueTask.FromResult<PagingData?>(result);
    }
}

public enum SortDirection
{
    Default,
    Asc,
    Desc
}

Bindungsfehler

Wenn die Bindung fehlschlägt, protokolliert das Framework eine Debugmeldung und gibt abhängig vom Fehlermodus verschiedene Statuscodes an den Client zurück.

Fehlermodus Parametertypen, die Nullwerte zulassen Bindungsquelle Statuscode
{ParameterType}.TryParse gibt false zurück. ja Route/Abfrage/Header 400
{ParameterType}.BindAsync gibt null zurück. ja custom 400
{ParameterType}.BindAsync wird ausgelöst Nicht relevant custom 500
Fehler beim Deserialisieren des JSON-Texts Nicht relevant body 400
Falscher Inhaltstyp (nicht application/json) Nicht relevant body 415

Bindungsrangfolge

Die Regeln zum Bestimmen einer Bindungsquelle anhand eines Parameters:

  1. Explizites Attribut, das für den Parameter (From*-Attribute) in der folgenden Reihenfolge definiert ist:
    1. Routenwerte: [FromRoute]
    2. Abfragezeichenfolge: [FromQuery]
    3. Header: [FromHeader]
    4. Hauptteil: [FromBody]
    5. Service: [FromServices]
  2. Sondertypen
    1. HttpContext
    2. HttpRequest
    3. HttpResponse
    4. ClaimsPrincipal
    5. CancellationToken
  3. Der Parametertyp umfasst eine gültige BindAsync-Methode.
  4. Der Parametertyp lautet „string“ oder umfasst eine gültige TryParse-Methode.
    1. Wenn der Parametername in der Routenvorlage vorhanden ist, z. B. app.Map("/todo/{id}", (int id) => {});, wird er über die Route gebunden.
    2. Bindung über die Abfragezeichenfolge.
  5. Wenn der Parametertyp ein durch die Abhängigkeitsinjektion bereitgestellter Dienst ist, wird dieser Dienst als Quelle verwendet.
  6. Der Parameter stammt aus dem Text.

Anpassen der JSON-Bindung

Die Textbindungsquelle verwendet System.Text.Json für die Deserialisierung. Es ist nicht möglich, diese Standardeinstellung zu ändern, aber die Bindung kann mithilfe von anderen zuvor beschriebenen Techniken angepasst werden. Verwenden Sie zum Anpassen von JSON-Serialisierungsoptionen Code ähnlich dem folgenden:

using Microsoft.AspNetCore.Http.Json;

var builder = WebApplication.CreateBuilder(args);

// Configure JSON options.
builder.Services.Configure<JsonOptions>(options =>
{
    options.SerializerOptions.IncludeFields = true;
});

var app = builder.Build();

app.MapPost("/products", (Product product) => product);

app.Run();

class Product
{
    // These are public fields, not properties.
    public int Id;
    public string? Name;
}

Der vorangehende Code:

  • Konfiguriert die JSON-Standardoptionen sowohl für die Eingabe als auch für die Ausgabe.
  • Gibt folgenden JSON-Code zurück:
    {
      "id": 1,
      "name": "Joe Smith"
    }
    
    Wenn Folgendes gepostet wird:
    {
      "Id": 1,
      "Name": "Joe Smith"
    }
    

Lesen des Anforderungstexts

Lesen Sie den Anforderungstext direkt mithilfe des Parameters HttpContext oder HttpRequest:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/uploadstream", async (IConfiguration config, HttpRequest request) =>
{
    var filePath = Path.Combine(config["StoredFilesPath"], Path.GetRandomFileName());

    await using var writeStream = File.Create(filePath);
    await request.BodyReader.CopyToAsync(writeStream);
});

app.Run();

Der obige Code:

  • Greift mit HttpRequest.BodyReader auf den Anforderungstext zu.
  • Kopiert den Anforderungstext in eine lokale Datei.

Antworten

Routenhandler unterstützen die folgenden Typen von Rückgabewerten:

  1. IResult-basiert: Dies schließt Task<IResult> und ValueTask<IResult> ein.
  2. string: Dies schließt Task<string> und ValueTask<string> ein.
  3. T (ein beliebiger weiterer Typ): Dies schließt Task<T> und ValueTask<T> ein.
Rückgabewert Verhalten Content-Type
IResult Das Framework ruft IResult.ExecuteAsync auf. Richtet sich nach der IResult-Implementierung
string Das Framework schreibt die Zeichenfolge direkt in die Antwort. text/plain
T (beliebiger anderer Typ) Das Framework serialisiert die Antwort im JSON-Format. application/json

Beispielrückgabewerte

Rückgabewerte vom Typ „string“

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

JSON-Rückgabewerte

app.MapGet("/hello", () => new { Message = "Hello World" });

IResult-Rückgabewerte

app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));

Im folgenden Beispiel werden die integrierten Ergebnistypen verwendet, um die Antwort anzupassen:

app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
         await db.Todos.FindAsync(id) 
         is Todo todo
         ? Results.Ok(todo) 
         : Results.NotFound())
   .Produces<Todo>(StatusCodes.Status200OK)
   .Produces(StatusCodes.Status404NotFound);

JSON

app.MapGet("/hello", () => Results.Json(new { Message = "Hello World" }));

Benutzerdefinierter Statuscode

app.MapGet("/405", () => Results.StatusCode(405));

Text

app.MapGet("/text", () => Results.Text("This is some text"));

STREAM

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () => 
{
    var stream = await proxyClient.GetStreamAsync("http://consoto/pokedex.json");
    // Proxy the response as JSON
    return Results.Stream(stream, "application/json");
});

app.Run();

Umleiten

app.MapGet("/old-path", () => Results.Redirect("/new-path"));

Datei

app.MapGet("/download", () => Results.File("myfile.text"));

Integrierte Ergebnisse

Die statische Klasse Microsoft.AspNetCore.Http.Results enthält allgemeine Ergebnishilfen.

Beschreibung Antworttyp Statuscode API
Schreiben einer JSON-Antwort mit erweiterten Optionen Anwendung/json 200 Results.Json
Schreiben einer JSON-Antwort Anwendung/json 200 Results.Ok
Schreiben einer Textantwort text/plain (Standard), konfigurierbar 200 Results.Text
Schreiben der Antwort in Byte application/octet-stream (Standard), konfigurierbar 200 Results.Bytes
Schreiben eines Bytestreams in die Antwort application/octet-stream (Standard), konfigurierbar 200 Results.Stream
Streamen einer Datei in die Antwort zum Herunterladen mit dem content-disposition-Header application/octet-stream (Standard), konfigurierbar 200 Results.File
Festlegen des Statuscodes auf 404 mit optionaler JSON-Antwort 404 Results.NotFound
Festlegen des Statuscodes auf 204 204 Results.NoContent
Festlegen des Statuscodes auf 422 mit optionaler JSON-Antwort 422 Results.UnprocessableEntity
Festlegen des Statuscodes auf 400 mit optionaler JSON-Antwort 400 Results.BadRequest
Festlegen des Statuscodes auf 409 mit optionaler JSON-Antwort 409 Results.Conflict
Schreiben eines JSON-Objekts mit Problemdetails in die Antwort 500 (Standard), konfigurierbar Results.Problem
Schreiben eines JSON-Objekts mit Problemdetails in die Antwort, mit Validierungsfehlern –, konfigurierbar Results.ValidationProblem

Anpassen von Ergebnissen

Anwendungen können Antworten steuern, indem sie einen benutzerdefinierten IResult-Typ implementieren. Der folgende Code ist ein Beispiel für einen HTML-Ergebnistyp:

using System.Net.Mime;
using System.Text;
static class ResultsExtensions
{
    public static IResult Html(this IResultExtensions resultExtensions, string html)
    {
        ArgumentNullException.ThrowIfNull(resultExtensions);

        return new HtmlResult(html);
    }
}

class HtmlResult : IResult
{
    private readonly string _html;

    public HtmlResult(string html)
    {
        _html = html;
    }

    public Task ExecuteAsync(HttpContext httpContext)
    {
        httpContext.Response.ContentType = MediaTypeNames.Text.Html;
        httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(_html);
        return httpContext.Response.WriteAsync(_html);
    }
}

Es wird empfohlen, Microsoft.AspNetCore.Http.IResultExtensions eine Erweiterungsmethode hinzuzufügen, um diese benutzerdefinierten Ergebnisse leichter auffindbar zu machen.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/html", () => Results.Extensions.Html(@$"<!doctype html>
<html>
    <head><title>miniHTML</title></head>
    <body>
        <h1>Hello World</h1>
        <p>The time on the server is {DateTime.Now:O}</p>
    </body>
</html>"));

app.Run();

Autorisierung

Routen können mithilfe von Autorisierungsrichtlinien geschützt werden. Diese können mit dem Attribut [Authorize] oder der Methode RequireAuthorization angegeben werden.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebRPauth.Data;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization(o => o.AddPolicy("AdminsOnly", 
                                  b => b.RequireClaim("admin", "true")));

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

var app = builder.Build();

app.UseAuthorization();

app.MapGet("/auth", [Authorize] () => "This endpoint requires authorization.");
app.MapGet("/", () => "This endpoint doesn't require authorization.");
app.MapGet("/Identity/Account/Login", () => "Sign in page at this endpoint.");

app.Run();

Der vorangehende Code kann mit RequireAuthorization geschrieben werden:

app.MapGet("/auth", () => "This endpoint requires authorization")
   .RequireAuthorization();

Im folgenden Beispiel wird die richtlinienbasierte Autorisierung verwendet:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebRPauth.Data;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization(o => o.AddPolicy("AdminsOnly", 
                                  b => b.RequireClaim("admin", "true")));

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

var app = builder.Build();

app.UseAuthorization();

app.MapGet("/admin", [Authorize("AdminsOnly")] () => 
                             "The /admin endpoint is for admins only.");

app.MapGet("/admin2", () => "The /admin2 endpoint is for admins only.")
   .RequireAuthorization("AdminsOnly");

app.MapGet("/", () => "This endpoint doesn't require authorization.");
app.MapGet("/Identity/Account/Login", () => "Sign in page at this endpoint.");

app.Run();

Nicht authentifizierten Benutzern den Zugriff auf einen Endpunkt gestatten

Durch [AllowAnonymous] können nicht authentifizierte Benutzer auf Endpunkte zugreifen:

app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");


app.MapGet("/login2", () => "This endpoint also for all roles.")
   .AllowAnonymous();

CORS

Routen können mithilfe von CORS-Richtlinien für CORS aktiviert werden. CORS kann über das Attribut [EnableCors] oder mit der Methode RequireCors deklariert werden. In den folgenden Beispielen wird CORS aktiviert:

const string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      builder =>
                      {
                          builder.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

var app = builder.Build();
app.UseCors();

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

app.Run();
using Microsoft.AspNetCore.Cors;

const string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      builder =>
                      {
                          builder.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

var app = builder.Build();
app.UseCors();

app.MapGet("/cors", [EnableCors(MyAllowSpecificOrigins)] () => 
                           "This endpoint allows cross origin requests!");
app.MapGet("/cors2", () => "This endpoint allows cross origin requests!")
                     .RequireCors(MyAllowSpecificOrigins);

app.Run();

Weitere Informationen finden Sie unter Aktivieren ursprungsübergreifender Anforderungen (Cross-Origin Requests, CORS) in ASP.NET Core.

OpenAPI

Eine App kann die OpenAPI-Spezifikation für Routenhandler unter Verwendung von Swashbuckle beschreiben.

Der folgende Code ist eine typische ASP.NET Core-App mit OpenAPI-Unterstützung:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new() { Title = builder.Environment.ApplicationName,
                               Version = "v1" });
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json",
                                    $"{builder.Environment.ApplicationName} v1"));
}

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

app.Run();

Ausschließen der Open API-Beschreibung

Im folgenden Beispiel wird der /skipme-Endpunkt von der Generierung einer OpenAPI-Beschreibung ausgeschlossen:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.MapGet("/swag", () => "Hello Swagger!");
app.MapGet("/skipme", () => "Skipping Swagger.")
                    .ExcludeFromDescription();

app.Run();

Beschreiben von Antworttypen

Im folgenden Beispiel werden die integrierten Ergebnistypen verwendet, um die Antwort anzupassen:

app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
         await db.Todos.FindAsync(id) 
         is Todo todo
         ? Results.Ok(todo) 
         : Results.NotFound())
   .Produces<Todo>(StatusCodes.Status200OK)
   .Produces(StatusCodes.Status404NotFound);

Hinzufügen von Vorgangs-IDs zu Open API

app.MapGet("/todoitems2", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithName("GetToDoItems");

Hinzufügen von Tags zur Open API-Beschreibung

Im folgenden Code wird ein OpenAPI-Gruppierungstag verwendet:

app.MapGet("/todoitems", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithTags("TodoGroup");