Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Note
Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Warning
Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der .NET- und .NET Core-Supportrichtlinie. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Important
Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.
Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Dieses Dokument:
- Stellt eine Kurzübersicht für minimale APIs bereit.
- Ist für erfahrene Entwickler vorgesehen. Eine Einführung finden Sie im Tutorial: Erstellen einer Minimal-API mit ASP.NET Core.
Die minimalen 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.
WebApplication Fügt die folgende Middleware automatisch in Minimal-API-Anwendungen hinzu, abhängig von bestimmten Bedingungen:
-
UseDeveloperExceptionPagewird zuerst hinzugefügt, wennHostingEnvironmentgleich"Development"ist. -
UseRoutingwird zweitens hinzugefügt, wenn der BenutzercodeUseRoutingnoch nicht aufgerufen hat, und wenn Endpunkte konfiguriert sind, z. B.app.MapGet. -
UseEndpointswird am Ende der Middlewarepipeline hinzugefügt, wenn Endpunkte konfiguriert sind. -
UseAuthenticationwird unmittelbar nachUseRoutinghinzugefügt, wenn der BenutzercodeUseAuthenticationnoch nicht aufgerufen hat und wennIAuthenticationSchemeProviderim Dienstanbieter erkannt werden kann.IAuthenticationSchemeProviderwird standardmäßig hinzugefügt, wenn die VerwendungAddAuthenticationvon Diensten mitIServiceProviderIsServiceerkannt wird. -
UseAuthorizationwird als Nächstes hinzugefügt, wenn der BenutzercodeUseAuthorizationnoch nicht aufgerufen hat und wennIAuthorizationHandlerProviderim Dienstanbieter erkannt werden kann.IAuthorizationHandlerProviderwird standardmäßig hinzugefügt, wenn die VerwendungAddAuthorizationvon Diensten mitIServiceProviderIsServiceerkannt wird. - Benutzerkonfigurierte Middleware und Endpunkte werden zwischen
UseRoutingundUseEndpointshinzugefügt.
Nachfolgend sehen Sie den Code, der von der automatischen Middleware erzeugt wird, die zur App hinzugefügt wird:
if (isDevelopment)
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
if (isAuthenticationConfigured)
{
app.UseAuthentication();
}
if (isAuthorizationConfigured)
{
app.UseAuthorization();
}
// user middleware/endpoints
app.CustomMiddleware(...);
app.MapGet("/", () => "hello world");
// end user middleware/endpoints
app.UseEndpoints(e => {});
In einigen Fällen eignet sich die standardmäßige Middleware-Konfiguration nicht für die App und muss geändert werden. Beispielsweise sollte UseCors vor UseAuthentication und UseAuthorization aufgerufen werden. Die App muss UseAuthentication und UseAuthorization aufrufen, wenn UseCors aufgerufen wird:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Wenn Middleware ausgeführt werden muss, bevor der Routenabgleich erfolgt, muss UseRouting aufgerufen werden, und die Middleware muss vor dem Aufruf von UseRouting platziert werden.
UseEndpoints ist in diesem Fall nicht erforderlich, da es wie zuvor beschrieben automatisch hinzugefügt wird:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Beim Hinzufügen einer Terminal-Middleware:
- Die Middleware muss nach
UseEndpointshinzugefügt werden. - Die App muss
UseRoutingundUseEndpointsaufrufen, damit die Terminal-Middleware an der richtigen Position platziert werden kann.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Terminal-Middleware ist Middleware, die ausgeführt wird, wenn kein Endpunkt die Anforderung verarbeitet.
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. Visual Studio gibt einen Fehler zurück, da der in Properties/launchSettings.jsonangegebene Port erwartet wird, die App jedoch den von app.Run("http://localhost:3000") angegebenen Port verwendet. 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
Überwachen aller Schnittstellen mithilfe von ASPNETCORE_HTTPS_PORTS
In den vorherigen Beispielen können ASPNETCORE_HTTPS_PORTS und ASPNETCORE_HTTP_PORTS verwendet werden.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Weitere Informationen finden Sie unter Konfigurieren von Endpunkten für den Kestrel-Webserver von ASP.NET Core.
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 ASP.NET Core-Laufzeitumgebungen
Configuration
Der folgende Code liest Informationen aus dem Konfigurationssystem:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Weitere Informationen finden Sie unter Konfiguration in ASP.NET Core.
Logging
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 Logging in .NET 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();
Der folgende Code zeigt, wie Sie mit dem [FromKeyedServices]-Attribut auf Schlüssel aus dem DI-Container (Dependency Injection, Abhängigkeitsinjektion) zugreifen können:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
var app = builder.Build();
app.MapGet("/big", ([FromKeyedServices("big")] ICache bigCache) => bigCache.Get("date"));
app.MapGet("/small", ([FromKeyedServices("small")] ICache smallCache) => smallCache.Get("date"));
app.Run();
public interface ICache
{
object Get(string key);
}
public class BigCache : ICache
{
public object Get(string key) => $"Resolving {key} from big cache.";
}
public class SmallCache : ICache
{
public object Get(string key) => $"Resolving {key} from small cache.";
}
Weitere Informationen zu DI 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
{
Args = args,
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 |
| Inhaltsstamm | 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.
Konfiguration lesen
Standardmäßig liest die WebApplicationBuilder die Konfiguration aus mehreren Quellen, darunter:
-
appSettings.jsonundappSettings.{environment}.json - Umgebungsvariablen
- Die Befehlszeile
Eine vollständige Liste der gelesenen Konfigurationsquellen finden Sie unter Standardkonfiguration in Konfiguration in ASP.NET Core.
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);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Lesen der Umgebung
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();
Dienste hinzufügen
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. Webstamm ist der Ort, an dem die Static File Middleware nach statischen Dateien sucht. 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 sind einige der Middleware aufgeführt, die häufig mit minimalen APIs verwendet werden.
| Middleware | Description | API |
|---|---|---|
| Authentication | Bietet Unterstützung für Authentifizierungen. | UseAuthentication |
| Authorization | 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 Kopfzeilen | 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 |
| Anforderungstimeouts | Bietet Unterstützung beim Konfigurieren von Anforderungstimeouts und globaler Standardwerte sowie bei der Konfiguration pro Endpunkt. | UseRequestTimeouts |
| W3C-Anforderungsprotokollierung | Bietet Unterstützung für die Protokollierung von HTTP-Anforderungen und -Antworten im W3C-Format. | UseW3CLogging |
| Zwischenspeicherung von Antworten | Bietet Unterstützung für das Zwischenspeichern von Antworten. | UseResponseCaching |
| Reaktionskomprimierung | Bietet Unterstützung für das Komprimieren von Antworten. | UseResponseCompression |
| Session | 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 |
In den folgenden Abschnitten werden Routing, Parameterbindung und Antworten behandelt.
Routing
Eine konfigurierte Unterstützung und dabei handelt es sich um eine camel-cased HTTP-Methode wie WebApplication, Map{Verb}, , MapMethodsoder {Verb}:GetPostPutDelete
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();
Die an diese Methoden übergebenen Delegate-Argumente werden als „Routenhandler“ bezeichnet.
Routenhandler
Routenhandler sind Methoden, die ausgeführt werden, wenn die Route übereinstimmt. Als Routenhandler kann ein Lambdaausdruck, eine lokale Funktion, eine Instanzmethode oder eine statische Methode verwendet werden. Routenhandler können synchron oder asynchron sein.
Lambda-Ausdruck
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();
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";
}
}
Static-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";
}
}
Endpunkt außerhalb von Program.cs definiert
Minimale APIs müssen sich nicht in Program.cs befinden.
Program.cs
using MinAPISeparateFile;
var builder = WebApplication.CreateSlimBuilder(args);
var app = builder.Build();
TodoEndpoints.Map(app);
app.Run();
TodoEndpoints.cs
namespace MinAPISeparateFile;
public static class TodoEndpoints
{
public static void Map(WebApplication app)
{
app.MapGet("/", async context =>
{
// Get all todo items
await context.Response.WriteAsJsonAsync(new { Message = "All todo items" });
});
app.MapGet("/{id}", async context =>
{
// Get one todo item
await context.Response.WriteAsJsonAsync(new { Message = "One todo item" });
});
}
}
Weitere Informationen dazu finden Sie auch unter Routengruppen weiter unten in diesem Artikel.
Benannte Endpunkte und Linkgenerierung
Endpunkte können Namen erhalten, um URLs für den jeweiligen Endpunkt zu generieren. Durch die Verwendung eines benannten Endpunkts 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.
HINWEIS: Für Endpunktnamen muss Groß-/Kleinschreibung beachtet werden.
Endpunktnamen:
- Dieser muss global eindeutig sein.
- Werden als OpenAPI-Vorgangs-ID verwendet, wenn die OpenAPI-Unterstützung aktiviert ist. Weitere Informationen finden Sie unter 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. Bei einer Anforderung an eine Route mit Parametern, deren Erfassung deklariert wurde, werden die Parameter geparst 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.
Routengruppen
Die MapGroup-Erweiterungsmethode hilft, Gruppen von Endpunkten mit einem gemeinsamen Präfix zu organisieren. Sie reduziert sich wiederholenden Code und ermöglicht die benutzerdefinierte Anpassung ganzer Gruppen von Endpunkten mit einem einzigen Aufruf von Methoden wie RequireAuthorization und WithMetadata,die Endpunktmetadaten hinzufügen.
Der folgende Code erstellt beispielsweise zwei ähnliche Endpunktgruppen:
app.MapGroup("/public/todos")
.MapTodosApi()
.WithTags("Public");
app.MapGroup("/private/todos")
.MapTodosApi()
.WithTags("Private")
.AddEndpointFilterFactory(QueryPrivateTodos)
.RequireAuthorization();
EndpointFilterDelegate QueryPrivateTodos(EndpointFilterFactoryContext factoryContext, EndpointFilterDelegate next)
{
var dbContextIndex = -1;
foreach (var argument in factoryContext.MethodInfo.GetParameters())
{
if (argument.ParameterType == typeof(TodoDb))
{
dbContextIndex = argument.Position;
break;
}
}
// Skip filter if the method doesn't have a TodoDb parameter.
if (dbContextIndex < 0)
{
return next;
}
return async invocationContext =>
{
var dbContext = invocationContext.GetArgument<TodoDb>(dbContextIndex);
dbContext.IsPrivate = true;
try
{
return await next(invocationContext);
}
finally
{
// This should only be relevant if you're pooling or otherwise reusing the DbContext instance.
dbContext.IsPrivate = false;
}
};
}
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
group.MapGet("/", GetAllTodos);
group.MapGet("/{id}", GetTodo);
group.MapPost("/", CreateTodo);
group.MapPut("/{id}", UpdateTodo);
group.MapDelete("/{id}", DeleteTodo);
return group;
}
In diesem Szenario können Sie eine relative Adresse für den Location-Header im 201 Created-Ergebnis verwenden:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Die erste Gruppe von Endpunkten entspricht nur Anforderungen mit dem Präfix /public/todos und ist ohne Authentifizierung zugänglich. Die zweite Gruppe von Endpunkten entspricht nur Anforderungen mit dem Präfix /private/todos und erfordert Authentifizierung.
Die QueryPrivateTodos-Endpunktfilterfactory ist eine lokale Funktion, die die TodoDb-Parameter des Routenhandlers so ändert, dass Zugriff auf private Aufgabendaten zulässig ist und diese gespeichert werden können.
Routengruppen unterstützen auch geschachtelte Gruppen und komplexe Präfixmuster mit Routenparametern und -einschränkungen. Im folgenden Beispiel kann der der user-Gruppe zugeordnete Routenhandler die Routenparameter {org} und {group} erfassen, die in den Präfixen der äußeren Gruppe definiert sind.
Das Präfix kann auch leer sein. Dies kann hilfreich sein, um Endpunktmetadaten oder Filter einer Gruppe von Endpunkten hinzuzufügen, ohne das Routenmuster zu ändern.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Das Hinzufügen von Filtern oder Metadaten zu einer Gruppe verhält sich genauso wie das individuelle Hinzufügen zu jedem Endpunkt, bevor zusätzliche Filter oder Metadaten hinzugefügt werden, die einer inneren Gruppe oder einem bestimmten Endpunkt hinzugefügt wurden.
var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");
inner.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/inner group filter");
return next(context);
});
outer.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/outer group filter");
return next(context);
});
inner.MapGet("/", () => "Hi!").AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("MapGet filter");
return next(context);
});
Im Beispiel oben protokolliert der äußere Filter die eingehende Anforderung vor dem inneren Filter, obwohl er als zweiter hinzugefügt wurde. Da die Filter auf verschiedene Gruppen angewendet wurden, spielt die Reihenfolge, in der sie relativ zueinander hinzugefügt wurden, keine Rolle. Die Reihenfolge, in der Filter hinzugefügt werden, spielt eine Rolle, wenn sie auf dieselbe Gruppe oder einen bestimmten Endpunkt angewendet werden.
Eine Anforderung an /outer/inner/ protokolliert Folgendes:
/outer group filter
/inner group filter
MapGet filter
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)
- Formularwerte
- Von der Abhängigkeitsinjektion bereitgestellte Dienste
- Custom
Der folgende GET-Routenhandler verwendet 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 { }
Wichtige Merkmale der Parameterbindung
-
Explizite Bindung: Verwenden Sie Attribute wie
[FromRoute], ,[FromQuery][FromHeader],[FromBody],[FromForm]und[FromServices]geben Sie explizit Bindungsquellen an. -
Formularbindung: Binden Sie Formularwerte mithilfe des
[FromForm]Attributs, einschließlich Unterstützung fürIFormFileundIFormFileCollectionfür Dateiuploads. - Komplexe Typen: Binden an Kollektionen und komplexe Typen von Formularen, Abfragezeichenfolgen und Kopfzeilen.
-
Benutzerdefinierte Bindung: Implementieren Sie die Logik für benutzerdefinierte Bindungen mithilfe von
TryParse,BindAsyncoder derIBindableFromHttpContext<T>Schnittstelle. - Optionale Parameter: Unterstützen Nullwerte und Standardwerte für optionale Parameter.
- Abhängigkeitseinfügung: Parameter werden automatisch von Diensten gebunden, die im DI-Container registriert sind.
-
Spezielle Typen: Automatische Bindung für
HttpContext, ,HttpRequestHttpResponse,CancellationToken,ClaimsPrincipal, , undStreamPipeReader.
Weitere Informationen: Ausführliche Informationen zur Parameterbindung, einschließlich erweiterter Szenarien, Validierung, Bindungsrangfolge und Problembehandlung, finden Sie unter Parameterbindung in Minimal-API-Anwendungen.
Json+PipeReader-Deserialisierung in minimalen APIs
Ab .NET 10 verwenden die folgenden Funktionsbereiche von ASP.NET Core Überladungen von JsonSerializer.DeserializeAsync basierend auf PipeReader anstelle von Stream.
- Minimale APIs (Parameterbindung, Leseanforderungstext)
- MVC (Eingabeformatierer, Modell)
- Die HttpRequestJsonExtensions Erweiterungsmethoden zum Lesen des Anforderungstexts als JSON.
Für die meisten Anwendungen bietet ein Übergang von Stream zu PipeReader eine bessere Leistung, ohne dass Änderungen am Anwendungscode erforderlich sind. Wenn Ihre Anwendung jedoch einen benutzerdefinierten Konverter hat, kann der Konverter Utf8JsonReader.HasValueSequence möglicherweise nicht richtig verarbeiten. Falls nicht, könnten beim Deserialisieren Fehler wie ArgumentOutOfRangeException oder fehlende Daten auftreten. Sie haben die folgenden Optionen, um den Konverter ohne PipeReader-bezogene Fehler zu verwenden.
Option 1: Temporäre Problemumgehung
Die schnelle Problemumgehung besteht darin, zu Stream ohne PipeReader-Unterstützung zurückzukehren. Um diese Option zu implementieren, legen Sie den AppContext-Switch "Microsoft.AspNetCore.UseStreamBasedJsonParsing" auf "true" fest. Wir empfehlen, dies nur als vorübergehende Lösung zu verwenden und Ihren Konverter so schnell wie möglich zu aktualisieren, um HasValueSequence zu unterstützen. Die Option kann in .NET 11 entfernt werden. Der einzige Zweck bestand darin, Entwicklern Zeit zu geben, ihre Konverter zu aktualisieren.
Option 2: Schnelle Lösung für JsonConverter Implementierungen
Für diese Korrektur weisen Sie ein Array aus der ReadOnlySequence zu. In diesem Beispiel wird gezeigt, wie der Code aussehen würde:
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
// previous code
}
Option 3: Eine kompliziertere, aber bessere Fehlerkorrektur
Dieser Fix umfasst das Einrichten eines separaten Codepfads für die ReadOnlySequence Behandlung:
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.HasValueSequence)
{
reader.ValueSequence;
// ReadOnlySequence optimized path
}
else
{
reader.ValueSpan;
// ReadOnlySpan optimized path
}
}
Weitere Informationen finden Sie unter
Überprüfungsunterstützung in minimalen APIs
Durch aktivieren der Überprüfung kann die ASP.NET Core-Laufzeit Validierungen ausführen, die für Folgendes definiert sind:
- Query
- Header
- Anfragekörper
Validierungen werden mithilfe von Attributen im DataAnnotations Namespace definiert.
Wenn ein Parameter für einen minimalen API-Endpunkt eine Klasse oder ein Datensatztyp ist, werden Validierungsattribute automatisch angewendet. Beispiel:
public record Product(
[Required] string Name,
[Range(1, 1000)] int Quantity);
Entwickler passen das Verhalten des Validierungssystems an:
- Erstellen von benutzerdefinierten
[Validation]Attributimplementierungen . - Implementieren der
IValidatableObjectSchnittstelle für komplexe Validierungslogik.
Wenn die Überprüfung fehlschlägt, gibt die Laufzeit eine Antwort von 400 – Ungültige Anforderung mit Details zu den Überprüfungsfehlern zurück.
Aktivieren der integrierten Überprüfungsunterstützung für minimale APIs
Aktivieren Sie die integrierte Überprüfungsunterstützung für minimale APIs, indem Sie die AddValidation Erweiterungsmethode aufrufen, um die erforderlichen Dienste im Dienstcontainer für Ihre Anwendung zu registrieren:
builder.Services.AddValidation();
Die Implementierung ermittelt automatisch Typen, die in minimalen API-Handlern oder als Basistypen definiert werden, die in minimalen API-Handlern definiert sind. Ein Endpunktfilter führt eine Überprüfung für diese Typen durch und wird für jeden Endpunkt hinzugefügt.
Die Überprüfung kann für bestimmte Endpunkte mithilfe der DisableValidation Erweiterungsmethode deaktiviert werden, wie im folgenden Beispiel gezeigt:
app.MapPost("/products",
([EvenNumber(ErrorMessage = "Product ID must be even")] int productId, [Required] string name)
=> TypedResults.Ok(productId))
.DisableValidation();
Anpassen von Überprüfungsfehlerantworten mithilfe von IProblemDetailsService
Passen Sie Fehlerantworten aus minimaler API-Validierungslogik mit einer IProblemDetailsService Implementierung an. Registrieren Sie diesen Dienst in der Dienstsammlung Ihrer Anwendung, um konsistentere und benutzerspezifische Fehlerantworten zu ermöglichen. Unterstützung für die minimale API-Überprüfung wurde in ASP.NET Core in .NET 10 eingeführt.
So implementieren Sie benutzerdefinierte Überprüfungsfehlerantworten:
- Implementieren IProblemDetailsService oder Verwenden der Standardimplementierung
- Registrieren des Diensts im DI-Container
- Das Überprüfungssystem verwendet automatisch den registrierten Dienst zum Formatieren von Überprüfungsfehlerantworten.
Weitere Informationen zum Anpassen von Überprüfungsfehlerantworten mit IProblemDetailsService finden Sie unter Erstellen von Antworten in minimalen API-Anwendungen.
Responses
Routenhandler unterstützen die folgenden Typen von Rückgabewerten:
-
IResult-basiert: Dies schließtTask<IResult>undValueTask<IResult>ein. -
string: Dies schließtTask<string>undValueTask<string>ein. -
T(ein beliebiger weiterer Typ): Dies schließtTask<T>undValueTask<T>ein.
| Rückgabewert | Behavior | 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 JSON-serialisiert die Antwort. | application/json |
Ausführlichere Anleitungen zu Rückgabewerten des Routenhandlers finden Sie unter Erstellen von Antworten in Minimal-API-Anwendungen.
Beispielrückgabewerte
Rückgabewerte vom Typ „string“
app.MapGet("/hello", () => "Hello World");
JSON-Rückgabewerte
app.MapGet("/hello", () => new { Message = "Hello World" });
Geben Sie Typisierte Ergebnisse zurück
Der folgende Code gibt TypedResults zurück:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Die Rückgabe von TypedResults wird der Rückgabe von Results vorgezogen. Weitere Informationen finden Sie unter Vergleich von TypedResults und Results.
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 proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
Weitere Beispiele finden Sie unter Erstellen von Antworten in Minimal-API-Anwendungen.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Integrierte Ergebnisse
Die statischen Klassen Results und TypedResults enthalten allgemeine Ergebnishilfen. Die Rückgabe von TypedResults wird der Rückgabe von Results vorgezogen. Weitere Informationen finden Sie unter Vergleich von TypedResults und Results.
Ändern von Kopfzeilen
Verwenden Sie das HttpResponse-Objekt, um Antwortheader zu ändern:
app.MapGet("/", (HttpContext context) => {
// Set a custom header
context.Response.Headers["X-Custom-Header"] = "CustomValue";
// Set a known header
context.Response.Headers.CacheControl = $"public,max-age=3600";
return "Hello World";
});
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();
Eingegebene Ergebnisse
Die IResult Schnittstelle kann Werte darstellen, die von minimalen APIs zurückgegeben werden, die nicht die implizite Unterstützung für JSON-Serialisierung des zurückgegebenen Objekts in die HTTP-Antwort verwenden. Die statische Results-Klasse wird verwendet, um unterschiedliche IResult-Objekte zu erstellen, die verschiedene Arten von Antworten darstellen. Beispiel: Festlegen des Antwortstatuscodes oder Umleitung an eine andere URL.
Die IResult implementierenden Typen sind öffentlich, sodass Typassertionen beim Testen zugelassen werden. Beispiel:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Sie können die Rückgabetypen der entsprechenden Methoden in der statischen TypedResults-Klasse anzeigen, um den richtigen öffentlichen IResult-Typ zu finden, in den Werte umgewandelt erden sollen.
Weitere Beispiele finden Sie unter Erstellen von Antworten in Minimal-API-Anwendungen.
Filters
Weitere Informationen finden Sie unter Filter in Minimal-API-Apps.
Authorization
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.
ValidateScopes und ValidateOnBuild
ValidateScopes und ValidateOnBuild sind standardmäßig in der Entwicklungsumgebung aktiviert, aber in anderen Umgebungen deaktiviert.
Wenn ValidateOnBuildtrue ist, überprüft der DI-Container die Dienstkonfiguration zur Buildzeit. Wenn die Dienstkonfiguration ungültig ist, schlägt der Build beim Starten der App und nicht zur Laufzeit fehl, wenn der Dienst angefordert wird.
Wenn ValidateScopestrue ist, überprüft der DI-Container, dass ein bereichsbezogener Dienst nicht aus dem Stammbereich aufgelöst wird. Das Auflösen eines bereichsbezogenen Diensts aus dem Stammbereich kann zu einem Arbeitsspeicherverlust führen, da der Dienst länger als der Anforderungsbereich im Arbeitsspeicher aufbewahrt wird.
ValidateScopes und ValidateOnBuild sind aus Leistungsgründen standardmäßig in Nichtentwicklungsmodi falsch.
Der folgende Code zeigt, dass ValidateScopes im Entwicklungsmodus standardmäßig aktiviert, aber im Releasemodus deaktiviert ist:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<MyScopedService>();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
Console.WriteLine("Development environment");
}
else
{
Console.WriteLine("Release environment");
}
app.MapGet("/", context =>
{
// Intentionally getting service provider from app, not from the request
// This causes an exception from attempting to resolve a scoped service
// outside of a scope.
// Throws System.InvalidOperationException:
// 'Cannot resolve scoped service 'MyScopedService' from root provider.'
var service = app.Services.GetRequiredService<MyScopedService>();
return context.Response.WriteAsync("Service resolved");
});
app.Run();
public class MyScopedService { }
Der folgende Code zeigt, dass ValidateOnBuild im Entwicklungsmodus standardmäßig aktiviert, aber im Releasemodus deaktiviert ist:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<MyScopedService>();
builder.Services.AddScoped<AnotherService>();
// System.AggregateException: 'Some services are not able to be constructed (Error
// while validating the service descriptor 'ServiceType: AnotherService Lifetime:
// Scoped ImplementationType: AnotherService': Unable to resolve service for type
// 'BrokenService' while attempting to activate 'AnotherService'.)'
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
Console.WriteLine("Development environment");
}
else
{
Console.WriteLine("Release environment");
}
app.MapGet("/", context =>
{
var service = context.RequestServices.GetRequiredService<MyScopedService>();
return context.Response.WriteAsync("Service resolved correctly!");
});
app.Run();
public class MyScopedService { }
public class AnotherService
{
public AnotherService(BrokenService brokenService) { }
}
public class BrokenService { }
Der folgende Code deaktiviert ValidateScopes und ValidateOnBuild in Development:
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine("Development environment");
// Doesn't detect the validation problems because ValidateScopes is false.
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = false;
options.ValidateOnBuild = false;
});
}
Siehe auch
- Minimal-APIs: Kurzreferenz
- OpenAPI-Dokumente generieren
- Erstellen von Antworten in Minimal-API-Anwendungen
- Filter in Minimal-API-Apps
- Behandeln von Fehlern in ASP.NET Core-APIs
- Authentifizierung und Autorisierung in Minimal-API-Apps
- Testen von Minimal-API-Apps
- Kurzschlussrouting
- Identity-API-Endpunkte
- Unterstützung von Containern für abhängigkeitsbasierte Dienste
- Ein Blick hinter die Kulissen von minimalen API-Endpunkten
- Organisieren von Minimal-APIs für ASP.NET Core
- Fluent-Validierungsdiskussion auf GitHub
Dieses Dokument:
- Stellt eine Kurzreferenz für Minimal-APIs bereit.
- Ist für erfahrene Entwickler vorgesehen. Eine Einführung finden Sie im Tutorial: Erstellen einer Minimal-API mit ASP.NET Core.
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.
WebApplication Fügt die folgende Middleware automatisch in Minimal-API-Anwendungen hinzu, abhängig von bestimmten Bedingungen:
-
UseDeveloperExceptionPagewird zuerst hinzugefügt, wennHostingEnvironmentgleich"Development"ist. -
UseRoutingwird zweitens hinzugefügt, wenn der BenutzercodeUseRoutingnoch nicht aufgerufen hat, und wenn Endpunkte konfiguriert sind, z. B.app.MapGet. -
UseEndpointswird am Ende der Middlewarepipeline hinzugefügt, wenn Endpunkte konfiguriert sind. -
UseAuthenticationwird unmittelbar nachUseRoutinghinzugefügt, wenn der BenutzercodeUseAuthenticationnoch nicht aufgerufen hat und wennIAuthenticationSchemeProviderim Dienstanbieter erkannt werden kann.IAuthenticationSchemeProviderwird standardmäßig hinzugefügt, wenn die VerwendungAddAuthenticationvon Diensten mitIServiceProviderIsServiceerkannt wird. -
UseAuthorizationwird als Nächstes hinzugefügt, wenn der BenutzercodeUseAuthorizationnoch nicht aufgerufen hat und wennIAuthorizationHandlerProviderim Dienstanbieter erkannt werden kann.IAuthorizationHandlerProviderwird standardmäßig hinzugefügt, wenn die VerwendungAddAuthorizationvon Diensten mitIServiceProviderIsServiceerkannt wird. - Benutzerkonfigurierte Middleware und Endpunkte werden zwischen
UseRoutingundUseEndpointshinzugefügt.
Nachfolgend sehen Sie den Code, der von der automatischen Middleware erzeugt wird, die zur App hinzugefügt wird:
if (isDevelopment)
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
if (isAuthenticationConfigured)
{
app.UseAuthentication();
}
if (isAuthorizationConfigured)
{
app.UseAuthorization();
}
// user middleware/endpoints
app.CustomMiddleware(...);
app.MapGet("/", () => "hello world");
// end user middleware/endpoints
app.UseEndpoints(e => {});
In einigen Fällen eignet sich die standardmäßige Middleware-Konfiguration nicht für die App und muss geändert werden. Beispielsweise sollte UseCors vor UseAuthentication und UseAuthorization aufgerufen werden. Die App muss UseAuthentication und UseAuthorization aufrufen, wenn UseCors aufgerufen wird:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Wenn Middleware ausgeführt werden muss, bevor der Routenabgleich erfolgt, muss UseRouting aufgerufen werden, und die Middleware muss vor dem Aufruf von UseRouting platziert werden.
UseEndpoints ist in diesem Fall nicht erforderlich, da es wie zuvor beschrieben automatisch hinzugefügt wird:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Beim Hinzufügen einer Terminal-Middleware:
- Die Middleware muss nach
UseEndpointshinzugefügt werden. - Die App muss
UseRoutingundUseEndpointsaufrufen, damit die Terminal-Middleware an der richtigen Position platziert werden kann.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Terminal-Middleware ist Middleware, die ausgeführt wird, wenn kein Endpunkt die Anforderung verarbeitet.
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. Visual Studio gibt einen Fehler zurück, da der in Properties/launchSettings.jsonangegebene Port erwartet wird, die App jedoch den von app.Run("http://localhost:3000") angegebenen Port verwendet. 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
Überwachen aller Schnittstellen mithilfe von ASPNETCORE_HTTPS_PORTS
In den vorherigen Beispielen können ASPNETCORE_HTTPS_PORTS und ASPNETCORE_HTTP_PORTS verwendet werden.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Weitere Informationen finden Sie unter Konfigurieren von Endpunkten für den Kestrel-Webserver von ASP.NET Core.
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 ASP.NET Core-Laufzeitumgebungen
Configuration
Der folgende Code liest Informationen aus dem Konfigurationssystem:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Weitere Informationen finden Sie unter Konfiguration in ASP.NET Core.
Logging
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 Logging in .NET 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();
Der folgende Code zeigt, wie Sie mit dem [FromKeyedServices]-Attribut auf Schlüssel aus dem DI-Container (Dependency Injection, Abhängigkeitsinjektion) zugreifen können:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
var app = builder.Build();
app.MapGet("/big", ([FromKeyedServices("big")] ICache bigCache) => bigCache.Get("date"));
app.MapGet("/small", ([FromKeyedServices("small")] ICache smallCache) => smallCache.Get("date"));
app.Run();
public interface ICache
{
object Get(string key);
}
public class BigCache : ICache
{
public object Get(string key) => $"Resolving {key} from big cache.";
}
public class SmallCache : ICache
{
public object Get(string key) => $"Resolving {key} from small cache.";
}
Weitere Informationen zu DI 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
{
Args = args,
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 |
| Inhaltsstamm | 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.
Konfiguration lesen
Standardmäßig liest die WebApplicationBuilder die Konfiguration aus mehreren Quellen, darunter:
-
appSettings.jsonundappSettings.{environment}.json - Umgebungsvariablen
- Die Befehlszeile
Eine vollständige Liste der gelesenen Konfigurationsquellen finden Sie unter Standardkonfiguration in Konfiguration in ASP.NET Core.
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);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Lesen der Umgebung
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();
Dienste hinzufügen
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. Webstamm ist der Ort, an dem die Static File Middleware nach statischen Dateien sucht. 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 | Description | API |
|---|---|---|
| Authentication | Bietet Unterstützung für Authentifizierungen. | UseAuthentication |
| Authorization | 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 Kopfzeilen | 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 |
| Anforderungstimeouts | Bietet Unterstützung beim Konfigurieren von Anforderungstimeouts und globaler Standardwerte sowie bei der Konfiguration pro Endpunkt. | UseRequestTimeouts |
| W3C-Anforderungsprotokollierung | Bietet Unterstützung für die Protokollierung von HTTP-Anforderungen und -Antworten im W3C-Format. | UseW3CLogging |
| Zwischenspeicherung von Antworten | Bietet Unterstützung für das Zwischenspeichern von Antworten. | UseResponseCaching |
| Reaktionskomprimierung | Bietet Unterstützung für das Komprimieren von Antworten. | UseResponseCompression |
| Session | 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 |
In den folgenden Abschnitten werden Routing, Parameterbindung und Antworten behandelt.
Routing
Eine konfigurierte WebApplication unterstützt Map{Verb} und MapMethods, wobei {Verb} eine HTTP-Methode mit Camel-Case-Schreibweise wie Get, Post, Put oder Delete ist:
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();
Die an diese Methoden übergebenen Delegate-Argumente werden als „Routenhandler“ bezeichnet.
Routenhandler
Routenhandler sind Methoden, die ausgeführt werden, wenn die Route übereinstimmt. Als Routenhandler kann ein Lambdaausdruck, eine lokale Funktion, eine Instanzmethode oder eine statische Methode verwendet werden. Routenhandler können synchron oder asynchron sein.
Lambda-Ausdruck
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();
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";
}
}
Static-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";
}
}
Endpunkt außerhalb von Program.cs definiert
Minimale APIs müssen sich nicht in Program.cs befinden.
Program.cs
using MinAPISeparateFile;
var builder = WebApplication.CreateSlimBuilder(args);
var app = builder.Build();
TodoEndpoints.Map(app);
app.Run();
TodoEndpoints.cs
namespace MinAPISeparateFile;
public static class TodoEndpoints
{
public static void Map(WebApplication app)
{
app.MapGet("/", async context =>
{
// Get all todo items
await context.Response.WriteAsJsonAsync(new { Message = "All todo items" });
});
app.MapGet("/{id}", async context =>
{
// Get one todo item
await context.Response.WriteAsJsonAsync(new { Message = "One todo item" });
});
}
}
Weitere Informationen dazu finden Sie auch unter Routengruppen weiter unten in diesem Artikel.
Benannte Endpunkte und Linkgenerierung
Endpunkte können Namen erhalten, um URLs für den jeweiligen Endpunkt zu generieren. Durch die Verwendung eines benannten Endpunkts 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.
HINWEIS: Für Endpunktnamen muss Groß-/Kleinschreibung beachtet werden.
Endpunktnamen:
- Dieser muss global eindeutig sein.
- Werden als OpenAPI-Vorgangs-ID verwendet, wenn die OpenAPI-Unterstützung aktiviert ist. Weitere Informationen finden Sie unter 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. Bei einer Anforderung an eine Route mit Parametern, deren Erfassung deklariert wurde, werden die Parameter geparst 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.
Routengruppen
Die MapGroup-Erweiterungsmethode hilft, Gruppen von Endpunkten mit einem gemeinsamen Präfix zu organisieren. Sie reduziert sich wiederholenden Code und ermöglicht die benutzerdefinierte Anpassung ganzer Gruppen von Endpunkten mit einem einzigen Aufruf von Methoden wie RequireAuthorization und WithMetadata,die Endpunktmetadaten hinzufügen.
Der folgende Code erstellt beispielsweise zwei ähnliche Endpunktgruppen:
app.MapGroup("/public/todos")
.MapTodosApi()
.WithTags("Public");
app.MapGroup("/private/todos")
.MapTodosApi()
.WithTags("Private")
.AddEndpointFilterFactory(QueryPrivateTodos)
.RequireAuthorization();
EndpointFilterDelegate QueryPrivateTodos(EndpointFilterFactoryContext factoryContext, EndpointFilterDelegate next)
{
var dbContextIndex = -1;
foreach (var argument in factoryContext.MethodInfo.GetParameters())
{
if (argument.ParameterType == typeof(TodoDb))
{
dbContextIndex = argument.Position;
break;
}
}
// Skip filter if the method doesn't have a TodoDb parameter.
if (dbContextIndex < 0)
{
return next;
}
return async invocationContext =>
{
var dbContext = invocationContext.GetArgument<TodoDb>(dbContextIndex);
dbContext.IsPrivate = true;
try
{
return await next(invocationContext);
}
finally
{
// This should only be relevant if you're pooling or otherwise reusing the DbContext instance.
dbContext.IsPrivate = false;
}
};
}
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
group.MapGet("/", GetAllTodos);
group.MapGet("/{id}", GetTodo);
group.MapPost("/", CreateTodo);
group.MapPut("/{id}", UpdateTodo);
group.MapDelete("/{id}", DeleteTodo);
return group;
}
In diesem Szenario können Sie eine relative Adresse für den Location-Header im 201 Created-Ergebnis verwenden:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Die erste Gruppe von Endpunkten entspricht nur Anforderungen mit dem Präfix /public/todos und ist ohne Authentifizierung zugänglich. Die zweite Gruppe von Endpunkten entspricht nur Anforderungen mit dem Präfix /private/todos und erfordert Authentifizierung.
Die QueryPrivateTodos-Endpunktfilterfactory ist eine lokale Funktion, die die TodoDb-Parameter des Routenhandlers so ändert, dass Zugriff auf private Aufgabendaten zulässig ist und diese gespeichert werden können.
Routengruppen unterstützen auch geschachtelte Gruppen und komplexe Präfixmuster mit Routenparametern und -einschränkungen. Im folgenden Beispiel kann der der user-Gruppe zugeordnete Routenhandler die Routenparameter {org} und {group} erfassen, die in den Präfixen der äußeren Gruppe definiert sind.
Das Präfix kann auch leer sein. Dies kann hilfreich sein, um Endpunktmetadaten oder Filter einer Gruppe von Endpunkten hinzuzufügen, ohne das Routenmuster zu ändern.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Das Hinzufügen von Filtern oder Metadaten zu einer Gruppe verhält sich genauso wie das individuelle Hinzufügen zu jedem Endpunkt, bevor zusätzliche Filter oder Metadaten hinzugefügt werden, die einer inneren Gruppe oder einem bestimmten Endpunkt hinzugefügt wurden.
var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");
inner.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/inner group filter");
return next(context);
});
outer.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/outer group filter");
return next(context);
});
inner.MapGet("/", () => "Hi!").AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("MapGet filter");
return next(context);
});
Im Beispiel oben protokolliert der äußere Filter die eingehende Anforderung vor dem inneren Filter, obwohl er als zweiter hinzugefügt wurde. Da die Filter auf verschiedene Gruppen angewendet wurden, spielt die Reihenfolge, in der sie relativ zueinander hinzugefügt wurden, keine Rolle. Die Reihenfolge, in der Filter hinzugefügt werden, spielt eine Rolle, wenn sie auf dieselbe Gruppe oder einen bestimmten Endpunkt angewendet werden.
Eine Anforderung an /outer/inner/ protokolliert Folgendes:
/outer group filter
/inner group filter
MapGet filter
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)
- Formularwerte
- Von der Abhängigkeitsinjektion bereitgestellte Dienste
- Custom
Der folgende GET-Routenhandler verwendet 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 { }
Wichtige Parameterbindungsfunktionen
-
Explizite Bindung: Verwenden Sie Attribute wie
[FromRoute], ,[FromQuery][FromHeader],[FromBody],[FromForm]und[FromServices]geben Sie explizit Bindungsquellen an. -
Formularbindung: Binden Sie Formularwerte mithilfe des
[FromForm]Attributs, einschließlich Unterstützung fürIFormFileundIFormFileCollectionfür Dateiuploads. - Komplexe Typen: Binden Sie an Sammlungen und komplexe Typen aus Formularen, Abfragezeichenfolgen und Kopfzeilen.
-
Benutzerdefinierte Bindung: Implementieren Sie benutzerdefinierte Bindungslogik mithilfe von
TryParse,BindAsyncoder derIBindableFromHttpContext<T>-Schnittstelle. - Optionale Parameter: Unterstützen Nullwerte und Standardwerte für optionale Parameter.
- Abhängigkeitseinfügung: Parameter werden automatisch von Diensten gebunden, die im DI-Container registriert sind.
-
Spezielle Typen: Automatische Bindung für
HttpContext, ,HttpRequestHttpResponse,CancellationToken,ClaimsPrincipal, , undStreamPipeReader.
Weitere Informationen: Ausführliche Informationen zur Parameterbindung, einschließlich erweiterter Szenarien, Validierung, Bindungsrangfolge und Problembehandlung, finden Sie unter Parameterbindung in Minimal-API-Anwendungen.
Responses
Routenhandler unterstützen die folgenden Typen von Rückgabewerten:
-
IResult-basiert: Dies schließtTask<IResult>undValueTask<IResult>ein. -
string: Dies schließtTask<string>undValueTask<string>ein. -
T(ein beliebiger weiterer Typ): Dies schließtTask<T>undValueTask<T>ein.
| Rückgabewert | Behavior | 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 JSON-serialisiert die Antwort. | application/json |
Ausführlichere Anleitungen zu Rückgabewerten des Routenhandlers finden Sie unter Erstellen von Antworten in Minimal-API-Anwendungen.
Beispielrückgabewerte
Rückgabewerte vom Typ „string“
app.MapGet("/hello", () => "Hello World");
JSON-Rückgabewerte
app.MapGet("/hello", () => new { Message = "Hello World" });
Geben Sie Typisierte Ergebnisse zurück
Der folgende Code gibt TypedResults zurück:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Die Rückgabe von TypedResults wird der Rückgabe von Results vorgezogen. Weitere Informationen finden Sie unter Vergleich von TypedResults und Results.
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 proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
Weitere Beispiele finden Sie unter Erstellen von Antworten in Minimal-API-Anwendungen.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Integrierte Ergebnisse
Die statischen Klassen Results und TypedResults enthalten allgemeine Ergebnishilfen. Die Rückgabe von TypedResults wird der Rückgabe von Results vorgezogen. Weitere Informationen finden Sie unter Vergleich von TypedResults und Results.
Ändern von Kopfzeilen
Verwenden Sie das HttpResponse-Objekt, um Antwortheader zu ändern:
app.MapGet("/", (HttpContext context) => {
// Set a custom header
context.Response.Headers["X-Custom-Header"] = "CustomValue";
// Set a known header
context.Response.Headers.CacheControl = $"public,max-age=3600";
return "Hello World";
});
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();
Eingegebene Ergebnisse
Die IResult-Schnittstelle kann von minimalen APIs zurückgegebene Werte darstellen, welche nicht die implizite Unterstützung für die JSON-Serialisierung des zurückgegebenen Objekts in die HTTP-Antwort verwenden. Die statische Results-Klasse wird verwendet, um unterschiedliche IResult-Objekte zu erstellen, die verschiedene Arten von Antworten darstellen. Beispiel: Festlegen des Antwortstatuscodes oder Umleitung an eine andere URL.
Die IResult implementierenden Typen sind öffentlich, sodass Typassertionen beim Testen zugelassen werden. Beispiel:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Sie können die Rückgabetypen der entsprechenden Methoden in der statischen TypedResults-Klasse anzeigen, um den richtigen öffentlichen IResult-Typ zu finden, in den Werte umgewandelt erden sollen.
Weitere Beispiele finden Sie unter Erstellen von Antworten in Minimal-API-Anwendungen.
Filters
Weitere Informationen finden Sie unter Filter in Minimal-API-Apps.
Authorization
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.
ValidateScopes und ValidateOnBuild
ValidateScopes und ValidateOnBuild sind standardmäßig in der Entwicklungsumgebung aktiviert, aber in anderen Umgebungen deaktiviert.
Wenn ValidateOnBuildtrue ist, überprüft der DI-Container die Dienstkonfiguration zur Buildzeit. Wenn die Dienstkonfiguration ungültig ist, schlägt der Build beim Starten der App und nicht zur Laufzeit fehl, wenn der Dienst angefordert wird.
Wenn ValidateScopestrue ist, überprüft der DI-Container, dass ein bereichsbezogener Dienst nicht aus dem Stammbereich aufgelöst wird. Das Auflösen eines bereichsbezogenen Diensts aus dem Stammbereich kann zu einem Arbeitsspeicherverlust führen, da der Dienst länger als der Anforderungsbereich im Arbeitsspeicher aufbewahrt wird.
ValidateScopes und ValidateOnBuild sind aus Leistungsgründen standardmäßig in Nichtentwicklungsmodi falsch.
Der folgende Code zeigt, dass ValidateScopes im Entwicklungsmodus standardmäßig aktiviert, aber im Releasemodus deaktiviert ist:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<MyScopedService>();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
Console.WriteLine("Development environment");
}
else
{
Console.WriteLine("Release environment");
}
app.MapGet("/", context =>
{
// Intentionally getting service provider from app, not from the request
// This causes an exception from attempting to resolve a scoped service
// outside of a scope.
// Throws System.InvalidOperationException:
// 'Cannot resolve scoped service 'MyScopedService' from root provider.'
var service = app.Services.GetRequiredService<MyScopedService>();
return context.Response.WriteAsync("Service resolved");
});
app.Run();
public class MyScopedService { }
Der folgende Code zeigt, dass ValidateOnBuild im Entwicklungsmodus standardmäßig aktiviert, aber im Releasemodus deaktiviert ist:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<MyScopedService>();
builder.Services.AddScoped<AnotherService>();
// System.AggregateException: 'Some services are not able to be constructed (Error
// while validating the service descriptor 'ServiceType: AnotherService Lifetime:
// Scoped ImplementationType: AnotherService': Unable to resolve service for type
// 'BrokenService' while attempting to activate 'AnotherService'.)'
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
Console.WriteLine("Development environment");
}
else
{
Console.WriteLine("Release environment");
}
app.MapGet("/", context =>
{
var service = context.RequestServices.GetRequiredService<MyScopedService>();
return context.Response.WriteAsync("Service resolved correctly!");
});
app.Run();
public class MyScopedService { }
public class AnotherService
{
public AnotherService(BrokenService brokenService) { }
}
public class BrokenService { }
Der folgende Code deaktiviert ValidateScopes und ValidateOnBuild in Development:
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine("Development environment");
// Doesn't detect the validation problems because ValidateScopes is false.
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = false;
options.ValidateOnBuild = false;
});
}
Siehe auch
- Minimal-APIs: Kurzreferenz
- OpenAPI-Dokumente generieren
- Erstellen von Antworten in Minimal-API-Anwendungen
- Filter in Minimal-API-Apps
- Behandeln von Fehlern in ASP.NET Core-APIs
- Authentifizierung und Autorisierung in Minimal-API-Apps
- Testen von Minimal-API-Apps
- Kurzschlussrouting
- Identity-API-Endpunkte
- Unterstützung von Containern für abhängigkeitsbasierte Dienste
- Ein Blick hinter die Kulissen von minimalen API-Endpunkten
- Organisieren von Minimal-APIs für ASP.NET Core
- Fluent-Validierungsdiskussion auf GitHub
Dieses Dokument:
- Stellt eine Kurzreferenz für Minimal-APIs bereit.
- Ist für erfahrene Entwickler vorgesehen. Eine Einführung finden Sie im Tutorial: Erstellen einer Minimal-API mit ASP.NET Core.
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.
WebApplication Fügt die folgende Middleware automatisch in Minimal-API-Anwendungen hinzu, abhängig von bestimmten Bedingungen:
-
UseDeveloperExceptionPagewird zuerst hinzugefügt, wennHostingEnvironmentgleich"Development"ist. -
UseRoutingwird zweitens hinzugefügt, wenn der BenutzercodeUseRoutingnoch nicht aufgerufen hat, und wenn Endpunkte konfiguriert sind, z. B.app.MapGet. -
UseEndpointswird am Ende der Middlewarepipeline hinzugefügt, wenn Endpunkte konfiguriert sind. -
UseAuthenticationwird unmittelbar nachUseRoutinghinzugefügt, wenn der BenutzercodeUseAuthenticationnoch nicht aufgerufen hat und wennIAuthenticationSchemeProviderim Dienstanbieter erkannt werden kann.IAuthenticationSchemeProviderwird standardmäßig hinzugefügt, wenn die VerwendungAddAuthenticationvon Diensten mitIServiceProviderIsServiceerkannt wird. -
UseAuthorizationwird als Nächstes hinzugefügt, wenn der BenutzercodeUseAuthorizationnoch nicht aufgerufen hat und wennIAuthorizationHandlerProviderim Dienstanbieter erkannt werden kann.IAuthorizationHandlerProviderwird standardmäßig hinzugefügt, wenn die VerwendungAddAuthorizationvon Diensten mitIServiceProviderIsServiceerkannt wird. - Benutzerkonfigurierte Middleware und Endpunkte werden zwischen
UseRoutingundUseEndpointshinzugefügt.
Nachfolgend sehen Sie den Code, der von der automatischen Middleware erzeugt wird, die zur App hinzugefügt wird:
if (isDevelopment)
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
if (isAuthenticationConfigured)
{
app.UseAuthentication();
}
if (isAuthorizationConfigured)
{
app.UseAuthorization();
}
// user middleware/endpoints
app.CustomMiddleware(...);
app.MapGet("/", () => "hello world");
// end user middleware/endpoints
app.UseEndpoints(e => {});
In einigen Fällen eignet sich die standardmäßige Middleware-Konfiguration nicht für die App und muss geändert werden. Beispielsweise sollte UseCors vor UseAuthentication und UseAuthorization aufgerufen werden. Die App muss UseAuthentication und UseAuthorization aufrufen, wenn UseCors aufgerufen wird:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Wenn Middleware ausgeführt werden muss, bevor der Routenabgleich erfolgt, muss UseRouting aufgerufen werden, und die Middleware muss vor dem Aufruf von UseRouting platziert werden.
UseEndpoints ist in diesem Fall nicht erforderlich, da es wie zuvor beschrieben automatisch hinzugefügt wird:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Beim Hinzufügen einer Terminal-Middleware:
- Die Middleware muss nach
UseEndpointshinzugefügt werden. - Die App muss
UseRoutingundUseEndpointsaufrufen, damit die Terminal-Middleware an der richtigen Position platziert werden kann.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Terminal-Middleware ist Middleware, die ausgeführt wird, wenn kein Endpunkt die Anforderung verarbeitet.
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. Visual Studio gibt einen Fehler zurück, da der in Properties/launchSettings.jsonangegebene Port erwartet wird, die App jedoch den von app.Run("http://localhost:3000") angegebenen Port verwendet. 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
Überwachen aller Schnittstellen mithilfe von ASPNETCORE_HTTPS_PORTS
In den vorherigen Beispielen können ASPNETCORE_HTTPS_PORTS und ASPNETCORE_HTTP_PORTS verwendet werden.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Weitere Informationen finden Sie unter Konfigurieren von Endpunkten für den Kestrel-Webserver von ASP.NET Core.
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 ASP.NET Core-Laufzeitumgebungen
Configuration
Der folgende Code liest Informationen aus dem Konfigurationssystem:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Weitere Informationen finden Sie unter Konfiguration in ASP.NET Core.
Logging
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 Logging in .NET 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();
Der folgende Code zeigt, wie Sie mit dem [FromKeyedServices]-Attribut auf Schlüssel aus dem DI-Container (Dependency Injection, Abhängigkeitsinjektion) zugreifen können:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
var app = builder.Build();
app.MapGet("/big", ([FromKeyedServices("big")] ICache bigCache) => bigCache.Get("date"));
app.MapGet("/small", ([FromKeyedServices("small")] ICache smallCache) => smallCache.Get("date"));
app.Run();
public interface ICache
{
object Get(string key);
}
public class BigCache : ICache
{
public object Get(string key) => $"Resolving {key} from big cache.";
}
public class SmallCache : ICache
{
public object Get(string key) => $"Resolving {key} from small cache.";
}
Weitere Informationen zu DI 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
{
Args = args,
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 |
| Inhaltsstamm | 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.
Konfiguration lesen
Standardmäßig liest die WebApplicationBuilder die Konfiguration aus mehreren Quellen, darunter:
-
appSettings.jsonundappSettings.{environment}.json - Umgebungsvariablen
- Die Befehlszeile
Eine vollständige Liste der gelesenen Konfigurationsquellen finden Sie unter Standardkonfiguration in Konfiguration in ASP.NET Core.
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);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Lesen der Umgebung
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();
Dienste hinzufügen
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. Webstamm ist der Ort, an dem die Static File Middleware nach statischen Dateien sucht. 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 | Description | API |
|---|---|---|
| Authentication | Bietet Unterstützung für Authentifizierungen. | UseAuthentication |
| Authorization | 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 Kopfzeilen | 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 |
| Anforderungstimeouts | Bietet Unterstützung beim Konfigurieren von Anforderungstimeouts und globaler Standardwerte sowie bei der Konfiguration pro Endpunkt. | UseRequestTimeouts |
| W3C-Anforderungsprotokollierung | Bietet Unterstützung für die Protokollierung von HTTP-Anforderungen und -Antworten im W3C-Format. | UseW3CLogging |
| Zwischenspeicherung von Antworten | Bietet Unterstützung für das Zwischenspeichern von Antworten. | UseResponseCaching |
| Reaktionskomprimierung | Bietet Unterstützung für das Komprimieren von Antworten. | UseResponseCompression |
| Session | 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 |
In den folgenden Abschnitten werden Routing, Parameterbindung und Antworten behandelt.
Routing
Eine konfigurierte WebApplication unterstützt Map{Verb} und MapMethods, wobei {Verb} eine HTTP-Methode mit Camel-Case-Schreibweise wie Get, Post, Put oder Delete ist:
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();
Die an diese Methoden übergebenen Delegate-Argumente werden als „Routenhandler“ bezeichnet.
Routenhandler
Routenhandler sind Methoden, die ausgeführt werden, wenn die Route übereinstimmt. Als Routenhandler kann ein Lambdaausdruck, eine lokale Funktion, eine Instanzmethode oder eine statische Methode verwendet werden. Routenhandler können synchron oder asynchron sein.
Lambda-Ausdruck
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();
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";
}
}
Static-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";
}
}
Endpunkt außerhalb von Program.cs definiert
Minimale APIs müssen sich nicht in Program.cs befinden.
Program.cs
using MinAPISeparateFile;
var builder = WebApplication.CreateSlimBuilder(args);
var app = builder.Build();
TodoEndpoints.Map(app);
app.Run();
TodoEndpoints.cs
namespace MinAPISeparateFile;
public static class TodoEndpoints
{
public static void Map(WebApplication app)
{
app.MapGet("/", async context =>
{
// Get all todo items
await context.Response.WriteAsJsonAsync(new { Message = "All todo items" });
});
app.MapGet("/{id}", async context =>
{
// Get one todo item
await context.Response.WriteAsJsonAsync(new { Message = "One todo item" });
});
}
}
Weitere Informationen dazu finden Sie auch unter Routengruppen weiter unten in diesem Artikel.
Benannte Endpunkte und Linkgenerierung
Endpunkte können Namen erhalten, um URLs für den jeweiligen Endpunkt zu generieren. Durch die Verwendung eines benannten Endpunkts 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.
HINWEIS: Für Endpunktnamen muss Groß-/Kleinschreibung beachtet werden.
Endpunktnamen:
- Dieser muss global eindeutig sein.
- Werden als OpenAPI-Vorgangs-ID verwendet, wenn die OpenAPI-Unterstützung aktiviert ist. Weitere Informationen finden Sie unter 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. Bei einer Anforderung an eine Route mit Parametern, deren Erfassung deklariert wurde, werden die Parameter geparst 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.
Routengruppen
Die MapGroup-Erweiterungsmethode hilft, Gruppen von Endpunkten mit einem gemeinsamen Präfix zu organisieren. Sie reduziert sich wiederholenden Code und ermöglicht die benutzerdefinierte Anpassung ganzer Gruppen von Endpunkten mit einem einzigen Aufruf von Methoden wie RequireAuthorization und WithMetadata,die Endpunktmetadaten hinzufügen.
Der folgende Code erstellt beispielsweise zwei ähnliche Endpunktgruppen:
app.MapGroup("/public/todos")
.MapTodosApi()
.WithTags("Public");
app.MapGroup("/private/todos")
.MapTodosApi()
.WithTags("Private")
.AddEndpointFilterFactory(QueryPrivateTodos)
.RequireAuthorization();
EndpointFilterDelegate QueryPrivateTodos(EndpointFilterFactoryContext factoryContext, EndpointFilterDelegate next)
{
var dbContextIndex = -1;
foreach (var argument in factoryContext.MethodInfo.GetParameters())
{
if (argument.ParameterType == typeof(TodoDb))
{
dbContextIndex = argument.Position;
break;
}
}
// Skip filter if the method doesn't have a TodoDb parameter.
if (dbContextIndex < 0)
{
return next;
}
return async invocationContext =>
{
var dbContext = invocationContext.GetArgument<TodoDb>(dbContextIndex);
dbContext.IsPrivate = true;
try
{
return await next(invocationContext);
}
finally
{
// This should only be relevant if you're pooling or otherwise reusing the DbContext instance.
dbContext.IsPrivate = false;
}
};
}
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
group.MapGet("/", GetAllTodos);
group.MapGet("/{id}", GetTodo);
group.MapPost("/", CreateTodo);
group.MapPut("/{id}", UpdateTodo);
group.MapDelete("/{id}", DeleteTodo);
return group;
}
In diesem Szenario können Sie eine relative Adresse für den Location-Header im 201 Created-Ergebnis verwenden:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Die erste Gruppe von Endpunkten entspricht nur Anforderungen mit dem Präfix /public/todos und ist ohne Authentifizierung zugänglich. Die zweite Gruppe von Endpunkten entspricht nur Anforderungen mit dem Präfix /private/todos und erfordert Authentifizierung.
Die QueryPrivateTodos-Endpunktfilterfactory ist eine lokale Funktion, die die TodoDb-Parameter des Routenhandlers so ändert, dass Zugriff auf private Aufgabendaten zulässig ist und diese gespeichert werden können.
Routengruppen unterstützen auch geschachtelte Gruppen und komplexe Präfixmuster mit Routenparametern und -einschränkungen. Im folgenden Beispiel kann der der user-Gruppe zugeordnete Routenhandler die Routenparameter {org} und {group} erfassen, die in den Präfixen der äußeren Gruppe definiert sind.
Das Präfix kann auch leer sein. Dies kann hilfreich sein, um Endpunktmetadaten oder Filter einer Gruppe von Endpunkten hinzuzufügen, ohne das Routenmuster zu ändern.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Das Hinzufügen von Filtern oder Metadaten zu einer Gruppe verhält sich genauso wie das individuelle Hinzufügen zu jedem Endpunkt, bevor zusätzliche Filter oder Metadaten hinzugefügt werden, die einer inneren Gruppe oder einem bestimmten Endpunkt hinzugefügt wurden.
var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");
inner.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/inner group filter");
return next(context);
});
outer.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/outer group filter");
return next(context);
});
inner.MapGet("/", () => "Hi!").AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("MapGet filter");
return next(context);
});
Im Beispiel oben protokolliert der äußere Filter die eingehende Anforderung vor dem inneren Filter, obwohl er als zweiter hinzugefügt wurde. Da die Filter auf verschiedene Gruppen angewendet wurden, spielt die Reihenfolge, in der sie relativ zueinander hinzugefügt wurden, keine Rolle. Die Reihenfolge, in der Filter hinzugefügt werden, spielt eine Rolle, wenn sie auf dieselbe Gruppe oder einen bestimmten Endpunkt angewendet werden.
Eine Anforderung an /outer/inner/ protokolliert Folgendes:
/outer group filter
/inner group filter
MapGet filter
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)
- Formularwerte
- Von der Abhängigkeitsinjektion bereitgestellte Dienste
- Custom
Der folgende GET-Routenhandler verwendet 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 { }
Wichtige Parameterbindungsfunktionen
-
Explizite Bindung: Verwenden Sie Attribute wie
[FromRoute], ,[FromQuery][FromHeader],[FromBody],[FromForm]und[FromServices]geben Sie explizit Bindungsquellen an. -
Formularbindung: Binden Sie Formularwerte mithilfe des
[FromForm]Attributs, einschließlich Unterstützung fürIFormFileundIFormFileCollectionfür Dateiuploads. - Komplexe Typen: Binden an Sammlungen und komplexe Typen aus Formularen, Abfragezeichenfolgen und Kopfzeilen.
-
Benutzerdefinierte Bindung: Implementieren Sie benutzerdefinierte Bindungslogik mithilfe von
TryParse,BindAsyncoder derIBindableFromHttpContext<T>-Schnittstelle. - Optionale Parameter: Unterstützen nullbare Typen und Standardwerte für optionale Parameter.
- Abhängigkeitseinfügung: Parameter werden automatisch von Diensten gebunden, die im DI-Container registriert sind.
-
Spezielle Typen: Automatische Bindung für
HttpContext, ,HttpRequestHttpResponse,CancellationToken,ClaimsPrincipal, , undStreamPipeReader.
Weitere Informationen: Ausführliche Informationen zur Parameterbindung, einschließlich erweiterter Szenarien, Validierung, Bindungsrangfolge und Problembehandlung, finden Sie unter Parameterbindung in Minimal-API-Anwendungen.
Responses
Routenhandler unterstützen die folgenden Typen von Rückgabewerten:
-
IResult-basiert: Dies schließtTask<IResult>undValueTask<IResult>ein. -
string: Dies schließtTask<string>undValueTask<string>ein. -
T(ein beliebiger weiterer Typ): Dies schließtTask<T>undValueTask<T>ein.
| Rückgabewert | Behavior | 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 JSON-serialisiert die Antwort. | application/json |
Ausführlichere Anleitungen zu Rückgabewerten des Routenhandlers finden Sie unter Erstellen von Antworten in Minimal-API-Anwendungen.
Beispielrückgabewerte
Rückgabewerte vom Typ „string“
app.MapGet("/hello", () => "Hello World");
JSON-Rückgabewerte
app.MapGet("/hello", () => new { Message = "Hello World" });
Geben Sie Typisierte Ergebnisse zurück
Der folgende Code gibt TypedResults zurück:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Die Rückgabe von TypedResults wird der Rückgabe von Results vorgezogen. Weitere Informationen finden Sie unter Vergleich von TypedResults und Results.
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 proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
Weitere Beispiele finden Sie unter Erstellen von Antworten in Minimal-API-Anwendungen.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Integrierte Ergebnisse
Die statischen Klassen Results und TypedResults enthalten allgemeine Ergebnishilfen. Die Rückgabe von TypedResults wird der Rückgabe von Results vorgezogen. Weitere Informationen finden Sie unter Vergleich von TypedResults und Results.
Ändern von Kopfzeilen
Verwenden Sie das HttpResponse-Objekt, um Antwortheader zu ändern:
app.MapGet("/", (HttpContext context) => {
// Set a custom header
context.Response.Headers["X-Custom-Header"] = "CustomValue";
// Set a known header
context.Response.Headers.CacheControl = $"public,max-age=3600";
return "Hello World";
});
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();
Eingegebene Ergebnisse
Die IResult-Schnittstelle kann von minimalen APIs zurückgegebene Werte darstellen, welche nicht die implizite Unterstützung für die JSON-Serialisierung des zurückgegebenen Objekts in die HTTP-Antwort verwenden. Die statische Results-Klasse wird verwendet, um unterschiedliche IResult-Objekte zu erstellen, die verschiedene Arten von Antworten darstellen. Beispiel: Festlegen des Antwortstatuscodes oder Umleitung an eine andere URL.
Die IResult implementierenden Typen sind öffentlich, sodass Typassertionen beim Testen zugelassen werden. Beispiel:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Sie können die Rückgabetypen der entsprechenden Methoden in der statischen TypedResults-Klasse anzeigen, um den richtigen öffentlichen IResult-Typ zu finden, in den Werte umgewandelt erden sollen.
Weitere Beispiele finden Sie unter Erstellen von Antworten in Minimal-API-Anwendungen.
Filters
Weitere Informationen finden Sie unter Filter in Minimal-API-Apps.
Authorization
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.
ValidateScopes und ValidateOnBuild
ValidateScopes und ValidateOnBuild sind standardmäßig in der Entwicklungsumgebung aktiviert, aber in anderen Umgebungen deaktiviert.
Wenn ValidateOnBuildtrue ist, überprüft der DI-Container die Dienstkonfiguration zur Buildzeit. Wenn die Dienstkonfiguration ungültig ist, schlägt der Build beim Starten der App und nicht zur Laufzeit fehl, wenn der Dienst angefordert wird.
Wenn ValidateScopestrue ist, überprüft der DI-Container, dass ein bereichsbezogener Dienst nicht aus dem Stammbereich aufgelöst wird. Das Auflösen eines bereichsbezogenen Diensts aus dem Stammbereich kann zu einem Arbeitsspeicherverlust führen, da der Dienst länger als der Anforderungsbereich im Arbeitsspeicher aufbewahrt wird.
ValidateScopes und ValidateOnBuild sind aus Leistungsgründen standardmäßig in Nichtentwicklungsmodi falsch.
Der folgende Code zeigt, dass ValidateScopes im Entwicklungsmodus standardmäßig aktiviert, aber im Releasemodus deaktiviert ist:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<MyScopedService>();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
Console.WriteLine("Development environment");
}
else
{
Console.WriteLine("Release environment");
}
app.MapGet("/", context =>
{
// Intentionally getting service provider from app, not from the request
// This causes an exception from attempting to resolve a scoped service
// outside of a scope.
// Throws System.InvalidOperationException:
// 'Cannot resolve scoped service 'MyScopedService' from root provider.'
var service = app.Services.GetRequiredService<MyScopedService>();
return context.Response.WriteAsync("Service resolved");
});
app.Run();
public class MyScopedService { }
Der folgende Code zeigt, dass ValidateOnBuild im Entwicklungsmodus standardmäßig aktiviert, aber im Releasemodus deaktiviert ist:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<MyScopedService>();
builder.Services.AddScoped<AnotherService>();
// System.AggregateException: 'Some services are not able to be constructed (Error
// while validating the service descriptor 'ServiceType: AnotherService Lifetime:
// Scoped ImplementationType: AnotherService': Unable to resolve service for type
// 'BrokenService' while attempting to activate 'AnotherService'.)'
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
Console.WriteLine("Development environment");
}
else
{
Console.WriteLine("Release environment");
}
app.MapGet("/", context =>
{
var service = context.RequestServices.GetRequiredService<MyScopedService>();
return context.Response.WriteAsync("Service resolved correctly!");
});
app.Run();
public class MyScopedService { }
public class AnotherService
{
public AnotherService(BrokenService brokenService) { }
}
public class BrokenService { }
Der folgende Code deaktiviert ValidateScopes und ValidateOnBuild in Development:
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine("Development environment");
// Doesn't detect the validation problems because ValidateScopes is false.
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = false;
options.ValidateOnBuild = false;
});
}
Siehe auch
- Minimal-APIs: Kurzreferenz
- OpenAPI-Dokumente generieren
- Erstellen von Antworten in Minimal-API-Anwendungen
- Filter in Minimal-API-Apps
- Behandeln von Fehlern in ASP.NET Core-APIs
- Authentifizierung und Autorisierung in Minimal-API-Apps
- Testen von Minimal-API-Apps
- Kurzschlussrouting
- Identity-API-Endpunkte
- Unterstützung von Containern für abhängigkeitsbasierte Dienste
- Ein Blick hinter die Kulissen von minimalen API-Endpunkten
- Organisieren von Minimal-APIs für ASP.NET Core
- Fluent-Validierungsdiskussion auf GitHub
Dieses Dokument:
- Stellt eine Kurzreferenz für Minimal-APIs bereit.
- Ist für erfahrene Entwickler vorgesehen. Eine Einführung finden Sie im Tutorial: Erstellen einer minimalen API mit ASP.NET Core.
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.
WebApplication Fügt die folgende Middleware automatisch in Minimal-API-Anwendungen hinzu, abhängig von bestimmten Bedingungen:
-
UseDeveloperExceptionPagewird zuerst hinzugefügt, wennHostingEnvironmentgleich"Development"ist. -
UseRoutingwird zweitens hinzugefügt, wenn der BenutzercodeUseRoutingnoch nicht aufgerufen hat, und wenn Endpunkte konfiguriert sind, z. B.app.MapGet. -
UseEndpointswird am Ende der Middlewarepipeline hinzugefügt, wenn Endpunkte konfiguriert sind. -
UseAuthenticationwird unmittelbar nachUseRoutinghinzugefügt, wenn der BenutzercodeUseAuthenticationnoch nicht aufgerufen hat und wennIAuthenticationSchemeProviderim Dienstanbieter erkannt werden kann.IAuthenticationSchemeProviderwird standardmäßig hinzugefügt, wenn die VerwendungAddAuthenticationvon Diensten mitIServiceProviderIsServiceerkannt wird. -
UseAuthorizationwird als Nächstes hinzugefügt, wenn der BenutzercodeUseAuthorizationnoch nicht aufgerufen hat und wennIAuthorizationHandlerProviderim Dienstanbieter erkannt werden kann.IAuthorizationHandlerProviderwird standardmäßig hinzugefügt, wenn die VerwendungAddAuthorizationvon Diensten mitIServiceProviderIsServiceerkannt wird. - Benutzerkonfigurierte Middleware und Endpunkte werden zwischen
UseRoutingundUseEndpointshinzugefügt.
Nachfolgend sehen Sie den Code, der von der automatischen Middleware erzeugt wird, die zur App hinzugefügt wird:
if (isDevelopment)
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
if (isAuthenticationConfigured)
{
app.UseAuthentication();
}
if (isAuthorizationConfigured)
{
app.UseAuthorization();
}
// user middleware/endpoints
app.CustomMiddleware(...);
app.MapGet("/", () => "hello world");
// end user middleware/endpoints
app.UseEndpoints(e => {});
In einigen Fällen eignet sich die standardmäßige Middleware-Konfiguration nicht für die App und muss geändert werden. Beispielsweise sollte UseCors vor UseAuthentication und UseAuthorization aufgerufen werden. Die App muss UseAuthentication und UseAuthorization aufrufen, wenn UseCors aufgerufen wird:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Wenn Middleware ausgeführt werden muss, bevor der Routenabgleich erfolgt, muss UseRouting aufgerufen werden, und die Middleware muss vor dem Aufruf von UseRouting platziert werden.
UseEndpoints ist in diesem Fall nicht erforderlich, da es wie zuvor beschrieben automatisch hinzugefügt wird:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Beim Hinzufügen einer Terminal-Middleware:
- Die Middleware muss nach
UseEndpointshinzugefügt werden. - Die App muss
UseRoutingundUseEndpointsaufrufen, damit die Terminal-Middleware an der richtigen Position platziert werden kann.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Terminal-Middleware ist Middleware, die ausgeführt wird, wenn kein Endpunkt die Anforderung verarbeitet.
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. Visual Studio gibt einen Fehler zurück, da der in Properties/launchSettings.jsonangegebene Port erwartet wird, die App jedoch den von app.Run("http://localhost:3000") angegebenen Port verwendet. 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
Weitere Informationen zur Verwendung der Umgebung finden Sie unter ASP.NET Core-Laufzeitumgebungen
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();
Configuration
Der folgende Code liest Informationen aus dem Konfigurationssystem:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Weitere Informationen finden Sie unter Konfiguration in ASP.NET Core.
Logging
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 Logging in .NET 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
{
Args = args,
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 |
| Inhaltsstamm | 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.
Konfiguration lesen
Standardmäßig liest die WebApplicationBuilder die Konfiguration aus mehreren Quellen, darunter:
-
appSettings.jsonundappSettings.{environment}.json - Umgebungsvariablen
- Die Befehlszeile
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);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Eine vollständige Liste der gelesenen Konfigurationsquellen finden Sie unter Standardkonfiguration in Konfiguration in ASP.NET Core.
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();
Dienste hinzufügen
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. Webstamm ist der Ort, an dem die Static File Middleware nach statischen Dateien sucht. 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 | Description | API |
|---|---|---|
| Authentication | Bietet Unterstützung für Authentifizierungen. | UseAuthentication |
| Authorization | 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 Kopfzeilen | 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 |
| Anforderungstimeouts | Bietet Unterstützung beim Konfigurieren von Anforderungstimeouts und globaler Standardwerte sowie bei der Konfiguration pro Endpunkt. | UseRequestTimeouts |
| W3C-Anforderungsprotokollierung | Bietet Unterstützung für die Protokollierung von HTTP-Anforderungen und -Antworten im W3C-Format. | UseW3CLogging |
| Zwischenspeicherung von Antworten | Bietet Unterstützung für das Zwischenspeichern von Antworten. | UseResponseCaching |
| Reaktionskomprimierung | Bietet Unterstützung für das Komprimieren von Antworten. | UseResponseCompression |
| Session | 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 |
In den folgenden Abschnitten werden Routing, Parameterbindung und Antworten behandelt.
Routing
Eine konfigurierte WebApplication unterstützt Map{Verb} und MapMethods, wobei {Verb} eine HTTP-Methode mit Camel-Case-Schreibweise wie Get, Post, Put oder Delete ist:
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();
Die an diese Methoden übergebenen Delegate-Argumente werden als „Routenhandler“ bezeichnet.
Routenhandler
Routenhandler sind Methoden, die ausgeführt werden, wenn die Route übereinstimmt. Als Routenhandler kann ein Lambdaausdruck, eine lokale Funktion, eine Instanzmethode oder eine statische Methode verwendet werden. Routenhandler können synchron oder asynchron sein.
Lambda-Ausdruck
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();
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";
}
}
Static-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";
}
}
Endpunkt außerhalb von Program.cs definiert
Minimale APIs müssen sich nicht in Program.cs befinden.
Program.cs
using MinAPISeparateFile;
var builder = WebApplication.CreateSlimBuilder(args);
var app = builder.Build();
TodoEndpoints.Map(app);
app.Run();
TodoEndpoints.cs
namespace MinAPISeparateFile;
public static class TodoEndpoints
{
public static void Map(WebApplication app)
{
app.MapGet("/", async context =>
{
// Get all todo items
await context.Response.WriteAsJsonAsync(new { Message = "All todo items" });
});
app.MapGet("/{id}", async context =>
{
// Get one todo item
await context.Response.WriteAsJsonAsync(new { Message = "One todo item" });
});
}
}
Weitere Informationen dazu finden Sie auch unter Routengruppen weiter unten in diesem Artikel.
Benannte Endpunkte und Linkgenerierung
Endpunkte können Namen erhalten, um URLs für den jeweiligen Endpunkt zu generieren. Durch die Verwendung eines benannten Endpunkts 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.
HINWEIS: Für Endpunktnamen muss Groß-/Kleinschreibung beachtet werden.
Endpunktnamen:
- Dieser muss global eindeutig sein.
- Werden als OpenAPI-Vorgangs-ID verwendet, wenn die OpenAPI-Unterstützung aktiviert ist. Weitere Informationen finden Sie unter 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. Bei einer Anforderung an eine Route mit Parametern, deren Erfassung deklariert wurde, werden die Parameter geparst 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.
Routengruppen
Die MapGroup-Erweiterungsmethode hilft, Gruppen von Endpunkten mit einem gemeinsamen Präfix zu organisieren. Sie reduziert sich wiederholenden Code und ermöglicht die benutzerdefinierte Anpassung ganzer Gruppen von Endpunkten mit einem einzigen Aufruf von Methoden wie RequireAuthorization und WithMetadata,die Endpunktmetadaten hinzufügen.
Der folgende Code erstellt beispielsweise zwei ähnliche Endpunktgruppen:
app.MapGroup("/public/todos")
.MapTodosApi()
.WithTags("Public");
app.MapGroup("/private/todos")
.MapTodosApi()
.WithTags("Private")
.AddEndpointFilterFactory(QueryPrivateTodos)
.RequireAuthorization();
EndpointFilterDelegate QueryPrivateTodos(EndpointFilterFactoryContext factoryContext, EndpointFilterDelegate next)
{
var dbContextIndex = -1;
foreach (var argument in factoryContext.MethodInfo.GetParameters())
{
if (argument.ParameterType == typeof(TodoDb))
{
dbContextIndex = argument.Position;
break;
}
}
// Skip filter if the method doesn't have a TodoDb parameter.
if (dbContextIndex < 0)
{
return next;
}
return async invocationContext =>
{
var dbContext = invocationContext.GetArgument<TodoDb>(dbContextIndex);
dbContext.IsPrivate = true;
try
{
return await next(invocationContext);
}
finally
{
// This should only be relevant if you're pooling or otherwise reusing the DbContext instance.
dbContext.IsPrivate = false;
}
};
}
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
group.MapGet("/", GetAllTodos);
group.MapGet("/{id}", GetTodo);
group.MapPost("/", CreateTodo);
group.MapPut("/{id}", UpdateTodo);
group.MapDelete("/{id}", DeleteTodo);
return group;
}
In diesem Szenario können Sie eine relative Adresse für den Location-Header im 201 Created-Ergebnis verwenden:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Die erste Gruppe von Endpunkten entspricht nur Anforderungen mit dem Präfix /public/todos und ist ohne Authentifizierung zugänglich. Die zweite Gruppe von Endpunkten entspricht nur Anforderungen mit dem Präfix /private/todos und erfordert Authentifizierung.
Die QueryPrivateTodos-Endpunktfilterfactory ist eine lokale Funktion, die die TodoDb-Parameter des Routenhandlers so ändert, dass Zugriff auf private Aufgabendaten zulässig ist und diese gespeichert werden können.
Routengruppen unterstützen auch geschachtelte Gruppen und komplexe Präfixmuster mit Routenparametern und -einschränkungen. Im folgenden Beispiel kann der der user-Gruppe zugeordnete Routenhandler die Routenparameter {org} und {group} erfassen, die in den Präfixen der äußeren Gruppe definiert sind.
Das Präfix kann auch leer sein. Dies kann hilfreich sein, um Endpunktmetadaten oder Filter einer Gruppe von Endpunkten hinzuzufügen, ohne das Routenmuster zu ändern.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Das Hinzufügen von Filtern oder Metadaten zu einer Gruppe verhält sich genauso wie das individuelle Hinzufügen zu jedem Endpunkt, bevor zusätzliche Filter oder Metadaten hinzugefügt werden, die einer inneren Gruppe oder einem bestimmten Endpunkt hinzugefügt wurden.
var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");
inner.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/inner group filter");
return next(context);
});
outer.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/outer group filter");
return next(context);
});
inner.MapGet("/", () => "Hi!").AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("MapGet filter");
return next(context);
});
Im Beispiel oben protokolliert der äußere Filter die eingehende Anforderung vor dem inneren Filter, obwohl er als zweiter hinzugefügt wurde. Da die Filter auf verschiedene Gruppen angewendet wurden, spielt die Reihenfolge, in der sie relativ zueinander hinzugefügt wurden, keine Rolle. Die Reihenfolge, in der Filter hinzugefügt werden, spielt eine Rolle, wenn sie auf dieselbe Gruppe oder einen bestimmten Endpunkt angewendet werden.
Eine Anforderung an /outer/inner/ protokolliert Folgendes:
/outer group filter
/inner group filter
MapGet filter
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
- Custom
Die Bindung aus Formularen wird in .NET 6 und 7 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 Routenhandler, 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" |
Note
Die Bindung aus Formularen wird in .NET 6 und 7 nicht nativ unterstützt.
Parameterbindung mit Abhängigkeitsinjektion
Parameterbindung für minimale APIs bindet Parameter durch Abhängigkeitsinjektion (Dependency Injection, DI), wenn der Typ als Dienst konfiguriert wird. Es ist nicht erforderlich, das [FromServices]-Attribut explizit auf einen Parameter anzuwenden. Im folgenden Code geben beide Aktionen die Uhrzeit zurück:
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IDateTime, SystemDateTime>();
var app = builder.Build();
app.MapGet("/", ( IDateTime dateTime) => dateTime.Now);
app.MapGet("/fs", ([FromServices] IDateTime dateTime) => dateTime.Now);
app.Run();
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?pageNumber=two |
BadHttpRequestException: Fehler beim Binden von Parameter "Nullable<int> pageNumber" aus „two“. |
/products/two |
HTTP-Fehler vom Typ 404, keine übereinstimmende Route |
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);
Binden des Anforderungstexts als Stream oder PipeReader
Der Anforderungstext kann als Stream oder PipeReader gebunden werden, um effizient Szenarien zu unterstützen, in denen der Benutzer Daten verarbeiten und Folgendes tun muss:
- Daten im Blobspeicher speichern oder Daten in die Warteschlange eines Warteschlangenanbieters einreihen.
- Die gespeicherten Daten mit einem Workerprozess oder einer Cloudfunktion verarbeiten.
Beispielsweise werden die Daten möglicherweise in Azure Queue Storage eingereiht oder in Azure Blob Storage gespeichert.
Der folgende Code implementiert eine Hintergrundwarteschlange:
using System.Text.Json;
using System.Threading.Channels;
namespace BackgroundQueueService;
class BackgroundQueue : BackgroundService
{
private readonly Channel<ReadOnlyMemory<byte>> _queue;
private readonly ILogger<BackgroundQueue> _logger;
public BackgroundQueue(Channel<ReadOnlyMemory<byte>> queue,
ILogger<BackgroundQueue> logger)
{
_queue = queue;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await foreach (var dataStream in _queue.Reader.ReadAllAsync(stoppingToken))
{
try
{
var person = JsonSerializer.Deserialize<Person>(dataStream.Span)!;
_logger.LogInformation($"{person.Name} is {person.Age} " +
$"years and from {person.Country}");
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
}
}
}
class Person
{
public string Name { get; set; } = String.Empty;
public int Age { get; set; }
public string Country { get; set; } = String.Empty;
}
Der folgende Code bindet den Anforderungstext an einen Stream:
app.MapPost("/register", async (HttpRequest req, Stream body,
Channel<ReadOnlyMemory<byte>> queue) =>
{
if (req.ContentLength is not null && req.ContentLength > maxMessageSize)
{
return Results.BadRequest();
}
// We're not above the message size and we have a content length, or
// we're a chunked request and we're going to read up to the maxMessageSize + 1.
// We add one to the message size so that we can detect when a chunked request body
// is bigger than our configured max.
var readSize = (int?)req.ContentLength ?? (maxMessageSize + 1);
var buffer = new byte[readSize];
// Read at least that many bytes from the body.
var read = await body.ReadAtLeastAsync(buffer, readSize, throwOnEndOfStream: false);
// We read more than the max, so this is a bad request.
if (read > maxMessageSize)
{
return Results.BadRequest();
}
// Attempt to send the buffer to the background queue.
if (queue.Writer.TryWrite(buffer.AsMemory(0..read)))
{
return Results.Accepted();
}
// We couldn't accept the message since we're overloaded.
return Results.StatusCode(StatusCodes.Status429TooManyRequests);
});
Der folgende Code veranschaulicht die vollständige Program.cs-Datei:
using System.Threading.Channels;
using BackgroundQueueService;
var builder = WebApplication.CreateBuilder(args);
// The max memory to use for the upload endpoint on this instance.
var maxMemory = 500 * 1024 * 1024;
// The max size of a single message, staying below the default LOH size of 85K.
var maxMessageSize = 80 * 1024;
// The max size of the queue based on those restrictions
var maxQueueSize = maxMemory / maxMessageSize;
// Create a channel to send data to the background queue.
builder.Services.AddSingleton<Channel<ReadOnlyMemory<byte>>>((_) =>
Channel.CreateBounded<ReadOnlyMemory<byte>>(maxQueueSize));
// Create a background queue service.
builder.Services.AddHostedService<BackgroundQueue>();
var app = builder.Build();
// curl --request POST 'https://localhost:<port>/register' --header 'Content-Type: application/json' --data-raw '{ "Name":"Samson", "Age": 23, "Country":"Nigeria" }'
// curl --request POST "https://localhost:<port>/register" --header "Content-Type: application/json" --data-raw "{ \"Name\":\"Samson\", \"Age\": 23, \"Country\":\"Nigeria\" }"
app.MapPost("/register", async (HttpRequest req, Stream body,
Channel<ReadOnlyMemory<byte>> queue) =>
{
if (req.ContentLength is not null && req.ContentLength > maxMessageSize)
{
return Results.BadRequest();
}
// We're not above the message size and we have a content length, or
// we're a chunked request and we're going to read up to the maxMessageSize + 1.
// We add one to the message size so that we can detect when a chunked request body
// is bigger than our configured max.
var readSize = (int?)req.ContentLength ?? (maxMessageSize + 1);
var buffer = new byte[readSize];
// Read at least that many bytes from the body.
var read = await body.ReadAtLeastAsync(buffer, readSize, throwOnEndOfStream: false);
// We read more than the max, so this is a bad request.
if (read > maxMessageSize)
{
return Results.BadRequest();
}
// Attempt to send the buffer to the background queue.
if (queue.Writer.TryWrite(buffer.AsMemory(0..read)))
{
return Results.Accepted();
}
// We couldn't accept the message since we're overloaded.
return Results.StatusCode(StatusCodes.Status429TooManyRequests);
});
app.Run();
- Beim Lesen von Daten ist der
Streamdasselbe Objekt wieHttpRequest.Body. - Der Anforderungstext wird standardmäßig nicht gepuffert. Nachdem der Textkörper gelesen wurde, ist eine Rückkehr zum Anfang nicht möglich. Der Datenstrom kann nicht mehrmals gelesen werden.
-
StreamundPipeReadersind außerhalb des minimalen Aktionshandlers nicht verwendbar, da die zugrunde liegenden Puffer verworfen oder wiederverwendet werden.
Dateiuploads mit IFormFile und IFormFileCollection
Der folgende Code verwendet IFormFile und IFormFileCollection zum Hochladen der Datei:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.MapPost("/upload", async (IFormFile file) =>
{
var tempFile = Path.GetTempFileName();
app.Logger.LogInformation(tempFile);
using var stream = File.OpenWrite(tempFile);
await file.CopyToAsync(stream);
});
app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
{
foreach (var file in myFiles)
{
var tempFile = Path.GetTempFileName();
app.Logger.LogInformation(tempFile);
using var stream = File.OpenWrite(tempFile);
await file.CopyToAsync(stream);
}
});
app.Run();
Authentifizierte Dateiuploadanforderungen werden mithilfe eines Autorisierungsheaders, eines Clientzertifikats oder eines cookie-Headers unterstützt.
Es gibt keine integrierte Unterstützung für Antifälschung in ASP.NET Core in .NET 7.
Antiforgery ist in ASP.NET Core in .NET 8 oder höher verfügbar. Die Implementierung ist jedoch mit dem IAntiforgery-Dienst möglich.
Binden von Arrays und Zeichenfolgenwerten aus Headern und Abfragezeichenfolgen
Der folgende Code veranschaulicht die Bindung von Abfragezeichenfolgen an ein Array von primitiven Typen, Zeichenfolgenarrays und StringValues:
// Bind query string values to a primitive type array.
// GET /tags?q=1&q=2&q=3
app.MapGet("/tags", (int[] q) =>
$"tag1: {q[0]} , tag2: {q[1]}, tag3: {q[2]}");
// Bind to a string array.
// GET /tags2?names=john&names=jack&names=jane
app.MapGet("/tags2", (string[] names) =>
$"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}");
// Bind to StringValues.
// GET /tags3?names=john&names=jack&names=jane
app.MapGet("/tags3", (StringValues names) =>
$"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}");
Die Bindung von Abfragezeichenfolgen oder Headerwerten an ein Array komplexer Typen wird unterstützt, wenn im Typ TryParse implementiert ist. Der folgende Code bindet an ein Zeichenfolgenarray und gibt alle Elemente mit den angegebenen Tags zurück:
// GET /todoitems/tags?tags=home&tags=work
app.MapGet("/todoitems/tags", async (Tag[] tags, TodoDb db) =>
{
return await db.Todos
.Where(t => tags.Select(i => i.Name).Contains(t.Tag.Name))
.ToListAsync();
});
Der folgende Code zeigt das Modell und die erforderliche TryParse-Implementierung:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
// This is an owned entity.
public Tag Tag { get; set; } = new();
}
[Owned]
public class Tag
{
public string? Name { get; set; } = "n/a";
public static bool TryParse(string? name, out Tag tag)
{
if (name is null)
{
tag = default!;
return false;
}
tag = new Tag { Name = name };
return true;
}
}
Der folgende Code richtet eine Bindung mit einem int-Array ein:
// GET /todoitems/query-string-ids?ids=1&ids=3
app.MapGet("/todoitems/query-string-ids", async (int[] ids, TodoDb db) =>
{
return await db.Todos
.Where(t => ids.Contains(t.Id))
.ToListAsync();
});
Fügen Sie zum Testen des vorherigen Codes den folgenden Endpunkt hinzu, um die Datenbank mit Todo-Elementen aufzufüllen:
// POST /todoitems/batch
app.MapPost("/todoitems/batch", async (Todo[] todos, TodoDb db) =>
{
await db.Todos.AddRangeAsync(todos);
await db.SaveChangesAsync();
return Results.Ok(todos);
});
Verwenden Sie ein API-Testtool wie HttpRepl, um die folgenden Daten an den vorherigen Endpunkt zu übergeben:
[
{
"id": 1,
"name": "Have Breakfast",
"isComplete": true,
"tag": {
"name": "home"
}
},
{
"id": 2,
"name": "Have Lunch",
"isComplete": true,
"tag": {
"name": "work"
}
},
{
"id": 3,
"name": "Have Supper",
"isComplete": true,
"tag": {
"name": "home"
}
},
{
"id": 4,
"name": "Have Snacks",
"isComplete": true,
"tag": {
"name": "N/A"
}
}
]
Der folgende Code bindet an den Headerschlüssel X-Todo-Id und gibt die Todo-Elemente mit übereinstimmenden Id-Werten zurück:
// GET /todoitems/header-ids
// The keys of the headers should all be X-Todo-Id with different values
app.MapGet("/todoitems/header-ids", async ([FromHeader(Name = "X-Todo-Id")] int[] ids, TodoDb db) =>
{
return await db.Todos
.Where(t => ids.Contains(t.Id))
.ToListAsync();
});
Note
Beim Binden eines string[]-Werts aus einer Abfragezeichenfolge führt das Fehlen einer übereinstimmenden Abfragezeichenfolge zu einem leeren Array anstelle eines NULL-Werts.
Parameterbindung für Argumentlisten mit [AsParameters]
AsParametersAttribute ermöglicht einfache Parameterbindung an Typen und nicht komplexe oder rekursive Modellbindung.
Betrachten Sie folgenden Code:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
var app = builder.Build();
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.Select(x => new TodoItemDTO(x)).ToListAsync());
app.MapGet("/todoitems/{id}",
async (int Id, TodoDb Db) =>
await Db.Todos.FindAsync(Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
// Remaining code removed for brevity.
Betrachten Sie den folgenden GET-Endpunkt:
app.MapGet("/todoitems/{id}",
async (int Id, TodoDb Db) =>
await Db.Todos.FindAsync(Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
Mit der folgenden struct können die vorherigen hervorgehobenen Parameter ersetzt werden:
struct TodoItemRequest
{
public int Id { get; set; }
public TodoDb Db { get; set; }
}
Der umgestaltete GET-Endpunkt verwendet die vorherige struct mit dem AsParameters-Attribut:
app.MapGet("/ap/todoitems/{id}",
async ([AsParameters] TodoItemRequest request) =>
await request.Db.Todos.FindAsync(request.Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
Der folgende Code zeigt zusätzliche Endpunkte in der App:
app.MapPost("/todoitems", async (TodoItemDTO Dto, TodoDb Db) =>
{
var todoItem = new Todo
{
IsComplete = Dto.IsComplete,
Name = Dto.Name
};
Db.Todos.Add(todoItem);
await Db.SaveChangesAsync();
return Results.Created($"/todoitems/{todoItem.Id}", new TodoItemDTO(todoItem));
});
app.MapPut("/todoitems/{id}", async (int Id, TodoItemDTO Dto, TodoDb Db) =>
{
var todo = await Db.Todos.FindAsync(Id);
if (todo is null) return Results.NotFound();
todo.Name = Dto.Name;
todo.IsComplete = Dto.IsComplete;
await Db.SaveChangesAsync();
return Results.NoContent();
});
app.MapDelete("/todoitems/{id}", async (int Id, TodoDb Db) =>
{
if (await Db.Todos.FindAsync(Id) is Todo todo)
{
Db.Todos.Remove(todo);
await Db.SaveChangesAsync();
return Results.Ok(new TodoItemDTO(todo));
}
return Results.NotFound();
});
Die folgenden Klassen werden verwendet, um die Parameterlisten umzugestalten:
class CreateTodoItemRequest
{
public TodoItemDTO Dto { get; set; } = default!;
public TodoDb Db { get; set; } = default!;
}
class EditTodoItemRequest
{
public int Id { get; set; }
public TodoItemDTO Dto { get; set; } = default!;
public TodoDb Db { get; set; } = default!;
}
Der folgende Code zeigt die umgestalteten Endpunkte mit AsParameters und der vorherigen struct und Klassen:
app.MapPost("/ap/todoitems", async ([AsParameters] CreateTodoItemRequest request) =>
{
var todoItem = new Todo
{
IsComplete = request.Dto.IsComplete,
Name = request.Dto.Name
};
request.Db.Todos.Add(todoItem);
await request.Db.SaveChangesAsync();
return Results.Created($"/todoitems/{todoItem.Id}", new TodoItemDTO(todoItem));
});
app.MapPut("/ap/todoitems/{id}", async ([AsParameters] EditTodoItemRequest request) =>
{
var todo = await request.Db.Todos.FindAsync(request.Id);
if (todo is null) return Results.NotFound();
todo.Name = request.Dto.Name;
todo.IsComplete = request.Dto.IsComplete;
await request.Db.SaveChangesAsync();
return Results.NoContent();
});
app.MapDelete("/ap/todoitems/{id}", async ([AsParameters] TodoItemRequest request) =>
{
if (await request.Db.Todos.FindAsync(request.Id) is Todo todo)
{
request.Db.Todos.Remove(todo);
await request.Db.SaveChangesAsync();
return Results.Ok(new TodoItemDTO(todo));
}
return Results.NotFound();
});
Die folgenden record-Typen können verwendet werden, um die vorherigen Parameter zu ersetzen:
record TodoItemRequest(int Id, TodoDb Db);
record CreateTodoItemRequest(TodoItemDTO Dto, TodoDb Db);
record EditTodoItemRequest(int Id, TodoItemDTO Dto, TodoDb Db);
Die Verwendung einer struct mit AsParameters kann effizienter sein als die Verwendung eines record-Typs.
Den vollständigen Beispielcode finden Sie im AspNetCore.Docs.Samples-Repository.
Benutzerdefinierte Bindung
Es gibt drei Möglichkeiten zum Anpassen der Parameterbindung:
- Binden Sie für Routen-, Abfrage- und Headerbindungsquellen benutzerdefinierte Typen, indem Sie eine statische
TryParse-Methode für den Typ hinzufügen. - Steuern Sie den Bindungsprozess, indem Sie eine
BindAsync-Methode für einen Typ implementieren. - Implementieren Sie für erweiterte Szenarien die IBindableFromHttpContext<TSelf>-Schnittstelle, um eine benutzerdefinierte Bindungslogik direkt aus dem
HttpContextbereitzustellen.
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 folgende 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
}
Benutzerdefinierte Parameterbindung mit IBindableFromHttpContext
ASP.NET Core bietet Unterstützung für die benutzerdefinierte Parameterbindung in minimalen APIs mithilfe der IBindableFromHttpContext<TSelf> Schnittstelle. Diese Schnittstelle, die mit den statischen abstrakten Membern von C# 11 eingeführt wurde, ermöglicht ihnen das Erstellen von Typen, die aus einem HTTP-Kontext direkt in Routinghandlerparametern gebunden werden können.
public interface IBindableFromHttpContext<TSelf>
where TSelf : class, IBindableFromHttpContext<TSelf>
{
static abstract ValueTask<TSelf?> BindAsync(HttpContext context, ParameterInfo parameter);
}
Durch die Implementierung der IBindableFromHttpContext<TSelf> Schnittstelle können Sie benutzerdefinierte Typen erstellen, die ihre eigene Bindungslogik aus dem HttpContext behandeln. Wenn ein Routenhandler einen Parameter dieses Typs enthält, ruft das Framework automatisch die statische BindAsync-Methode auf, um die Instanz zu erstellen:
using CustomBindingExample;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseHttpsRedirection();
app.MapGet("/", () => "Hello, IBindableFromHttpContext example!");
app.MapGet("/custom-binding", (CustomBoundParameter param) =>
{
return $"Value from custom binding: {param.Value}";
});
app.MapGet("/combined/{id}", (int id, CustomBoundParameter param) =>
{
return $"ID: {id}, Custom Value: {param.Value}";
});
Es folgt eine Beispielimplementierung eines benutzerdefinierten Parameters, der von einem HTTP-Header gebunden wird:
using System.Reflection;
namespace CustomBindingExample;
public class CustomBoundParameter : IBindableFromHttpContext<CustomBoundParameter>
{
public string Value { get; init; } = default!;
public static ValueTask<CustomBoundParameter?> BindAsync(HttpContext context, ParameterInfo parameter)
{
// Custom binding logic here
// This example reads from a custom header
var value = context.Request.Headers["X-Custom-Header"].ToString();
// If no header was provided, you could fall back to a query parameter
if (string.IsNullOrEmpty(value))
{
value = context.Request.Query["customValue"].ToString();
}
return ValueTask.FromResult<CustomBoundParameter?>(new CustomBoundParameter
{
Value = value
});
}
}
Sie können die Überprüfung auch in Ihrer benutzerdefinierten Bindungslogik implementieren:
app.MapGet("/validated", (ValidatedParameter param) =>
{
if (string.IsNullOrEmpty(param.Value))
{
return Results.BadRequest("Value cannot be empty");
}
return Results.Ok($"Validated value: {param.Value}");
});
Anzeigen oder Herunterladen des Beispielcodes (herunterladen)
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. |
yes | route/query/header | 400 |
{ParameterType}.BindAsync gibt null zurück. |
yes | 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:
- Explizites Attribut, das für den Parameter (From*-Attribute) in der folgenden Reihenfolge definiert ist:
- Routenwerte:
[FromRoute] - Abfragezeichenfolge:
[FromQuery] - Kopfzeile:
[FromHeader] - Hauptteil:
[FromBody] - Dienst:
[FromServices] - Parameterwerte:
[AsParameters]
- Routenwerte:
- Sondertypen
HttpContext-
HttpRequest(HttpContext.Request) -
HttpResponse(HttpContext.Response) -
ClaimsPrincipal(HttpContext.User) -
CancellationToken(HttpContext.RequestAborted) -
IFormFileCollection(HttpContext.Request.Form.Files) -
IFormFile(HttpContext.Request.Form.Files[paramName]) -
Stream(HttpContext.Request.Body) -
PipeReader(HttpContext.Request.BodyReader)
- Der Parametertyp verfügt über eine gültige statische
BindAsync-Methode. - Der Parametertyp lautet „string“ oder verfügt über eine gültige statische
TryParse-Methode.- Wenn der Parametername in der Routenvorlage vorhanden ist. In
app.Map("/todo/{id}", (int id) => {});gehtidvon der Route aus. - Bindung über die Abfragezeichenfolge.
- Wenn der Parametername in der Routenvorlage vorhanden ist. In
- Wenn der Parametertyp ein durch die Abhängigkeitsinjektion bereitgestellter Dienst ist, wird dieser Dienst als Quelle verwendet.
- Der Parameter stammt aus dem Text.
Konfigurieren von JSON-Deserialisierungsoptionen für die Textbindung
Die Textbindungsquelle verwendet System.Text.Json für die Deserialisierung. Es ist nicht möglich, diese Standardeinstellung zu ändern, aber die JSON-Serialisierungs- und Deserialisierungsoptionen können konfiguriert werden.
Globales Konfigurieren von JSON-Deserialisierungsoptionen
Optionen, die global für eine App gelten, können durch Aufrufen von ConfigureHttpJsonOptions konfiguriert werden. Das folgende Beispiel enthält öffentliche Felder und Formate der JSON-Ausgabe.
var builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigureHttpJsonOptions(options => {
options.SerializerOptions.WriteIndented = true;
options.SerializerOptions.IncludeFields = true;
});
var app = builder.Build();
app.MapPost("/", (Todo todo) => {
if (todo is not null) {
todo.Name = todo.NameField;
}
return todo;
});
app.Run();
class Todo {
public string? Name { get; set; }
public string? NameField;
public bool IsComplete { get; set; }
}
// If the request body contains the following JSON:
//
// {"nameField":"Walk dog", "isComplete":false}
//
// The endpoint returns the following JSON:
//
// {
// "name":"Walk dog",
// "nameField":"Walk dog",
// "isComplete":false
// }
Da der Beispielcode sowohl die Serialisierung als auch die Deserialisierung konfiguriert, kann er NameField lesen und NameField in die JSON-Ausgabe einschließen.
Konfigurieren von JSON-Deserialisierungsoptionen für einen Endpunkt
ReadFromJsonAsync verfügt über Überladungen, die ein JsonSerializerOptions-Objekt akzeptieren. Das folgende Beispiel enthält öffentliche Felder und Formate der JSON-Ausgabe.
using System.Text.Json;
var app = WebApplication.Create();
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web) {
IncludeFields = true,
WriteIndented = true
};
app.MapPost("/", async (HttpContext context) => {
if (context.Request.HasJsonContentType()) {
var todo = await context.Request.ReadFromJsonAsync<Todo>(options);
if (todo is not null) {
todo.Name = todo.NameField;
}
return Results.Ok(todo);
}
else {
return Results.BadRequest();
}
});
app.Run();
class Todo
{
public string? Name { get; set; }
public string? NameField;
public bool IsComplete { get; set; }
}
// If the request body contains the following JSON:
//
// {"nameField":"Walk dog", "isComplete":false}
//
// The endpoint returns the following JSON:
//
// {
// "name":"Walk dog",
// "isComplete":false
// }
Da der vorangehende Code die angepassten Optionen nur auf die Deserialisierung anwendet, schließt die JSON-Ausgabe NameField aus.
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.
Responses
Routenhandler unterstützen die folgenden Typen von Rückgabewerten:
-
IResult-basiert: Dies schließtTask<IResult>undValueTask<IResult>ein. -
string: Dies schließtTask<string>undValueTask<string>ein. -
T(ein beliebiger weiterer Typ): Dies schließtTask<T>undValueTask<T>ein.
| Rückgabewert | Behavior | 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 JSON-serialisiert die Antwort. | application/json |
Ausführlichere Anleitungen zu Rückgabewerten des Routenhandlers finden Sie unter Erstellen von Antworten in Minimal-API-Anwendungen.
Beispielrückgabewerte
Rückgabewerte vom Typ „string“
app.MapGet("/hello", () => "Hello World");
JSON-Rückgabewerte
app.MapGet("/hello", () => new { Message = "Hello World" });
Geben Sie Typisierte Ergebnisse zurück
Der folgende Code gibt TypedResults zurück:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Die Rückgabe von TypedResults wird der Rückgabe von Results vorgezogen. Weitere Informationen finden Sie unter Vergleich von TypedResults und Results.
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 proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
Weitere Beispiele finden Sie unter Erstellen von Antworten in Minimal-API-Anwendungen.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Integrierte Ergebnisse
Die statischen Klassen Results und TypedResults enthalten allgemeine Ergebnishilfen. Die Rückgabe von TypedResults wird der Rückgabe von Results vorgezogen. Weitere Informationen finden Sie unter Vergleich von TypedResults und Results.
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();
Eingegebene Ergebnisse
Die IResult-Schnittstelle kann von minimalen APIs zurückgegebene Werte darstellen, welche nicht die implizite Unterstützung für die JSON-Serialisierung des zurückgegebenen Objekts in die HTTP-Antwort verwenden. Die statische Results-Klasse wird verwendet, um unterschiedliche IResult-Objekte zu erstellen, die verschiedene Arten von Antworten darstellen. Beispiel: Festlegen des Antwortstatuscodes oder Umleitung an eine andere URL.
Die IResult implementierenden Typen sind öffentlich, sodass Typassertionen beim Testen zugelassen werden. Beispiel:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Sie können die Rückgabetypen der entsprechenden Methoden in der statischen TypedResults-Klasse anzeigen, um den richtigen öffentlichen IResult-Typ zu finden, in den Werte umgewandelt erden sollen.
Weitere Beispiele finden Sie unter Erstellen von Antworten in Minimal-API-Anwendungen.
Filters
Weitere Informationen finden Sie unter Filter in Minimal-API-Apps.
Authorization
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.
Siehe auch
Dieses Dokument:
- Stellt eine Kurzreferenz für Minimal-APIs bereit.
- Ist für erfahrene Entwickler vorgesehen. Eine Einführung finden Sie im Tutorial: Erstellen einer minimalen API mit ASP.NET Core.
Die Minimal-APIs bestehen aus:
- WebApplication und WebApplicationBuilder
- Routenhandler
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 ASP.NET Core-Laufzeitumgebungen
Configuration
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.
Logging
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 Logging in .NET 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
{
Args = args,
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 |
| Inhaltsstamm | 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.
Konfiguration lesen
Standardmäßig liest die WebApplicationBuilder die Konfiguration aus mehreren Quellen, darunter:
-
appSettings.jsonundappSettings.{environment}.json - Umgebungsvariablen
- Die Befehlszeile
Eine vollständige Liste der gelesenen Konfigurationsquellen finden Sie unter Standardkonfiguration in Konfiguration in ASP.NET Core.
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);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Lesen der Umgebung
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
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();
Dienste hinzufügen
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. Webstamm ist der Ort, an dem die Static File Middleware nach statischen Dateien sucht. 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 | Description | API |
|---|---|---|
| Authentication | Bietet Unterstützung für Authentifizierungen. | UseAuthentication |
| Authorization | 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 Kopfzeilen | 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 |
| Zwischenspeicherung von Antworten | Bietet Unterstützung für das Zwischenspeichern von Antworten. | UseResponseCaching |
| Reaktionskomprimierung | Bietet Unterstützung für das Komprimieren von Antworten. | UseResponseCompression |
| Session | 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 in beliebiger Form sein, sowohl synchron als auch asynchron. Als Routenhandler kann ein Lambdaausdruck, eine lokale Funktion, eine Instanzmethode oder eine statische Methode verwendet werden.
Lambda-Ausdruck
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();
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";
}
}
Static-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";
}
}
Benannte Endpunkte und Linkgenerierung
Endpunkte können Namen erhalten, um URLs für den jeweiligen Endpunkt zu generieren. Durch die Verwendung eines benannten Endpunkts 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 endpoint is /hello vom Endpunkt / an.
HINWEIS: Für Endpunktnamen muss Groß-/Kleinschreibung beachtet werden.
Endpunktnamen:
- Dieser muss global eindeutig sein.
- Werden als OpenAPI-Vorgangs-ID verwendet, wenn die OpenAPI-Unterstützung aktiviert ist. Weitere Informationen finden Sie unter 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
- Custom
Note
Die Bindung aus Formularen wird in .NET 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" |
Note
Die Bindung aus Formularen wird in .NET nicht nativ unterstützt.
Parameterbindung mit DI
Parameterbindung für minimale APIs bindet Parameter durch Abhängigkeitsinjektion (Dependency Injection, DI), wenn der Typ als Dienst konfiguriert wird. Es ist nicht erforderlich, das [FromServices]-Attribut explizit auf einen Parameter anzuwenden. Im folgenden Code geben beide Aktionen die Uhrzeit zurück:
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IDateTime, SystemDateTime>();
var app = builder.Build();
app.MapGet("/", ( IDateTime dateTime) => dateTime.Now);
app.MapGet("/fs", ([FromServices] IDateTime dateTime) => dateTime.Now);
app.Run();
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?pageNumber=two |
BadHttpRequestException: Fehler beim Binden von Parameter "Nullable<int> pageNumber" aus „two“. |
/products/two |
HTTP-Fehler vom Typ 404, keine übereinstimmende Route |
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:
- Binden Sie für Routen-, Abfrage- und Headerbindungsquellen benutzerdefinierte Typen, indem Sie eine statische
TryParse-Methode für den Typ hinzufügen. - 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 folgende 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. |
yes | route/query/header | 400 |
{ParameterType}.BindAsync gibt null zurück. |
yes | 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:
- Explizites Attribut, das für den Parameter (From*-Attribute) in der folgenden Reihenfolge definiert ist:
- Routenwerte:
[FromRoute] - Abfragezeichenfolge:
[FromQuery] - Kopfzeile:
[FromHeader] - Hauptteil:
[FromBody] - Dienst:
[FromServices]
- Routenwerte:
- Sondertypen
- Der Parametertyp umfasst eine gültige
BindAsync-Methode. - Der Parametertyp lautet „string“ oder umfasst eine gültige
TryParse-Methode.- Wenn der Parametername in der Routenvorlage vorhanden ist. In
app.Map("/todo/{id}", (int id) => {});gehtidvon der Route aus. - Bindung über die Abfragezeichenfolge.
- Wenn der Parametername in der Routenvorlage vorhanden ist. In
- Wenn der Parametertyp ein durch die Abhängigkeitsinjektion bereitgestellter Dienst ist, wird dieser Dienst als Quelle verwendet.
- 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 obige Code:
- Konfiguriert die JSON-Standardoptionen sowohl für die Eingabe als auch für die Ausgabe.
- Gibt folgenden JSON-Code zurück:
Beim Posten{ "id": 1, "name": "Joe Smith" }{ "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.
Responses
Routenhandler unterstützen die folgenden Typen von Rückgabewerten:
-
IResult-basiert: Dies schließtTask<IResult>undValueTask<IResult>ein. -
string: Dies schließtTask<string>undValueTask<string>ein. -
T(ein beliebiger weiterer Typ): Dies schließtTask<T>undValueTask<T>ein.
| Rückgabewert | Behavior | 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://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
app.Run();
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Integrierte Ergebnisse
Die statische Klasse Microsoft.AspNetCore.Http.Results enthält allgemeine Ergebnishilfen.
| Description | Antworttyp | Statuscode | API |
|---|---|---|---|
| Schreiben einer JSON-Antwort mit erweiterten Optionen | application/json | 200 | Results.Json |
| Schreiben einer JSON-Antwort | application/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 | N/A | 404 | Results.NotFound |
| Festlegen des Statuscodes auf 204 | N/A | 204 | Results.NoContent |
| Festlegen des Statuscodes auf 422 mit optionaler JSON-Antwort | N/A | 422 | Results.UnprocessableEntity |
| Festlegen des Statuscodes auf 400 mit optionaler JSON-Antwort | N/A | 400 | Results.BadRequest |
| Festlegen des Statuscodes auf 409 mit optionaler JSON-Antwort | N/A | 409 | Results.Conflict |
| Schreiben eines JSON-Objekts mit Problemdetails in die Antwort | N/A | 500 (Standard), konfigurierbar | Results.Problem |
| Schreiben eines JSON-Objekts mit Problemdetails in die Antwort, mit Validierungsfehlern | N/A | N/A, 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();
Authorization
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.