Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Note
Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikel voor de huidige release.
Warning
Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie het .NET- en .NET Core-ondersteuningsbeleid voor meer informatie. Zie de .NET 9-versie van dit artikel voor de huidige release.
Important
Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.
Zie de .NET 9-versie van dit artikel voor de huidige release.
Dit document:
- Biedt een beknopt overzicht van minimale API's.
- Is bedoeld voor ervaren ontwikkelaars. Zie Zelfstudie: Een minimale API maken met ASP.NET Core voor een inleiding.
De minimale API's bestaan uit:
WebApplication
De volgende code wordt gegenereerd door een ASP.NET Core-sjabloon:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
De voorgaande code kan worden gemaakt via dotnet new web de opdrachtregel of door de lege websjabloon in Visual Studio te selecteren.
Met de volgende code wordt een WebApplication (app) gemaakt zonder expliciet een WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create initialiseert een nieuw exemplaar van de WebApplication klasse met vooraf geconfigureerde standaardwaarden.
WebApplication voegt automatisch de volgende middleware toe in Minimale API-toepassingen , afhankelijk van bepaalde voorwaarden:
-
UseDeveloperExceptionPagewordt eerst toegevoegd wanneer hetHostingEnvironmentis"Development". -
UseRoutingwordt als tweede toegevoegd als de gebruikerscodeUseRoutingnog niet is aangeroepen en als er geconfigureerde eindpunten zijn, bijvoorbeeldapp.MapGet. -
UseEndpointswordt toegevoegd aan het einde van de middleware-pijplijn als er eindpunten zijn geconfigureerd. -
UseAuthenticationwordt onmiddellijk toegevoegd naUseRoutingals de gebruikerscodeUseAuthenticationnog niet is aangeroepen en alsIAuthenticationSchemeProviderkan worden gedetecteerd in de serviceprovider.IAuthenticationSchemeProviderwordt standaard toegevoegd wanneer uAddAuthenticationgebruikt, en services worden gedetecteerd met behulp vanIServiceProviderIsService. -
UseAuthorizationwordt vervolgens toegevoegd als de gebruikerscode nog niet heeft aangeroepenUseAuthorizationen alsIAuthorizationHandlerProviderkan worden gedetecteerd in de serviceprovider.IAuthorizationHandlerProviderwordt standaard toegevoegd wanneer uAddAuthorizationgebruikt, en services worden gedetecteerd met behulp vanIServiceProviderIsService. - Door de gebruiker geconfigureerde middleware en eindpunten worden toegevoegd tussen
UseRoutingenUseEndpoints.
De volgende code is effectief wat de automatische middleware die aan de app wordt toegevoegd, produceert:
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 sommige gevallen is de standaard-middlewareconfiguratie niet juist voor de app en moet deze worden gewijzigd. Moet bijvoorbeeld UseCors worden aangeroepen voor UseAuthentication en UseAuthorization. De app moet UseAuthentication en UseAuthorization oproepen als UseCors wordt aangeroepen.
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Als middleware moet worden uitgevoerd voordat routekoppeling plaatsvindt, moet UseRouting worden aangeroepen en moet de middleware worden geplaatst voordat de aanroep naar UseRouting plaatsvindt.
UseEndpoints is in dit geval niet vereist, omdat deze automatisch wordt toegevoegd zoals eerder beschreven:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Wanneer u een terminal-middleware toevoegt:
- De middleware moet worden toegevoegd na
UseEndpoints. - De app moet
UseRoutingenUseEndpointsaanroepen zodat de terminal middleware op de juiste locatie kan worden geplaatst.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Terminal middleware is middleware die actief is wanneer er geen eindpunt de aanvraag afhandelt.
Werken met poorten
Wanneer een web-app wordt gemaakt met Visual Studio of dotnet new, wordt er een Properties/launchSettings.json bestand gemaakt waarin de poorten worden opgegeven waarop de app reageert. In de volgende voorbeelden van poortinstellingen retourneert het uitvoeren van de app vanuit Visual Studio een foutdialoogvenster Unable to connect to web server 'AppName'. Visual Studio retourneert een fout omdat deze de poort verwacht die is opgegeven in Properties/launchSettings.json, maar de app de poort gebruikt die is opgegeven door app.Run("http://localhost:3000"). Voer de volgende voorbeelden die de poort wijzigen uit vanuit de opdrachtregel.
In de volgende secties wordt de poort ingesteld waar de app op reageert.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
In de voorgaande code reageert de app op poort 3000.
Meerdere poorten
In de volgende code reageert de app op poort 3000 en 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
De poort instellen via de opdrachtregel
Met de volgende opdracht reageert de app op 7777poort:
dotnet run --urls="https://localhost:7777"
Als het Kestrel eindpunt ook in het appsettings.json bestand is geconfigureerd, wordt de opgegeven URL van het appsettings.json bestand gebruikt. Zie de eindpuntconfiguratie voor meer informatie Kestrel
De poort uit de omgevingsvariabelen lezen
Met de volgende code wordt de poort uit de omgeving gelezen:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
De voorkeursmethode voor het instellen van de poort vanuit de omgeving is het gebruik van de ASPNETCORE_URLS omgevingsvariabele, die wordt weergegeven in de volgende sectie.
De poorten instellen via de omgevingsvariabele ASPNETCORE_URLS
De ASPNETCORE_URLS omgevingsvariabele is beschikbaar om de poort in te stellen:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS ondersteunt meerdere URL's:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Luisteren op alle interfaces
In de volgende voorbeelden ziet u hoe u luistert op alle interfaces
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();
Luisteren op alle interfaces met behulp van ASPNETCORE_URLS
De voorgaande voorbeelden kunnen ASPNETCORE_URLS gebruiken
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Luisteren op alle interfaces met behulp van ASPNETCORE_HTTPS_PORTS
De voorgaande voorbeelden kunnen worden gebruikt ASPNETCORE_HTTPS_PORTS en ASPNETCORE_HTTP_PORTS.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Zie Eindpunten configureren voor de ASP.NET Core-webserver Kestrel voor meer informatie
HTTPS specificeren met ontwikkelingscertificaat
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Zie Het ASP.NET Core HTTPS-ontwikkelingscertificaat vertrouwen in Windows en macOS voor meer informatie over het ontwikkelingscertificaat.
HTTPS opgeven met behulp van een aangepast certificaat
In de volgende secties ziet u hoe u het aangepaste certificaat opgeeft met behulp van het appsettings.json bestand en via de configuratie.
Geef het aangepaste certificaat op met appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Het aangepaste certificaat opgeven via configuratie
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();
De certificaat-API's gebruiken
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();
De omgeving lezen
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();
Zie ASP.NET Core runtime-omgevingen voor meer informatie over het gebruik van de omgeving
Configuration
De volgende code leest uit het configuratiesysteem:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Zie Configuratie in ASP.NET Core- voor meer informatie
Logging
Met de volgende code wordt bij de start van de toepassing een bericht naar het logboek geschreven.
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Zie Logboekregistratie in .NET en ASP.NET Core voor meer informatie
Toegang tot de Dependency Injection-container (DI)
De volgende code laat zien hoe u services uit de DI-container kunt ophalen tijdens het opstarten van de toepassing:
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();
De volgende code laat zien hoe u toegang hebt tot sleutels uit de DI-container met behulp van het [FromKeyedServices] kenmerk:
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.";
}
Zie Afhankelijkheidsinjectie in ASP.NET Core voor meer informatie over DI.
WebApplicationBuilder
Deze sectie bevat voorbeeldcode met behulp van WebApplicationBuilder.
De inhoudsbasis, de naam van de toepassing en de omgeving wijzigen
De volgende code stelt de inhoudsmap, toepassingsnaam en omgeving in.
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 initialiseert een nieuw exemplaar van de WebApplicationBuilder-klasse met vooraf geconfigureerde standaardwaarden.
Zie ASP.NET Overzicht van basisbeginselen van Core voor meer informatie
De hoofdmap, app-naam en omgeving wijzigen met behulp van omgevingsvariabelen of opdrachtregel
In de volgende tabel ziet u de omgevingsvariabele en het opdrachtregelargument dat wordt gebruikt om de hoofdmap, de naam van de app en de omgeving te wijzigen:
| feature | Omgevingsvariabele | Opdrachtregelargument |
|---|---|---|
| Toepassingsnaam | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Naam van de omgeving | ASPNETCORE_ENVIRONMENT | --environment |
| Hoofdmap van inhoud | ASPNETCORE_CONTENTROOT | --contentRoot |
Configuratieproviders toevoegen
In het volgende voorbeeld wordt de INI-configuratieprovider toegevoegd:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Zie Bestandsconfiguratieproviders in Configuratie in ASP.NET Core voor gedetailleerde informatie.
Leesconfiguratie
Standaard leest de WebApplicationBuilder de configuratie uit meerdere bronnen, waaronder:
-
appSettings.jsonenappSettings.{environment}.json - Omgevingsvariabelen
- De opdrachtregel
Zie De standaardconfiguratiein Configuratie in ASP.NET Core voor een volledige lijst met configuratiebronnen die worden gelezen.
De volgende code leest HelloKey uit de configuratie en geeft de waarde weer op het / eindpunt. Als de configuratiewaarde null is, wordt 'Hallo' toegewezen aan message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
De omgeving lezen
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine($"Running in development.");
}
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Logboekregistratieproviders toevoegen
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();
Services toevoegen
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();
De IHostBuilder aanpassen
Bestaande uitbreidingsmethoden IHostBuilder kunnen worden geopend met behulp van de eigenschap Host:
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();
De IWebHostBuilder aanpassen
Extensiemethoden zijn IWebHostBuilder toegankelijk via de eigenschap WebApplicationBuilder.WebHost .
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();
De webroot wijzigen
De webroot is standaard relatief ten opzichte van de inhoudsmap in de map wwwroot. De webroot is waar de Static File Middleware zoekt naar statische bestanden. Webhoofdmap kan worden gewijzigd met WebHostOptions, de opdrachtregel of met de UseWebRoot methode:
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Afhankelijkheidsinjectiecontainer op maat (DI)
In het volgende voorbeeld wordt Autofac gebruikt:
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();
Middleware toevoegen
Alle bestaande ASP.NET Core-middleware kan worden geconfigureerd op WebApplication.
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Zie ASP.NET Core Middleware voor meer informatie
Uitzonderingspagina voor ontwikkelaars
WebApplication.CreateBuilder initialiseert een nieuw exemplaar van de WebApplicationBuilder klasse met vooraf geconfigureerde standaardwaarden. De uitzonderingspagina voor ontwikkelaars is ingeschakeld in de vooraf geconfigureerde standaardinstellingen. Wanneer de volgende code wordt uitgevoerd in de ontwikkelomgeving, wordt er genavigeerd naar een vriendelijke pagina die de uitzondering toont.
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
De volgende tabel bevat enkele van de middleware die vaak wordt gebruikt met Minimale API's.
| Middleware | Description | API |
|---|---|---|
| Authentication | Biedt ondersteuning voor verificatie. | UseAuthentication |
| Authorization | Biedt autorisatieondersteuning. | UseAuthorization |
| CORS | Hiermee wordt Cross-Origin Resource Sharing geconfigureerd. | UseCors |
| Uitzonderingshandler | Wereldwijd worden uitzonderingen verwerkt die worden gegenereerd door de middleware-pijplijn. | UseExceptionHandler |
| Doorgestuurde headers | Hiermee worden via een proxy ontvangen headers doorgestuurd naar het huidige verzoek. | UseForwardedHeaders |
| HTTPS-omleiding | Alle HTTP-aanvragen worden omgeleid naar HTTPS. | UseHttpsRedirection |
| HTTP Strict Transport Security (HSTS) | Middleware voor beveiligingsverbeteringen waarmee een speciale antwoordheader wordt toegevoegd. | UseHsts |
| Logboekregistratie aanvragen | Biedt ondersteuning voor het registreren van HTTP-aanvragen en -antwoorden. | UseHttpLogging |
| Time-outs aanvragen | Biedt ondersteuning voor het configureren van time-outs voor aanvragen, globale standaardinstellingen en per eindpunt. | UseRequestTimeouts |
| Logboekregistratie van W3C-aanvragen | Biedt ondersteuning voor het registreren van HTTP-aanvragen en -antwoorden in de W3C-indeling. | UseW3CLogging |
| Antwoordcaching | Biedt ondersteuning voor het opslaan van antwoorden in cache. | UseResponseCaching |
| Antwoordcompressie | Biedt ondersteuning voor het comprimeren van antwoorden. | UseResponseCompression |
| Session | Biedt ondersteuning voor het beheren van gebruikerssessies. | UseSession |
| Statische bestanden | Biedt ondersteuning voor het leveren van statische bestanden en bladeren door mappen. | UseStaticFiles, UseFileServer |
| WebSockets | Hiermee schakelt u het WebSockets-protocol in. | UseWebSockets |
In de volgende secties worden de verwerking van aanvragen behandeld: routering, parameterbinding en antwoorden.
Routing
Een geconfigureerde WebApplication ondersteuning Map{Verb} en MapMethods waar {Verb} is een camel-cased HTTP-methode zoals Get, Post, Putof Delete:
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();
De Delegate argumenten die aan deze methoden worden doorgegeven, worden 'routehandlers' genoemd.
Route-handlers
Route-handlers zijn methoden die worden uitgevoerd wanneer de route overeenkomt. Route-handlers kunnen een lambda-expressie, een lokale functie, een instantiemethode of een statische methode zijn. Route-handlers kunnen synchroon of asynchroon zijn.
Lambda-expressie
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 functie
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Exemplaarmethode
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";
}
}
Statische methode
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", HelloHandler.Hello);
app.Run();
class HelloHandler
{
public static string Hello()
{
return "Hello static method";
}
}
Eindpunt gedefinieerd buiten Program.cs
Minimale API's hoeven zich niet in Program.cste bevinden.
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" });
});
}
}
Zie ook Routegroepen verderop in dit artikel.
Benoemde eindpunten en het genereren van koppelingen
Eindpunten kunnen namen krijgen om URL's naar het eindpunt te genereren. Als u een benoemd eindpunt gebruikt, hoeft u geen vaste codepaden in een app te gebruiken:
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();
De voorgaande code geeft The link to the hello route is /hello weer vanaf het / eindpunt.
OPMERKING: Eindpuntnamen zijn hoofdlettergevoelig.
Namen van eindpunten:
- De naam moet wereldwijd uniek zijn.
- Worden gebruikt als de OpenAPI-bewerkings-id wanneer OpenAPI-ondersteuning is ingeschakeld. Zie OpenAPI voor meer informatie.
Routeparameters
Routeparameters kunnen worden vastgelegd als onderdeel van de definitie van het routepatroon:
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();
De voorgaande code retourneert The user id is 3 and book id is 7 van de URI /users/3/books/7.
De route-handler kan de parameters declareren die moeten worden vastgelegd. Wanneer een aanvraag wordt ingediend bij een route met parameters die zijn gedeclareerd om vast te leggen, worden de parameters geparseerd en doorgegeven aan de handler. Hierdoor kunt u de waarden eenvoudig op een veilige manier vastleggen. In de voorgaande code zijn userId en bookId beide int.
Als in de voorgaande code een van beide routewaarden niet naar een intkan worden geconverteerd, wordt er een uitzondering gegenereerd. De GET-aanvraag /users/hello/books/3 genereert de volgende uitzondering:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Jokertekens en alle routes vangen
De volgende catch-all route retourneert Routing to hello van het eindpunt '/posts/hello':
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Routebeperkingen
Routebeperkingen beperken het overeenkomende gedrag van een route.
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();
In de volgende tabel ziet u de voorgaande routesjablonen en hun gedrag:
| Routesjabloon | Voorbeeld van overeenkomende URI |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Zie de naslaginformatie over routebeperkingenin Routering in ASP.NET Core voor meer informatie.
Routegroepen
Met de MapGroup-extensiemethode kunt u groepen eindpunten organiseren met een gemeenschappelijk voorvoegsel. Het vermindert terugkerende code en maakt het mogelijk om hele groepen eindpunten aan te passen met één aanroep naar methoden zoals RequireAuthorization en WithMetadata waarmee metagegevens van eindpuntenworden toegevoegd.
Met de volgende code worden bijvoorbeeld twee vergelijkbare groepen eindpunten gemaakt:
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 dit scenario kunt u een relatief adres gebruiken voor de Location-header in het 201 Created resultaat:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
De eerste groep eindpunten komt alleen overeen met aanvragen die zijn voorafgegaan door /public/todos en zijn toegankelijk zonder verificatie. De tweede groep eindpunten komt alleen overeen met aanvragen die zijn voorafgegaan door /private/todos en vereisen verificatie.
De QueryPrivateTodoseindpuntfilterfactory is een lokale functie waarmee de TodoDb parameters van de route-handler worden gewijzigd om toegang te krijgen tot privétodogegevens en deze op te slaan.
Routegroepen ondersteunen ook geneste groepen en complexe voorvoegselpatronen met routeparameters en beperkingen. In het volgende voorbeeld kan de routehandler die is toegewezen aan de user groep, de {org} en {group} routeparameters vastleggen die zijn gedefinieerd in de voorvoegsels van de buitenste groep.
Het voorvoegsel kan ook leeg zijn. Dit kan handig zijn voor het toevoegen van eindpuntmetagegevens of filters aan een groep eindpunten zonder het routepatroon te wijzigen.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Het toevoegen van filters of metagegevens aan een groep gedraagt zich op dezelfde manier als het afzonderlijk toevoegen van filters of metagegevens aan elk eindpunt voordat u extra filters of metagegevens toevoegt die mogelijk zijn toegevoegd aan een binnenste groep of specifiek eindpunt.
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);
});
In het bovenstaande voorbeeld registreert het buitenste filter de binnenkomende aanvraag voordat het binnenste filter de kans krijgt om dat te doen, hoewel het binnenste filter als tweede is toegevoegd. Omdat de filters zijn toegepast op verschillende groepen, maakt de volgorde die ze ten opzichte van elkaar zijn toegevoegd niet uit. De volgorde waarin filters worden toegevoegd, maakt uit of ze op dezelfde groep of een specifiek eindpunt worden toegepast.
Een aanvraag voor /outer/inner/ meldt het volgende:
/outer group filter
/inner group filter
MapGet filter
Parameterbinding
Parameterbinding is het proces van het converteren van aanvraaggegevens naar sterk getypte parameters die worden uitgedrukt door route-handlers. Een bindingsbron bepaalt waar parameters van afhankelijk zijn. Bindingsbronnen kunnen expliciet of afgeleid zijn op basis van de HTTP-methode en het parametertype.
Ondersteunde bindingsbronnen:
- Routewaarden
- Querystring
- Header
- Hoofdtekst (als JSON)
- Formulierwaarden
- Services geleverd door afhankelijkheidsinjectie
- Custom
De volgende GET-route-handler maakt gebruik van enkele van deze parameterbindingsbronnen:
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 { }
Bindingsfuncties voor sleutelparameters
-
Expliciete binding: gebruik kenmerken zoals
[FromRoute],[FromQuery],[FromHeader], ,[FromBody]en[FromForm][FromServices]om expliciet bindingsbronnen op te geven. -
Formulierbinding: Formulierwaarden binden met een
[FromForm]kenmerk, inclusief ondersteuning voorIFormFileenIFormFileCollectionvoor bestandsuploads. - Complexe typen: Binden aan verzamelingen en complexe typen uit formulieren, queryreeksen en headers.
-
Aangepaste binding: implementeer aangepaste bindingslogica met behulp van
TryParse,BindAsyncof deIBindableFromHttpContext<T>interface. - Optionele parameters: ondersteuning voor null-typen en standaardwaarden voor optionele parameters.
- Afhankelijkheidsinjectie: Parameters zijn automatisch afhankelijk van services die zijn geregistreerd in de DI-container.
-
Speciale typen: Automatische binding voor
HttpContext, ,HttpRequestHttpResponse,CancellationToken, ,ClaimsPrincipal, , ,StreamenPipeReader.
Meer informatie: Zie Parameterbinding in Minimale API-toepassingen voor gedetailleerde informatie over parameterbinding, waaronder geavanceerde scenario's, validatie, bindingsprioriteit en probleemoplossing.
Json+PipeReader deserialisatie in minimale API's
Vanaf .NET 10 maken de volgende functionele gebieden van ASP.NET Core gebruik van overloads van JsonSerializer.DeserializeAsync gebaseerd op PipeReader in plaats van Stream.
- Minimale API's (parameterbinding, hoofdtekst van leesaanvraag)
- MVC (invoerindelingen, model)
- De HttpRequestJsonExtensions extensiemethoden voor het lezen van de verzoekbody als JSON.
Voor de meeste toepassingen biedt een overgang van Stream naar PipeReader betere prestaties zonder dat hiervoor wijzigingen in de toepassingscode nodig zijn. Maar als uw toepassing een aangepast conversieprogramma heeft, kan het conversieprogramma Utf8JsonReader.HasValueSequence mogelijk niet correct verwerken. Als dit niet het probleem is, kan het resultaat fouten zijn, zoals ArgumentOutOfRangeException of ontbrekende gegevens bij het deserialiseren. U hebt de volgende opties om uw conversieprogramma te laten werken zonder PipeReader-gerelateerde fouten.
Optie 1: Tijdelijke tijdelijke oplossing
De snelle tijdelijke oplossing is terug te gaan naar het gebruik van Stream zonder PipeReader-ondersteuning. Als u deze optie wilt implementeren, stelt u de AppContext-switch Microsoft.AspNetCore.UseStreamBasedJsonParsing in op 'true'. We raden u aan dit alleen te doen als tijdelijke tijdelijke oplossing en uw conversieprogramma zo snel mogelijk bij te werken ter ondersteuning HasValueSequence . De switch kan worden verwijderd in .NET 11. Het enige doel was ontwikkelaars tijd te geven om hun conversieprogramma's bijgewerkt te krijgen.
Optie 2: Een snelle oplossing voor JsonConverter implementaties
Voor deze oplossing alloceert u een array vanuit de ReadOnlySequence. In dit voorbeeld ziet u hoe de code eruit zou zien:
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
// previous code
}
Optie 3: Een gecompliceerder maar beter presterende oplossing
Deze oplossing omvat het instellen van een afzonderlijk codepad voor de ReadOnlySequence verwerking:
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
}
}
Zie voor meer informatie
Ondersteuning voor validatie in minimale API's
Als u validatie inschakelt, kan de ASP.NET Core-runtime validaties uitvoeren die zijn gedefinieerd op de volgende:
- Query
- Header
- Inhoud van het verzoek
Validaties worden gedefinieerd met behulp van attributen in de DataAnnotations namespace.
Wanneer een parameter voor een minimaal API-eindpunt een klasse of recordtype is, worden validatiekenmerken automatisch toegepast. Voorbeeld:
public record Product(
[Required] string Name,
[Range(1, 1000)] int Quantity);
Ontwikkelaars passen het gedrag van het validatiesysteem aan door:
- Aangepaste
[Validation]kenmerk implementaties maken. - Het implementeren van de
IValidatableObjectinterface voor complexe validatielogica.
Als de validatie mislukt, retourneert de runtime een antwoord van 400 - Ongeldige aanvraag met details van de validatiefouten.
Ingebouwde validatieondersteuning inschakelen voor minimale API's
Schakel de ingebouwde validatieondersteuning voor minimale API's in door de AddValidation extensiemethode aan te roepen om de vereiste services te registreren in de servicecontainer voor uw toepassing:
builder.Services.AddValidation();
De implementatie detecteert automatisch typen die zijn gedefinieerd in minimale API-handlers of als basistypen die zijn gedefinieerd in minimale API-handlers. Een eindpuntfilter voert validatie uit op deze types en wordt toegevoegd voor elk eindpunt.
Validatie kan voor specifieke eindpunten worden uitgeschakeld met behulp van de DisableValidation extensiemethode, zoals in het volgende voorbeeld:
app.MapPost("/products",
([EvenNumber(ErrorMessage = "Product ID must be even")] int productId, [Required] string name)
=> TypedResults.Ok(productId))
.DisableValidation();
Validatiefoutantwoorden aanpassen met IProblemDetailsService
Pas foutreacties van minimale API-validatielogica aan met een IProblemDetailsService implementatie. Registreer deze service in de serviceverzameling van uw toepassing om consistentere en gebruikersspecifieke foutreacties mogelijk te maken. Ondersteuning voor minimale API-validatie is geĂŻntroduceerd in ASP.NET Core in .NET 10.
Aangepaste validatiefoutreacties implementeren:
- De standaard implementatie implementeren IProblemDetailsService of gebruiken
- De service registreren in de DI-container
- Het validatiesysteem maakt automatisch gebruik van de geregistreerde service om validatiefoutreacties op te maken
Zie Antwoorden maken in minimale API-toepassingen voor meer informatie over het aanpassen van validatiefoutreacties met IProblemDetailsService.
Responses
Route-handlers ondersteunen de volgende typen retourwaarden:
-
IResultgebaseerd - Dit omvatTask<IResult>enValueTask<IResult> -
string- Dit omvatTask<string>enValueTask<string> -
T(Elk ander type): dit omvatTask<T>enValueTask<T>
| Retourwaarde | Behavior | Content-Type |
|---|---|---|
IResult |
Het framework roept IResult.ExecuteAsync aan | Besloten door de IResult implementatie |
string |
Het raamwerk schrijft de string rechtstreeks naar de respons | text/plain |
T (Elk ander type) |
Het framework JSON-serialiseert het antwoord | application/json |
Zie Antwoorden maken in minimale API-toepassingen voor een uitgebreidere handleiding voor het routeren van handler-retourwaarden
Voorbeeld van retourwaarden
retourwaarden van string
app.MapGet("/hello", () => "Hello World");
JSON-retourwaarden
app.MapGet("/hello", () => new { Message = "Hello World" });
Typgeresultaten retourneren
De volgende code retourneert een TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Het retourneren van TypedResults heeft de voorkeur boven het retourneren van Results. Voor meer informatie, zie TypedResults versus Results.
IResult retourwaarden
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
In het volgende voorbeeld worden de ingebouwde resultaattypen gebruikt om het antwoord aan te passen:
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" }));
Aangepaste 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");
});
Zie Antwoorden maken in Minimale API-toepassingen voor meer voorbeelden.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Ingebouwde resultaten
Algemene helpers voor resultaten bestaan in de Results en TypedResults statische klassen. Het retourneren van TypedResults heeft de voorkeur boven het retourneren van Results. Voor meer informatie, zie TypedResults versus Results.
Kopteksten wijzigen
Gebruik het HttpResponse-object om antwoordheaders te wijzigen:
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";
});
Resultaten aanpassen
Toepassingen kunnen antwoorden beheren door een aangepast IResult type te implementeren. De volgende code is een voorbeeld van een HTML-resultaattype:
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);
}
}
U wordt aangeraden een extensiemethode toe te voegen aan Microsoft.AspNetCore.Http.IResultExtensions om deze aangepaste resultaten beter te detecteren.
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();
Getypte resultaten
De IResult interface kan waarden vertegenwoordigen die worden geretourneerd uit minimale API's die geen gebruikmaken van de impliciete ondersteuning voor JSON die het geretourneerde object serialiseren naar het HTTP-antwoord. De statische resultaten klasse wordt gebruikt om verschillende IResult objecten te maken die verschillende typen antwoorden vertegenwoordigen. Stel bijvoorbeeld de antwoordstatuscode in of omleiden naar een andere URL.
De typen die worden geĂŻmplementeerd IResult , zijn openbaar, waardoor typeverklaringen kunnen worden gebruikt bij het testen. Voorbeeld:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
U kunt de retourtypen van de bijbehorende methoden in de klasse Static TypedResults bekijken om het juiste openbare IResult type te vinden waarnaar moet worden gecast.
Zie Antwoorden maken in Minimale API-toepassingen voor meer voorbeelden.
Filters
Zie Filters in Minimale API-apps voor meer informatie.
Authorization
Routes kunnen worden beveiligd met autorisatiebeleid. Deze kunnen worden gedeclareerd via het [Authorize] kenmerk of met behulp van de RequireAuthorization methode:
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();
De voorgaande code kan worden geschreven met RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
In het volgende voorbeeld wordt gebruikgemaakt van autorisatie op basis van beleid:
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();
Niet-geverifieerde gebruikers toegang geven tot een eindpunt
Hiermee [AllowAnonymous] hebben niet-geverifieerde gebruikers toegang tot eindpunten:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Routes kunnen CORS-ondersteuning krijgen door gebruik te maken van CORS-beleid. CORS kan worden gedeclareerd via het [EnableCors] kenmerk of met behulp van de RequireCors methode. Met de volgende voorbeelden schakelt u CORS in:
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();
Zie CORS (Cross-Origin Requests) inschakelen in ASP.NET Core voor meer informatie
ValidateScopes en ValidateOnBuild
ValidateScopes en ValidateOnBuild zijn standaard ingeschakeld in de ontwikkelomgeving , maar uitgeschakeld in andere omgevingen.
Wanneer ValidateOnBuild is true, valideert de DI-container de serviceconfiguratie tijdens de build. Als de serviceconfiguratie ongeldig is, mislukt de build bij het opstarten van de app in plaats van tijdens runtime wanneer de service wordt aangevraagd.
Wanneer ValidateScopes is true, valideert de DI-container dat een scoped service niet wordt opgevraagd uit het hoofdbereik. Het oplossen van een dienst binnen een bereik vanuit het basisbereik kan leiden tot een geheugenlek omdat de dienst langer in het geheugen wordt bewaard dan het aanvraagbereik.
ValidateScopes en ValidateOnBuild zijn standaard onwaar in niet-ontwikkelingsmodi's voor prestatieoverwegingen.
In de volgende code ziet u dat deze ValidateScopes standaard is ingeschakeld in de ontwikkelingsmodus, maar uitgeschakeld in de releasemodus:
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 { }
In de volgende code ziet u dat deze ValidateOnBuild standaard is ingeschakeld in de ontwikkelingsmodus, maar uitgeschakeld in de releasemodus:
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 { }
De volgende code schakelt ValidateScopes en ValidateOnBuild uit 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;
});
}
Zie ook
- Minimal APIs beknopte naslaggids
- OpenAPI-documenten genereren
- Antwoorden maken in minimale API-toepassingen
- Filters in Minimal API-apps
- Fouten in ASP.NET Core-API's verwerken
- Authenticatie en autorisatie in minimale API's
- Minimale API-apps testen
- Kortsluitingsroutering
- Identity API-eindpunten
- Ondersteuning voor serviceafhankelijkheidsinjectiecontainer met sleutel
- Een kijkje achter de schermen van minimale API-eindpunten
- Minimale ASP.NET Core-API's ordenen
- Discussie over Fluent Validation op GitHub
Dit document:
- Biedt een beknopt overzicht voor minimale API's.
- Is bedoeld voor ervaren ontwikkelaars. Zie Zelfstudie: Een minimale API maken met ASP.NET Core voor een inleiding.
De minimale API's bestaan uit:
WebApplication
De volgende code wordt gegenereerd door een ASP.NET Core-sjabloon:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
De voorgaande code kan worden gemaakt via dotnet new web de opdrachtregel of door de lege websjabloon in Visual Studio te selecteren.
Met de volgende code wordt een WebApplication (app) gemaakt zonder expliciet een WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create initialiseert een nieuw exemplaar van de WebApplication klasse met vooraf geconfigureerde standaardwaarden.
WebApplication voegt automatisch de volgende middleware toe in Minimale API-toepassingen , afhankelijk van bepaalde voorwaarden:
-
UseDeveloperExceptionPagewordt eerst toegevoegd wanneer hetHostingEnvironmentis"Development". -
UseRoutingwordt als tweede toegevoegd als de gebruikerscodeUseRoutingnog niet is aangeroepen en als er geconfigureerde eindpunten zijn, bijvoorbeeldapp.MapGet. -
UseEndpointswordt toegevoegd aan het einde van de middleware-pijplijn als er eindpunten zijn geconfigureerd. -
UseAuthenticationwordt onmiddellijk toegevoegd naUseRoutingals de gebruikerscodeUseAuthenticationnog niet is aangeroepen en alsIAuthenticationSchemeProviderkan worden gedetecteerd in de serviceprovider.IAuthenticationSchemeProviderwordt standaard toegevoegd wanneer uAddAuthenticationgebruikt, en services worden gedetecteerd met behulp vanIServiceProviderIsService. -
UseAuthorizationwordt vervolgens toegevoegd als de gebruikerscode nog niet heeft aangeroepenUseAuthorizationen alsIAuthorizationHandlerProviderkan worden gedetecteerd in de serviceprovider.IAuthorizationHandlerProviderwordt standaard toegevoegd wanneer uAddAuthorizationgebruikt, en services worden gedetecteerd met behulp vanIServiceProviderIsService. - Door de gebruiker geconfigureerde middleware en eindpunten worden toegevoegd tussen
UseRoutingenUseEndpoints.
De volgende code is effectief wat de automatische middleware die aan de app wordt toegevoegd, produceert:
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 sommige gevallen is de standaard-middlewareconfiguratie niet juist voor de app en moet deze worden gewijzigd. Moet bijvoorbeeld UseCors worden aangeroepen voor UseAuthentication en UseAuthorization. De app moet UseAuthentication en UseAuthorization oproepen als UseCors wordt aangeroepen.
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Als middleware moet worden uitgevoerd voordat routekoppeling plaatsvindt, moet UseRouting worden aangeroepen en moet de middleware worden geplaatst voordat de aanroep naar UseRouting plaatsvindt.
UseEndpoints is in dit geval niet vereist, omdat deze automatisch wordt toegevoegd zoals eerder beschreven:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Wanneer u een terminal-middleware toevoegt:
- De middleware moet worden toegevoegd na
UseEndpoints. - De app moet
UseRoutingenUseEndpointsaanroepen zodat de terminal middleware op de juiste locatie kan worden geplaatst.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Terminal middleware is middleware die actief is wanneer er geen eindpunt de aanvraag afhandelt.
Werken met poorten
Wanneer een web-app wordt gemaakt met Visual Studio of dotnet new, wordt er een Properties/launchSettings.json bestand gemaakt waarin de poorten worden opgegeven waarop de app reageert. In de volgende voorbeelden van poortinstellingen retourneert het uitvoeren van de app vanuit Visual Studio een foutdialoogvenster Unable to connect to web server 'AppName'. Visual Studio retourneert een fout omdat deze de poort verwacht die is opgegeven in Properties/launchSettings.json, maar de app de poort gebruikt die is opgegeven door app.Run("http://localhost:3000"). Voer de volgende voorbeelden die de poort wijzigen uit vanuit de opdrachtregel.
In de volgende secties wordt de poort ingesteld waar de app op reageert.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
In de voorgaande code reageert de app op poort 3000.
Meerdere poorten
In de volgende code reageert de app op poort 3000 en 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
De poort instellen via de opdrachtregel
Met de volgende opdracht reageert de app op 7777poort:
dotnet run --urls="https://localhost:7777"
Als het Kestrel eindpunt ook in het appsettings.json bestand is geconfigureerd, wordt de opgegeven URL van het appsettings.json bestand gebruikt. Zie de eindpuntconfiguratie voor meer informatie Kestrel
De poort uit de omgevingsvariabelen lezen
Met de volgende code wordt de poort uit de omgeving gelezen:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
De voorkeursmethode voor het instellen van de poort vanuit de omgeving is het gebruik van de ASPNETCORE_URLS omgevingsvariabele, die wordt weergegeven in de volgende sectie.
De poorten instellen via de omgevingsvariabele ASPNETCORE_URLS
De ASPNETCORE_URLS omgevingsvariabele is beschikbaar om de poort in te stellen:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS ondersteunt meerdere URL's:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Luisteren op alle interfaces
In de volgende voorbeelden ziet u hoe u luistert op alle interfaces
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();
Luisteren op alle interfaces met behulp van ASPNETCORE_URLS
De voorgaande voorbeelden kunnen ASPNETCORE_URLS gebruiken
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Luisteren op alle interfaces met behulp van ASPNETCORE_HTTPS_PORTS
De voorgaande voorbeelden kunnen worden gebruikt ASPNETCORE_HTTPS_PORTS en ASPNETCORE_HTTP_PORTS.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Zie Eindpunten configureren voor de ASP.NET Core-webserver Kestrel voor meer informatie
HTTPS specificeren met ontwikkelingscertificaat
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Zie Het ASP.NET Core HTTPS-ontwikkelingscertificaat vertrouwen in Windows en macOS voor meer informatie over het ontwikkelingscertificaat.
HTTPS opgeven met behulp van een aangepast certificaat
In de volgende secties ziet u hoe u het aangepaste certificaat opgeeft met behulp van het appsettings.json bestand en via de configuratie.
Geef het aangepaste certificaat op met appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Het aangepaste certificaat opgeven via configuratie
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();
De certificaat-API's gebruiken
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();
De omgeving lezen
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();
Zie ASP.NET Core runtime-omgevingen voor meer informatie over het gebruik van de omgeving
Configuration
De volgende code leest uit het configuratiesysteem:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Zie Configuratie in ASP.NET Core- voor meer informatie
Logging
Met de volgende code wordt bij de start van de toepassing een bericht naar het logboek geschreven.
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Zie Logboekregistratie in .NET en ASP.NET Core voor meer informatie
Toegang tot de Dependency Injection-container (DI)
De volgende code laat zien hoe u services uit de DI-container kunt ophalen tijdens het opstarten van de toepassing:
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();
De volgende code laat zien hoe u toegang hebt tot sleutels uit de DI-container met behulp van het [FromKeyedServices] kenmerk:
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.";
}
Zie Afhankelijkheidsinjectie in ASP.NET Core voor meer informatie over DI.
WebApplicationBuilder
Deze sectie bevat voorbeeldcode met behulp van WebApplicationBuilder.
De inhoudsbasis, de naam van de toepassing en de omgeving wijzigen
De volgende code stelt de inhoudsmap, toepassingsnaam en omgeving in.
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 initialiseert een nieuw exemplaar van de WebApplicationBuilder-klasse met vooraf geconfigureerde standaardwaarden.
Zie ASP.NET Overzicht van basisbeginselen van Core voor meer informatie
De hoofdmap, app-naam en omgeving wijzigen met behulp van omgevingsvariabelen of opdrachtregel
In de volgende tabel ziet u de omgevingsvariabele en het opdrachtregelargument dat wordt gebruikt om de hoofdmap, de naam van de app en de omgeving te wijzigen:
| feature | Omgevingsvariabele | Opdrachtregelargument |
|---|---|---|
| Toepassingsnaam | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Naam van de omgeving | ASPNETCORE_ENVIRONMENT | --environment |
| Hoofdmap van inhoud | ASPNETCORE_CONTENTROOT | --contentRoot |
Configuratieproviders toevoegen
In het volgende voorbeeld wordt de INI-configuratieprovider toegevoegd:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Zie Bestandsconfiguratieproviders in Configuratie in ASP.NET Core voor gedetailleerde informatie.
Leesconfiguratie
Standaard leest de WebApplicationBuilder de configuratie uit meerdere bronnen, waaronder:
-
appSettings.jsonenappSettings.{environment}.json - Omgevingsvariabelen
- De opdrachtregel
Zie De standaardconfiguratiein Configuratie in ASP.NET Core voor een volledige lijst met configuratiebronnen die worden gelezen.
De volgende code leest HelloKey uit de configuratie en geeft de waarde weer op het / eindpunt. Als de configuratiewaarde null is, wordt 'Hallo' toegewezen aan message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
De omgeving lezen
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine($"Running in development.");
}
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Logboekregistratieproviders toevoegen
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();
Services toevoegen
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();
De IHostBuilder aanpassen
Bestaande uitbreidingsmethoden IHostBuilder kunnen worden geopend met behulp van de eigenschap Host:
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();
De IWebHostBuilder aanpassen
Extensiemethoden zijn IWebHostBuilder toegankelijk via de eigenschap WebApplicationBuilder.WebHost .
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();
De webroot wijzigen
De webroot is standaard relatief ten opzichte van de inhoudsmap in de map wwwroot. De webroot is waar de Static File Middleware zoekt naar statische bestanden. Webhoofdmap kan worden gewijzigd met WebHostOptions, de opdrachtregel of met de UseWebRoot methode:
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Afhankelijkheidsinjectiecontainer op maat (DI)
In het volgende voorbeeld wordt Autofac gebruikt:
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();
Middleware toevoegen
Alle bestaande ASP.NET Core-middleware kan worden geconfigureerd op WebApplication.
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Zie ASP.NET Core Middleware voor meer informatie
Uitzonderingspagina voor ontwikkelaars
WebApplication.CreateBuilder initialiseert een nieuw exemplaar van de WebApplicationBuilder klasse met vooraf geconfigureerde standaardwaarden. De uitzonderingspagina voor ontwikkelaars is ingeschakeld in de vooraf geconfigureerde standaardinstellingen. Wanneer de volgende code wordt uitgevoerd in de ontwikkelomgeving, wordt er genavigeerd naar een vriendelijke pagina die de uitzondering toont.
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
De volgende tabel bevat enkele van de middleware die vaak wordt gebruikt met minimale API's.
| Middleware | Description | API |
|---|---|---|
| Authentication | Biedt ondersteuning voor verificatie. | UseAuthentication |
| Authorization | Biedt autorisatieondersteuning. | UseAuthorization |
| CORS | Hiermee wordt Cross-Origin Resource Sharing geconfigureerd. | UseCors |
| Uitzonderingshandler | Wereldwijd worden uitzonderingen verwerkt die worden gegenereerd door de middleware-pijplijn. | UseExceptionHandler |
| Doorgestuurde headers | Hiermee worden via een proxy ontvangen headers doorgestuurd naar het huidige verzoek. | UseForwardedHeaders |
| HTTPS-omleiding | Alle HTTP-aanvragen worden omgeleid naar HTTPS. | UseHttpsRedirection |
| HTTP Strict Transport Security (HSTS) | Middleware voor beveiligingsverbeteringen waarmee een speciale antwoordheader wordt toegevoegd. | UseHsts |
| Logboekregistratie aanvragen | Biedt ondersteuning voor het registreren van HTTP-aanvragen en -antwoorden. | UseHttpLogging |
| Time-outs aanvragen | Biedt ondersteuning voor het configureren van time-outs voor aanvragen, globale standaardinstellingen en per eindpunt. | UseRequestTimeouts |
| Logboekregistratie van W3C-aanvragen | Biedt ondersteuning voor het registreren van HTTP-aanvragen en -antwoorden in de W3C-indeling. | UseW3CLogging |
| Antwoordcaching | Biedt ondersteuning voor het opslaan van antwoorden in cache. | UseResponseCaching |
| Antwoordcompressie | Biedt ondersteuning voor het comprimeren van antwoorden. | UseResponseCompression |
| Session | Biedt ondersteuning voor het beheren van gebruikerssessies. | UseSession |
| Statische bestanden | Biedt ondersteuning voor het leveren van statische bestanden en bladeren door mappen. | UseStaticFiles, UseFileServer |
| WebSockets | Hiermee schakelt u het WebSockets-protocol in. | UseWebSockets |
In de volgende secties worden de verwerking van aanvragen behandeld: routering, parameterbinding en antwoorden.
Routing
Een geconfigureerde WebApplication ondersteunt Map{Verb} en MapMethods waar {Verb} een camelCase HTTP-methode is zoals Get, Post, Put of Delete:
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();
De Delegate argumenten die aan deze methoden worden doorgegeven, worden 'routehandlers' genoemd.
Route-handlers
Route-handlers zijn methoden die worden uitgevoerd wanneer de route overeenkomt. Route-handlers kunnen een lambda-expressie, een lokale functie, een instantiemethode of een statische methode zijn. Route-handlers kunnen synchroon of asynchroon zijn.
Lambda-expressie
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 functie
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Exemplaarmethode
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";
}
}
Statische methode
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", HelloHandler.Hello);
app.Run();
class HelloHandler
{
public static string Hello()
{
return "Hello static method";
}
}
Eindpunt gedefinieerd buiten Program.cs
Minimale API's hoeven zich niet in Program.cste bevinden.
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" });
});
}
}
Zie ook Routegroepen verderop in dit artikel.
Benoemde eindpunten en het genereren van koppelingen
Eindpunten kunnen namen krijgen om URL's naar het eindpunt te genereren. Als u een benoemd eindpunt gebruikt, hoeft u geen vaste codepaden in een app te gebruiken:
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();
De voorgaande code geeft The link to the hello route is /hello weer vanaf het / eindpunt.
OPMERKING: Eindpuntnamen zijn hoofdlettergevoelig.
Namen van eindpunten:
- De naam moet wereldwijd uniek zijn.
- Worden gebruikt als de OpenAPI-bewerkings-id wanneer OpenAPI-ondersteuning is ingeschakeld. Zie OpenAPI voor meer informatie.
Routeparameters
Routeparameters kunnen worden vastgelegd als onderdeel van de definitie van het routepatroon:
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();
De voorgaande code retourneert The user id is 3 and book id is 7 van de URI /users/3/books/7.
De route-handler kan de parameters declareren die moeten worden vastgelegd. Wanneer een aanvraag wordt ingediend bij een route met parameters die zijn gedeclareerd om vast te leggen, worden de parameters geparseerd en doorgegeven aan de handler. Hierdoor kunt u de waarden eenvoudig op een veilige manier vastleggen. In de voorgaande code zijn userId en bookId beide int.
Als in de voorgaande code een van beide routewaarden niet naar een intkan worden geconverteerd, wordt er een uitzondering gegenereerd. De GET-aanvraag /users/hello/books/3 genereert de volgende uitzondering:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Jokertekens en alle routes vangen
De volgende catch-all route retourneert Routing to hello van het eindpunt '/posts/hello':
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Routebeperkingen
Routebeperkingen beperken het overeenkomende gedrag van een route.
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();
In de volgende tabel ziet u de voorgaande routesjablonen en hun gedrag:
| Routesjabloon | Voorbeeld van overeenkomende URI |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Zie de naslaginformatie over routebeperkingenin Routering in ASP.NET Core voor meer informatie.
Routegroepen
Met de MapGroup-extensiemethode kunt u groepen eindpunten organiseren met een gemeenschappelijk voorvoegsel. Het vermindert terugkerende code en maakt het mogelijk om hele groepen eindpunten aan te passen met één aanroep naar methoden zoals RequireAuthorization en WithMetadata waarmee metagegevens van eindpuntenworden toegevoegd.
Met de volgende code worden bijvoorbeeld twee vergelijkbare groepen eindpunten gemaakt:
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 dit scenario kunt u een relatief adres gebruiken voor de Location-header in het 201 Created resultaat:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
De eerste groep eindpunten komt alleen overeen met aanvragen die zijn voorafgegaan door /public/todos en zijn toegankelijk zonder verificatie. De tweede groep eindpunten komt alleen overeen met aanvragen die zijn voorafgegaan door /private/todos en vereisen verificatie.
De QueryPrivateTodoseindpuntfilterfactory is een lokale functie waarmee de TodoDb parameters van de route-handler worden gewijzigd om toegang te krijgen tot privétodogegevens en deze op te slaan.
Routegroepen ondersteunen ook geneste groepen en complexe voorvoegselpatronen met routeparameters en beperkingen. In het volgende voorbeeld kan de routehandler die is toegewezen aan de user groep, de {org} en {group} routeparameters vastleggen die zijn gedefinieerd in de voorvoegsels van de buitenste groep.
Het voorvoegsel kan ook leeg zijn. Dit kan handig zijn voor het toevoegen van eindpuntmetagegevens of filters aan een groep eindpunten zonder het routepatroon te wijzigen.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Het toevoegen van filters of metagegevens aan een groep gedraagt zich op dezelfde manier als het afzonderlijk toevoegen van filters of metagegevens aan elk eindpunt voordat u extra filters of metagegevens toevoegt die mogelijk zijn toegevoegd aan een binnenste groep of specifiek eindpunt.
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);
});
In het bovenstaande voorbeeld registreert het buitenste filter de binnenkomende aanvraag voordat het binnenste filter de kans krijgt om dat te doen, hoewel het binnenste filter als tweede is toegevoegd. Omdat de filters zijn toegepast op verschillende groepen, maakt de volgorde die ze ten opzichte van elkaar zijn toegevoegd niet uit. De volgorde waarin filters worden toegevoegd, maakt uit of ze op dezelfde groep of een specifiek eindpunt worden toegepast.
Een aanvraag voor /outer/inner/ meldt het volgende:
/outer group filter
/inner group filter
MapGet filter
Parameterbinding
Parameterbinding is het proces van het converteren van aanvraaggegevens naar sterk getypte parameters die worden uitgedrukt door route-handlers. Een bindingsbron bepaalt waar parameters van afhankelijk zijn. Bindingsbronnen kunnen expliciet of afgeleid zijn op basis van de HTTP-methode en het parametertype.
Ondersteunde bindingsbronnen:
- Routewaarden
- Querystring
- Header
- Hoofdtekst (als JSON)
- Formulierwaarden
- Services geleverd door afhankelijkheidsinjectie
- Custom
De volgende GET-route-handler maakt gebruik van enkele van deze parameterbindingsbronnen:
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 { }
Bindingsfuncties voor sleutelparameters
-
Expliciete binding: gebruik kenmerken zoals
[FromRoute],[FromQuery],[FromHeader], ,[FromBody]en[FromForm][FromServices]om expliciet bindingsbronnen op te geven. -
Formulierbinding: Formulierwaarden binden met een
[FromForm]kenmerk, inclusief ondersteuning voorIFormFileenIFormFileCollectionvoor bestandsuploads. - Complexe typen: Binden aan verzamelingen en complexe typen uit formulieren, queryreeksen en headers.
-
Aangepaste binding: implementeer aangepaste bindingslogica met behulp van
TryParse,BindAsyncof deIBindableFromHttpContext<T>interface. - Optionele parameters: ondersteuning voor null-typen en standaardwaarden voor optionele parameters.
- Afhankelijkheidsinjectie: Parameters zijn automatisch afhankelijk van services die zijn geregistreerd in de DI-container.
-
Speciale typen: Automatische binding voor
HttpContext, ,HttpRequestHttpResponse,CancellationToken, ,ClaimsPrincipal, , ,StreamenPipeReader.
Meer informatie: Zie Parameterbinding in Minimale API-toepassingen voor gedetailleerde informatie over parameterbinding, waaronder geavanceerde scenario's, validatie, bindingsprioriteit en probleemoplossing.
Responses
Route-handlers ondersteunen de volgende typen retourwaarden:
-
IResultgebaseerd - Dit omvatTask<IResult>enValueTask<IResult> -
string- Dit omvatTask<string>enValueTask<string> -
T(Elk ander type): dit omvatTask<T>enValueTask<T>
| Retourwaarde | Behavior | Content-Type |
|---|---|---|
IResult |
Het framework roept IResult.ExecuteAsync aan | Besloten door de IResult implementatie |
string |
Het raamwerk schrijft de string rechtstreeks naar de respons | text/plain |
T (Elk ander type) |
Het framework JSON-serialiseert het antwoord | application/json |
Zie Antwoorden maken in minimale API-toepassingen voor een uitgebreidere handleiding voor het routeren van handler-retourwaarden
Voorbeeld van retourwaarden
retourwaarden van string
app.MapGet("/hello", () => "Hello World");
JSON-retourwaarden
app.MapGet("/hello", () => new { Message = "Hello World" });
Typgeresultaten retourneren
De volgende code retourneert een TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Het retourneren van TypedResults heeft de voorkeur boven het retourneren van Results. Voor meer informatie, zie TypedResults versus Results.
IResult retourwaarden
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
In het volgende voorbeeld worden de ingebouwde resultaattypen gebruikt om het antwoord aan te passen:
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" }));
Aangepaste 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");
});
Zie Antwoorden maken in Minimale API-toepassingen voor meer voorbeelden.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Ingebouwde resultaten
Algemene helpers voor resultaten bestaan in de Results en TypedResults statische klassen. Het retourneren van TypedResults heeft de voorkeur boven het retourneren van Results. Voor meer informatie, zie TypedResults versus Results.
Kopteksten wijzigen
Gebruik het HttpResponse-object om antwoordheaders te wijzigen:
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";
});
Resultaten aanpassen
Toepassingen kunnen antwoorden beheren door een aangepast IResult type te implementeren. De volgende code is een voorbeeld van een HTML-resultaattype:
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);
}
}
U wordt aangeraden een extensiemethode toe te voegen aan Microsoft.AspNetCore.Http.IResultExtensions om deze aangepaste resultaten beter te detecteren.
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();
Getypte resultaten
De IResult interface kan waarden vertegenwoordigen die worden geretourneerd van minimale API's die niet gebruikmaken van de impliciete ondersteuning voor JSON die het geretourneerde object serialiseren naar het HTTP-antwoord. De statische resultaten klasse wordt gebruikt om verschillende IResult objecten te maken die verschillende typen antwoorden vertegenwoordigen. Stel bijvoorbeeld de antwoordstatuscode in of omleiden naar een andere URL.
De typen die worden geĂŻmplementeerd IResult , zijn openbaar, waardoor typeverklaringen kunnen worden gebruikt bij het testen. Voorbeeld:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
U kunt de retourtypen van de bijbehorende methoden in de klasse Static TypedResults bekijken om het juiste openbare IResult type te vinden waarnaar moet worden gecast.
Zie Antwoorden maken in Minimale API-toepassingen voor meer voorbeelden.
Filters
Zie Filters in Minimale API-apps voor meer informatie.
Authorization
Routes kunnen worden beveiligd met autorisatiebeleid. Deze kunnen worden gedeclareerd via het [Authorize] kenmerk of met behulp van de RequireAuthorization methode:
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();
De voorgaande code kan worden geschreven met RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
In het volgende voorbeeld wordt gebruikgemaakt van autorisatie op basis van beleid:
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();
Niet-geverifieerde gebruikers toegang geven tot een eindpunt
Hiermee [AllowAnonymous] hebben niet-geverifieerde gebruikers toegang tot eindpunten:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Routes kunnen CORS-ondersteuning krijgen door gebruik te maken van CORS-beleid. CORS kan worden gedeclareerd via het [EnableCors] kenmerk of met behulp van de RequireCors methode. Met de volgende voorbeelden schakelt u CORS in:
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();
Zie CORS (Cross-Origin Requests) inschakelen in ASP.NET Core voor meer informatie
ValidateScopes en ValidateOnBuild
ValidateScopes en ValidateOnBuild zijn standaard ingeschakeld in de ontwikkelomgeving , maar uitgeschakeld in andere omgevingen.
Wanneer ValidateOnBuild is true, valideert de DI-container de serviceconfiguratie tijdens de build. Als de serviceconfiguratie ongeldig is, mislukt de build bij het opstarten van de app in plaats van tijdens runtime wanneer de service wordt aangevraagd.
Wanneer ValidateScopes is true, valideert de DI-container dat een scoped service niet wordt opgevraagd uit het hoofdbereik. Het oplossen van een dienst binnen een bereik vanuit het basisbereik kan leiden tot een geheugenlek omdat de dienst langer in het geheugen wordt bewaard dan het aanvraagbereik.
ValidateScopes en ValidateOnBuild zijn standaard onwaar in niet-ontwikkelingsmodi's voor prestatieoverwegingen.
In de volgende code ziet u dat deze ValidateScopes standaard is ingeschakeld in de ontwikkelingsmodus, maar uitgeschakeld in de releasemodus:
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 { }
In de volgende code ziet u dat deze ValidateOnBuild standaard is ingeschakeld in de ontwikkelingsmodus, maar uitgeschakeld in de releasemodus:
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 { }
De volgende code schakelt ValidateScopes en ValidateOnBuild uit 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;
});
}
Zie ook
- Minimal APIs beknopte naslaggids
- OpenAPI-documenten genereren
- Antwoorden maken in minimale API-toepassingen
- Filters in Minimal API-apps
- Fouten in ASP.NET Core-API's verwerken
- Authenticatie en autorisatie in minimale API's
- Minimale API-apps testen
- Kortsluitingsroutering
- Identity API-eindpunten
- Ondersteuning voor serviceafhankelijkheidsinjectiecontainer met sleutel
- Een kijkje achter de schermen van minimale API-eindpunten
- Minimale ASP.NET Core-API's ordenen
- Discussie over Fluent Validation op GitHub
Dit document:
- Biedt een beknopt overzicht voor minimale API's.
- Is bedoeld voor ervaren ontwikkelaars. Zie Zelfstudie: Een minimale API maken met ASP.NET Core voor een inleiding.
De minimale API's bestaan uit:
WebApplication
De volgende code wordt gegenereerd door een ASP.NET Core-sjabloon:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
De voorgaande code kan worden gemaakt via dotnet new web de opdrachtregel of door de lege websjabloon in Visual Studio te selecteren.
Met de volgende code wordt een WebApplication (app) gemaakt zonder expliciet een WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create initialiseert een nieuw exemplaar van de WebApplication klasse met vooraf geconfigureerde standaardwaarden.
WebApplication voegt automatisch de volgende middleware toe in Minimale API-toepassingen , afhankelijk van bepaalde voorwaarden:
-
UseDeveloperExceptionPagewordt eerst toegevoegd wanneer hetHostingEnvironmentis"Development". -
UseRoutingwordt als tweede toegevoegd als de gebruikerscodeUseRoutingnog niet is aangeroepen en als er geconfigureerde eindpunten zijn, bijvoorbeeldapp.MapGet. -
UseEndpointswordt toegevoegd aan het einde van de middleware-pijplijn als er eindpunten zijn geconfigureerd. -
UseAuthenticationwordt onmiddellijk toegevoegd naUseRoutingals de gebruikerscodeUseAuthenticationnog niet is aangeroepen en alsIAuthenticationSchemeProviderkan worden gedetecteerd in de serviceprovider.IAuthenticationSchemeProviderwordt standaard toegevoegd wanneer uAddAuthenticationgebruikt, en services worden gedetecteerd met behulp vanIServiceProviderIsService. -
UseAuthorizationwordt vervolgens toegevoegd als de gebruikerscode nog niet heeft aangeroepenUseAuthorizationen alsIAuthorizationHandlerProviderkan worden gedetecteerd in de serviceprovider.IAuthorizationHandlerProviderwordt standaard toegevoegd wanneer uAddAuthorizationgebruikt, en services worden gedetecteerd met behulp vanIServiceProviderIsService. - Door de gebruiker geconfigureerde middleware en eindpunten worden toegevoegd tussen
UseRoutingenUseEndpoints.
De volgende code is effectief wat de automatische middleware die aan de app wordt toegevoegd, produceert:
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 sommige gevallen is de standaard-middlewareconfiguratie niet juist voor de app en moet deze worden gewijzigd. Moet bijvoorbeeld UseCors worden aangeroepen voor UseAuthentication en UseAuthorization. De app moet UseAuthentication en UseAuthorization oproepen als UseCors wordt aangeroepen.
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Als middleware moet worden uitgevoerd voordat routekoppeling plaatsvindt, moet UseRouting worden aangeroepen en moet de middleware worden geplaatst voordat de aanroep naar UseRouting plaatsvindt.
UseEndpoints is in dit geval niet vereist, omdat deze automatisch wordt toegevoegd zoals eerder beschreven:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Wanneer u een terminal-middleware toevoegt:
- De middleware moet worden toegevoegd na
UseEndpoints. - De app moet
UseRoutingenUseEndpointsaanroepen zodat de terminal middleware op de juiste locatie kan worden geplaatst.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Terminal middleware is middleware die actief is wanneer er geen eindpunt de aanvraag afhandelt.
Werken met poorten
Wanneer een web-app wordt gemaakt met Visual Studio of dotnet new, wordt er een Properties/launchSettings.json bestand gemaakt waarin de poorten worden opgegeven waarop de app reageert. In de volgende voorbeelden van poortinstellingen retourneert het uitvoeren van de app vanuit Visual Studio een foutdialoogvenster Unable to connect to web server 'AppName'. Visual Studio retourneert een fout omdat deze de poort verwacht die is opgegeven in Properties/launchSettings.json, maar de app de poort gebruikt die is opgegeven door app.Run("http://localhost:3000"). Voer de volgende voorbeelden die de poort wijzigen uit vanuit de opdrachtregel.
In de volgende secties wordt de poort ingesteld waar de app op reageert.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
In de voorgaande code reageert de app op poort 3000.
Meerdere poorten
In de volgende code reageert de app op poort 3000 en 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
De poort instellen via de opdrachtregel
Met de volgende opdracht reageert de app op 7777poort:
dotnet run --urls="https://localhost:7777"
Als het Kestrel eindpunt ook in het appsettings.json bestand is geconfigureerd, wordt de opgegeven URL van het appsettings.json bestand gebruikt. Zie de eindpuntconfiguratie voor meer informatie Kestrel
De poort uit de omgevingsvariabelen lezen
Met de volgende code wordt de poort uit de omgeving gelezen:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
De voorkeursmethode voor het instellen van de poort vanuit de omgeving is het gebruik van de ASPNETCORE_URLS omgevingsvariabele, die wordt weergegeven in de volgende sectie.
De poorten instellen via de omgevingsvariabele ASPNETCORE_URLS
De ASPNETCORE_URLS omgevingsvariabele is beschikbaar om de poort in te stellen:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS ondersteunt meerdere URL's:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Luisteren op alle interfaces
In de volgende voorbeelden ziet u hoe u luistert op alle interfaces
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();
Luisteren op alle interfaces met behulp van ASPNETCORE_URLS
De voorgaande voorbeelden kunnen ASPNETCORE_URLS gebruiken
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Luisteren op alle interfaces met behulp van ASPNETCORE_HTTPS_PORTS
De voorgaande voorbeelden kunnen worden gebruikt ASPNETCORE_HTTPS_PORTS en ASPNETCORE_HTTP_PORTS.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Zie Eindpunten configureren voor de ASP.NET Core-webserver Kestrel voor meer informatie
HTTPS specificeren met ontwikkelingscertificaat
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Zie Het ASP.NET Core HTTPS-ontwikkelingscertificaat vertrouwen in Windows en macOS voor meer informatie over het ontwikkelingscertificaat.
HTTPS opgeven met behulp van een aangepast certificaat
In de volgende secties ziet u hoe u het aangepaste certificaat opgeeft met behulp van het appsettings.json bestand en via de configuratie.
Geef het aangepaste certificaat op met appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Het aangepaste certificaat opgeven via configuratie
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();
De certificaat-API's gebruiken
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();
De omgeving lezen
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();
Zie ASP.NET Core runtime-omgevingen voor meer informatie over het gebruik van de omgeving
Configuration
De volgende code leest uit het configuratiesysteem:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Zie Configuratie in ASP.NET Core- voor meer informatie
Logging
Met de volgende code wordt bij de start van de toepassing een bericht naar het logboek geschreven.
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Zie Logboekregistratie in .NET en ASP.NET Core voor meer informatie
Toegang tot de Dependency Injection-container (DI)
De volgende code laat zien hoe u services uit de DI-container kunt ophalen tijdens het opstarten van de toepassing:
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();
De volgende code laat zien hoe u toegang hebt tot sleutels uit de DI-container met behulp van het [FromKeyedServices] kenmerk:
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.";
}
Zie Afhankelijkheidsinjectie in ASP.NET Core voor meer informatie over DI.
WebApplicationBuilder
Deze sectie bevat voorbeeldcode met behulp van WebApplicationBuilder.
De inhoudsbasis, de naam van de toepassing en de omgeving wijzigen
De volgende code stelt de inhoudsmap, toepassingsnaam en omgeving in.
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 initialiseert een nieuw exemplaar van de WebApplicationBuilder-klasse met vooraf geconfigureerde standaardwaarden.
Zie ASP.NET Overzicht van basisbeginselen van Core voor meer informatie
De hoofdmap, app-naam en omgeving wijzigen met behulp van omgevingsvariabelen of opdrachtregel
In de volgende tabel ziet u de omgevingsvariabele en het opdrachtregelargument dat wordt gebruikt om de hoofdmap, de naam van de app en de omgeving te wijzigen:
| feature | Omgevingsvariabele | Opdrachtregelargument |
|---|---|---|
| Toepassingsnaam | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Naam van de omgeving | ASPNETCORE_ENVIRONMENT | --environment |
| Hoofdmap van inhoud | ASPNETCORE_CONTENTROOT | --contentRoot |
Configuratieproviders toevoegen
In het volgende voorbeeld wordt de INI-configuratieprovider toegevoegd:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Zie Bestandsconfiguratieproviders in Configuratie in ASP.NET Core voor gedetailleerde informatie.
Leesconfiguratie
Standaard leest de WebApplicationBuilder de configuratie uit meerdere bronnen, waaronder:
-
appSettings.jsonenappSettings.{environment}.json - Omgevingsvariabelen
- De opdrachtregel
Zie De standaardconfiguratiein Configuratie in ASP.NET Core voor een volledige lijst met configuratiebronnen die worden gelezen.
De volgende code leest HelloKey uit de configuratie en geeft de waarde weer op het / eindpunt. Als de configuratiewaarde null is, wordt 'Hallo' toegewezen aan message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
De omgeving lezen
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine($"Running in development.");
}
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Logboekregistratieproviders toevoegen
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();
Services toevoegen
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();
De IHostBuilder aanpassen
Bestaande uitbreidingsmethoden IHostBuilder kunnen worden geopend met behulp van de eigenschap Host:
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();
De IWebHostBuilder aanpassen
Extensiemethoden zijn IWebHostBuilder toegankelijk via de eigenschap WebApplicationBuilder.WebHost .
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();
De webroot wijzigen
De webroot is standaard relatief ten opzichte van de inhoudsmap in de map wwwroot. De webroot is waar de Static File Middleware zoekt naar statische bestanden. Webhoofdmap kan worden gewijzigd met WebHostOptions, de opdrachtregel of met de UseWebRoot methode:
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Afhankelijkheidsinjectiecontainer op maat (DI)
In het volgende voorbeeld wordt Autofac gebruikt:
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();
Middleware toevoegen
Alle bestaande ASP.NET Core-middleware kan worden geconfigureerd op WebApplication.
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Zie ASP.NET Core Middleware voor meer informatie
Uitzonderingspagina voor ontwikkelaars
WebApplication.CreateBuilder initialiseert een nieuw exemplaar van de WebApplicationBuilder klasse met vooraf geconfigureerde standaardwaarden. De uitzonderingspagina voor ontwikkelaars is ingeschakeld in de vooraf geconfigureerde standaardinstellingen. Wanneer de volgende code wordt uitgevoerd in de ontwikkelomgeving, wordt er genavigeerd naar een vriendelijke pagina die de uitzondering toont.
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
De volgende tabel bevat enkele van de middleware die vaak wordt gebruikt met minimale API's.
| Middleware | Description | API |
|---|---|---|
| Authentication | Biedt ondersteuning voor verificatie. | UseAuthentication |
| Authorization | Biedt autorisatieondersteuning. | UseAuthorization |
| CORS | Hiermee wordt Cross-Origin Resource Sharing geconfigureerd. | UseCors |
| Uitzonderingshandler | Wereldwijd worden uitzonderingen verwerkt die worden gegenereerd door de middleware-pijplijn. | UseExceptionHandler |
| Doorgestuurde headers | Hiermee worden via een proxy ontvangen headers doorgestuurd naar het huidige verzoek. | UseForwardedHeaders |
| HTTPS-omleiding | Alle HTTP-aanvragen worden omgeleid naar HTTPS. | UseHttpsRedirection |
| HTTP Strict Transport Security (HSTS) | Middleware voor beveiligingsverbeteringen waarmee een speciale antwoordheader wordt toegevoegd. | UseHsts |
| Logboekregistratie aanvragen | Biedt ondersteuning voor het registreren van HTTP-aanvragen en -antwoorden. | UseHttpLogging |
| Time-outs aanvragen | Biedt ondersteuning voor het configureren van time-outs voor aanvragen, globale standaardinstellingen en per eindpunt. | UseRequestTimeouts |
| Logboekregistratie van W3C-aanvragen | Biedt ondersteuning voor het registreren van HTTP-aanvragen en -antwoorden in de W3C-indeling. | UseW3CLogging |
| Antwoordcaching | Biedt ondersteuning voor het opslaan van antwoorden in cache. | UseResponseCaching |
| Antwoordcompressie | Biedt ondersteuning voor het comprimeren van antwoorden. | UseResponseCompression |
| Session | Biedt ondersteuning voor het beheren van gebruikerssessies. | UseSession |
| Statische bestanden | Biedt ondersteuning voor het leveren van statische bestanden en bladeren door mappen. | UseStaticFiles, UseFileServer |
| WebSockets | Hiermee schakelt u het WebSockets-protocol in. | UseWebSockets |
In de volgende secties worden de verwerking van aanvragen behandeld: routering, parameterbinding en antwoorden.
Routing
Een geconfigureerde WebApplication ondersteunt Map{Verb} en MapMethods waar {Verb} een camelCase HTTP-methode is zoals Get, Post, Put of Delete:
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();
De Delegate argumenten die aan deze methoden worden doorgegeven, worden 'routehandlers' genoemd.
Route-handlers
Route-handlers zijn methoden die worden uitgevoerd wanneer de route overeenkomt. Route-handlers kunnen een lambda-expressie, een lokale functie, een instantiemethode of een statische methode zijn. Route-handlers kunnen synchroon of asynchroon zijn.
Lambda-expressie
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 functie
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Exemplaarmethode
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";
}
}
Statische methode
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", HelloHandler.Hello);
app.Run();
class HelloHandler
{
public static string Hello()
{
return "Hello static method";
}
}
Eindpunt gedefinieerd buiten Program.cs
Minimale API's hoeven zich niet in Program.cste bevinden.
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" });
});
}
}
Zie ook Routegroepen verderop in dit artikel.
Benoemde eindpunten en het genereren van koppelingen
Eindpunten kunnen namen krijgen om URL's naar het eindpunt te genereren. Als u een benoemd eindpunt gebruikt, hoeft u geen vaste codepaden in een app te gebruiken:
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();
De voorgaande code geeft The link to the hello route is /hello weer vanaf het / eindpunt.
OPMERKING: Eindpuntnamen zijn hoofdlettergevoelig.
Namen van eindpunten:
- De naam moet wereldwijd uniek zijn.
- Worden gebruikt als de OpenAPI-bewerkings-id wanneer OpenAPI-ondersteuning is ingeschakeld. Zie OpenAPI voor meer informatie.
Routeparameters
Routeparameters kunnen worden vastgelegd als onderdeel van de definitie van het routepatroon:
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();
De voorgaande code retourneert The user id is 3 and book id is 7 van de URI /users/3/books/7.
De route-handler kan de parameters declareren die moeten worden vastgelegd. Wanneer een aanvraag wordt ingediend bij een route met parameters die zijn gedeclareerd om vast te leggen, worden de parameters geparseerd en doorgegeven aan de handler. Hierdoor kunt u de waarden eenvoudig op een veilige manier vastleggen. In de voorgaande code zijn userId en bookId beide int.
Als in de voorgaande code een van beide routewaarden niet naar een intkan worden geconverteerd, wordt er een uitzondering gegenereerd. De GET-aanvraag /users/hello/books/3 genereert de volgende uitzondering:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Jokertekens en alle routes vangen
De volgende catch-all route retourneert Routing to hello van het eindpunt '/posts/hello':
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Routebeperkingen
Routebeperkingen beperken het overeenkomende gedrag van een route.
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();
In de volgende tabel ziet u de voorgaande routesjablonen en hun gedrag:
| Routesjabloon | Voorbeeld van overeenkomende URI |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Zie de naslaginformatie over routebeperkingenin Routering in ASP.NET Core voor meer informatie.
Routegroepen
Met de MapGroup-extensiemethode kunt u groepen eindpunten organiseren met een gemeenschappelijk voorvoegsel. Het vermindert terugkerende code en maakt het mogelijk om hele groepen eindpunten aan te passen met één aanroep naar methoden zoals RequireAuthorization en WithMetadata waarmee metagegevens van eindpuntenworden toegevoegd.
Met de volgende code worden bijvoorbeeld twee vergelijkbare groepen eindpunten gemaakt:
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 dit scenario kunt u een relatief adres gebruiken voor de Location-header in het 201 Created resultaat:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
De eerste groep eindpunten komt alleen overeen met aanvragen die zijn voorafgegaan door /public/todos en zijn toegankelijk zonder verificatie. De tweede groep eindpunten komt alleen overeen met aanvragen die zijn voorafgegaan door /private/todos en vereisen verificatie.
De QueryPrivateTodoseindpuntfilterfactory is een lokale functie waarmee de TodoDb parameters van de route-handler worden gewijzigd om toegang te krijgen tot privétodogegevens en deze op te slaan.
Routegroepen ondersteunen ook geneste groepen en complexe voorvoegselpatronen met routeparameters en beperkingen. In het volgende voorbeeld kan de routehandler die is toegewezen aan de user groep, de {org} en {group} routeparameters vastleggen die zijn gedefinieerd in de voorvoegsels van de buitenste groep.
Het voorvoegsel kan ook leeg zijn. Dit kan handig zijn voor het toevoegen van eindpuntmetagegevens of filters aan een groep eindpunten zonder het routepatroon te wijzigen.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Het toevoegen van filters of metagegevens aan een groep gedraagt zich op dezelfde manier als het afzonderlijk toevoegen van filters of metagegevens aan elk eindpunt voordat u extra filters of metagegevens toevoegt die mogelijk zijn toegevoegd aan een binnenste groep of specifiek eindpunt.
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);
});
In het bovenstaande voorbeeld registreert het buitenste filter de binnenkomende aanvraag voordat het binnenste filter de kans krijgt om dat te doen, hoewel het binnenste filter als tweede is toegevoegd. Omdat de filters zijn toegepast op verschillende groepen, maakt de volgorde die ze ten opzichte van elkaar zijn toegevoegd niet uit. De volgorde waarin filters worden toegevoegd, maakt uit of ze op dezelfde groep of een specifiek eindpunt worden toegepast.
Een aanvraag voor /outer/inner/ meldt het volgende:
/outer group filter
/inner group filter
MapGet filter
Parameterbinding
Parameterbinding is het proces van het converteren van aanvraaggegevens naar sterk getypte parameters die worden uitgedrukt door route-handlers. Een bindingsbron bepaalt waar parameters van afhankelijk zijn. Bindingsbronnen kunnen expliciet of afgeleid zijn op basis van de HTTP-methode en het parametertype.
Ondersteunde bindingsbronnen:
- Routewaarden
- Querystring
- Header
- Hoofdtekst (als JSON)
- Formulierwaarden
- Services geleverd door afhankelijkheidsinjectie
- Custom
De volgende GET-route-handler maakt gebruik van enkele van deze parameterbindingsbronnen:
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 { }
Bindingsfuncties voor sleutelparameters
-
Expliciete binding: gebruik kenmerken zoals
[FromRoute],[FromQuery],[FromHeader], ,[FromBody]en[FromForm][FromServices]om expliciet bindingsbronnen op te geven. -
Formulierbinding: Formulierwaarden binden met een
[FromForm]kenmerk, inclusief ondersteuning voorIFormFileenIFormFileCollectionvoor bestandsuploads. - Complexe typen: Binden aan verzamelingen en complexe typen uit formulieren, queryreeksen en headers.
-
Aangepaste binding: implementeer aangepaste bindingslogica met behulp van
TryParse,BindAsyncof deIBindableFromHttpContext<T>interface. - Optionele parameters: ondersteuning voor null-typen en standaardwaarden voor optionele parameters.
- Afhankelijkheidsinjectie: Parameters zijn automatisch afhankelijk van services die zijn geregistreerd in de DI-container.
-
Speciale typen: Automatische binding voor
HttpContext, ,HttpRequestHttpResponse,CancellationToken, ,ClaimsPrincipal, , ,StreamenPipeReader.
Meer informatie: Zie Parameterbinding in Minimale API-toepassingen voor gedetailleerde informatie over parameterbinding, waaronder geavanceerde scenario's, validatie, bindingsprioriteit en probleemoplossing.
Responses
Route-handlers ondersteunen de volgende typen retourwaarden:
-
IResultgebaseerd - Dit omvatTask<IResult>enValueTask<IResult> -
string- Dit omvatTask<string>enValueTask<string> -
T(Elk ander type): dit omvatTask<T>enValueTask<T>
| Retourwaarde | Behavior | Content-Type |
|---|---|---|
IResult |
Het framework roept IResult.ExecuteAsync aan | Besloten door de IResult implementatie |
string |
Het raamwerk schrijft de string rechtstreeks naar de respons | text/plain |
T (Elk ander type) |
Het framework JSON-serialiseert het antwoord | application/json |
Zie Antwoorden maken in minimale API-toepassingen voor een uitgebreidere handleiding voor het routeren van handler-retourwaarden
Voorbeeld van retourwaarden
retourwaarden van string
app.MapGet("/hello", () => "Hello World");
JSON-retourwaarden
app.MapGet("/hello", () => new { Message = "Hello World" });
Typgeresultaten retourneren
De volgende code retourneert een TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Het retourneren van TypedResults heeft de voorkeur boven het retourneren van Results. Voor meer informatie, zie TypedResults versus Results.
IResult retourwaarden
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
In het volgende voorbeeld worden de ingebouwde resultaattypen gebruikt om het antwoord aan te passen:
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" }));
Aangepaste 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");
});
Zie Antwoorden maken in Minimale API-toepassingen voor meer voorbeelden.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Ingebouwde resultaten
Algemene helpers voor resultaten bestaan in de Results en TypedResults statische klassen. Het retourneren van TypedResults heeft de voorkeur boven het retourneren van Results. Voor meer informatie, zie TypedResults versus Results.
Kopteksten wijzigen
Gebruik het HttpResponse-object om antwoordheaders te wijzigen:
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";
});
Resultaten aanpassen
Toepassingen kunnen antwoorden beheren door een aangepast IResult type te implementeren. De volgende code is een voorbeeld van een HTML-resultaattype:
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);
}
}
U wordt aangeraden een extensiemethode toe te voegen aan Microsoft.AspNetCore.Http.IResultExtensions om deze aangepaste resultaten beter te detecteren.
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();
Getypte resultaten
De IResult interface kan waarden vertegenwoordigen die worden geretourneerd van minimale API's die niet gebruikmaken van de impliciete ondersteuning voor JSON die het geretourneerde object serialiseren naar het HTTP-antwoord. De statische resultaten klasse wordt gebruikt om verschillende IResult objecten te maken die verschillende typen antwoorden vertegenwoordigen. Stel bijvoorbeeld de antwoordstatuscode in of omleiden naar een andere URL.
De typen die worden geĂŻmplementeerd IResult , zijn openbaar, waardoor typeverklaringen kunnen worden gebruikt bij het testen. Voorbeeld:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
U kunt de retourtypen van de bijbehorende methoden in de klasse Static TypedResults bekijken om het juiste openbare IResult type te vinden waarnaar moet worden gecast.
Zie Antwoorden maken in Minimale API-toepassingen voor meer voorbeelden.
Filters
Zie Filters in Minimale API-apps voor meer informatie.
Authorization
Routes kunnen worden beveiligd met autorisatiebeleid. Deze kunnen worden gedeclareerd via het [Authorize] kenmerk of met behulp van de RequireAuthorization methode:
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();
De voorgaande code kan worden geschreven met RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
In het volgende voorbeeld wordt gebruikgemaakt van autorisatie op basis van beleid:
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();
Niet-geverifieerde gebruikers toegang geven tot een eindpunt
Hiermee [AllowAnonymous] hebben niet-geverifieerde gebruikers toegang tot eindpunten:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Routes kunnen CORS-ondersteuning krijgen door gebruik te maken van CORS-beleid. CORS kan worden gedeclareerd via het [EnableCors] kenmerk of met behulp van de RequireCors methode. Met de volgende voorbeelden schakelt u CORS in:
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();
Zie CORS (Cross-Origin Requests) inschakelen in ASP.NET Core voor meer informatie
ValidateScopes en ValidateOnBuild
ValidateScopes en ValidateOnBuild zijn standaard ingeschakeld in de ontwikkelomgeving , maar uitgeschakeld in andere omgevingen.
Wanneer ValidateOnBuild is true, valideert de DI-container de serviceconfiguratie tijdens de build. Als de serviceconfiguratie ongeldig is, mislukt de build bij het opstarten van de app in plaats van tijdens runtime wanneer de service wordt aangevraagd.
Wanneer ValidateScopes is true, valideert de DI-container dat een scoped service niet wordt opgevraagd uit het hoofdbereik. Het oplossen van een dienst binnen een bereik vanuit het basisbereik kan leiden tot een geheugenlek omdat de dienst langer in het geheugen wordt bewaard dan het aanvraagbereik.
ValidateScopes en ValidateOnBuild zijn standaard onwaar in niet-ontwikkelingsmodi's voor prestatieoverwegingen.
In de volgende code ziet u dat deze ValidateScopes standaard is ingeschakeld in de ontwikkelingsmodus, maar uitgeschakeld in de releasemodus:
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 { }
In de volgende code ziet u dat deze ValidateOnBuild standaard is ingeschakeld in de ontwikkelingsmodus, maar uitgeschakeld in de releasemodus:
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 { }
De volgende code schakelt ValidateScopes en ValidateOnBuild uit 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;
});
}
Zie ook
- Minimal APIs beknopte naslaggids
- OpenAPI-documenten genereren
- Antwoorden maken in minimale API-toepassingen
- Filters in Minimal API-apps
- Fouten in ASP.NET Core-API's verwerken
- Authenticatie en autorisatie in minimale API's
- Minimale API-apps testen
- Kortsluitingsroutering
- Identity API-eindpunten
- Ondersteuning voor serviceafhankelijkheidsinjectiecontainer met sleutel
- Een kijkje achter de schermen van minimale API-eindpunten
- Minimale ASP.NET Core-API's ordenen
- Discussie over Fluent Validation op GitHub
Dit document:
- Biedt een beknopt overzicht voor minimale API's.
- Is bedoeld voor ervaren ontwikkelaars. Zie Zelfstudie: Een minimale API maken met ASP.NET Core voor een inleiding
De minimale API's bestaan uit:
WebApplication
De volgende code wordt gegenereerd door een ASP.NET Core-sjabloon:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
De voorgaande code kan worden gemaakt via dotnet new web de opdrachtregel of door de lege websjabloon in Visual Studio te selecteren.
Met de volgende code wordt een WebApplication (app) gemaakt zonder expliciet een WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create initialiseert een nieuw exemplaar van de WebApplication klasse met vooraf geconfigureerde standaardwaarden.
WebApplication voegt automatisch de volgende middleware toe in Minimale API-toepassingen , afhankelijk van bepaalde voorwaarden:
-
UseDeveloperExceptionPagewordt eerst toegevoegd wanneer hetHostingEnvironmentis"Development". -
UseRoutingwordt als tweede toegevoegd als de gebruikerscodeUseRoutingnog niet is aangeroepen en als er geconfigureerde eindpunten zijn, bijvoorbeeldapp.MapGet. -
UseEndpointswordt toegevoegd aan het einde van de middleware-pijplijn als er eindpunten zijn geconfigureerd. -
UseAuthenticationwordt onmiddellijk toegevoegd naUseRoutingals de gebruikerscodeUseAuthenticationnog niet is aangeroepen en alsIAuthenticationSchemeProviderkan worden gedetecteerd in de serviceprovider.IAuthenticationSchemeProviderwordt standaard toegevoegd wanneer uAddAuthenticationgebruikt, en services worden gedetecteerd met behulp vanIServiceProviderIsService. -
UseAuthorizationwordt vervolgens toegevoegd als de gebruikerscode nog niet heeft aangeroepenUseAuthorizationen alsIAuthorizationHandlerProviderkan worden gedetecteerd in de serviceprovider.IAuthorizationHandlerProviderwordt standaard toegevoegd wanneer uAddAuthorizationgebruikt, en services worden gedetecteerd met behulp vanIServiceProviderIsService. - Door de gebruiker geconfigureerde middleware en eindpunten worden toegevoegd tussen
UseRoutingenUseEndpoints.
De volgende code is effectief wat de automatische middleware die aan de app wordt toegevoegd, produceert:
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 sommige gevallen is de standaard-middlewareconfiguratie niet juist voor de app en moet deze worden gewijzigd. Moet bijvoorbeeld UseCors worden aangeroepen voor UseAuthentication en UseAuthorization. De app moet UseAuthentication en UseAuthorization oproepen als UseCors wordt aangeroepen.
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Als middleware moet worden uitgevoerd voordat routekoppeling plaatsvindt, moet UseRouting worden aangeroepen en moet de middleware worden geplaatst voordat de aanroep naar UseRouting plaatsvindt.
UseEndpoints is in dit geval niet vereist, omdat deze automatisch wordt toegevoegd zoals eerder beschreven:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Wanneer u een terminal-middleware toevoegt:
- De middleware moet worden toegevoegd na
UseEndpoints. - De app moet
UseRoutingenUseEndpointsaanroepen zodat de terminal middleware op de juiste locatie kan worden geplaatst.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Terminal middleware is middleware die actief is wanneer er geen eindpunt de aanvraag afhandelt.
Werken met poorten
Wanneer een web-app wordt gemaakt met Visual Studio of dotnet new, wordt er een Properties/launchSettings.json bestand gemaakt waarin de poorten worden opgegeven waarop de app reageert. In de volgende voorbeelden van poortinstellingen retourneert het uitvoeren van de app vanuit Visual Studio een foutdialoogvenster Unable to connect to web server 'AppName'. Visual Studio retourneert een fout omdat deze de poort verwacht die is opgegeven in Properties/launchSettings.json, maar de app de poort gebruikt die is opgegeven door app.Run("http://localhost:3000"). Voer de volgende voorbeelden die de poort wijzigen uit vanuit de opdrachtregel.
In de volgende secties wordt de poort ingesteld waar de app op reageert.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
In de voorgaande code reageert de app op poort 3000.
Meerdere poorten
In de volgende code reageert de app op poort 3000 en 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
De poort instellen via de opdrachtregel
Met de volgende opdracht reageert de app op 7777poort:
dotnet run --urls="https://localhost:7777"
Als het Kestrel eindpunt ook in het appsettings.json bestand is geconfigureerd, wordt de opgegeven URL van het appsettings.json bestand gebruikt. Zie de eindpuntconfiguratie voor meer informatie Kestrel
De poort uit de omgevingsvariabelen lezen
Met de volgende code wordt de poort uit de omgeving gelezen:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
De voorkeursmethode voor het instellen van de poort vanuit de omgeving is het gebruik van de ASPNETCORE_URLS omgevingsvariabele, die wordt weergegeven in de volgende sectie.
De poorten instellen via de omgevingsvariabele ASPNETCORE_URLS
De ASPNETCORE_URLS omgevingsvariabele is beschikbaar om de poort in te stellen:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS ondersteunt meerdere URL's:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Zie ASP.NET Core runtime-omgevingen voor meer informatie over het gebruik van de omgeving
Luisteren op alle interfaces
In de volgende voorbeelden ziet u hoe u luistert op alle interfaces
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();
Luisteren op alle interfaces met behulp van ASPNETCORE_URLS
De voorgaande voorbeelden kunnen ASPNETCORE_URLS gebruiken
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
HTTPS specificeren met ontwikkelingscertificaat
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Zie Het ASP.NET Core HTTPS-ontwikkelingscertificaat vertrouwen in Windows en macOS voor meer informatie over het ontwikkelingscertificaat.
HTTPS opgeven met behulp van een aangepast certificaat
In de volgende secties ziet u hoe u het aangepaste certificaat opgeeft met behulp van het appsettings.json bestand en via de configuratie.
Geef het aangepaste certificaat op met appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Het aangepaste certificaat opgeven via configuratie
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();
De certificaat-API's gebruiken
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
De volgende code leest uit het configuratiesysteem:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Zie Configuratie in ASP.NET Core- voor meer informatie
Logging
Met de volgende code wordt bij de start van de toepassing een bericht naar het logboek geschreven.
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Zie Logboekregistratie in .NET en ASP.NET Core voor meer informatie
Toegang tot de Dependency Injection-container (DI)
De volgende code laat zien hoe u services uit de DI-container kunt ophalen tijdens het opstarten van de toepassing:
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();
Zie Afhankelijkheidsinjectie in ASP.NET Corevoor meer informatie.
WebApplicationBuilder
Deze sectie bevat voorbeeldcode met behulp van WebApplicationBuilder.
De inhoudsbasis, de naam van de toepassing en de omgeving wijzigen
De volgende code stelt de inhoudsmap, toepassingsnaam en omgeving in.
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 initialiseert een nieuw exemplaar van de WebApplicationBuilder-klasse met vooraf geconfigureerde standaardwaarden.
Zie ASP.NET Overzicht van basisbeginselen van Core voor meer informatie
Het wijzigen van de hoofdmap, app-naam en omgeving via omgevingsvariabelen of de opdrachtregel
In de volgende tabel ziet u de omgevingsvariabele en het opdrachtregelargument dat wordt gebruikt om de hoofdmap, de naam van de app en de omgeving te wijzigen:
| feature | Omgevingsvariabele | Opdrachtregelargument |
|---|---|---|
| Toepassingsnaam | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Naam van de omgeving | ASPNETCORE_ENVIRONMENT | --environment |
| Hoofdmap van inhoud | ASPNETCORE_CONTENTROOT | --contentRoot |
Configuratieproviders toevoegen
In het volgende voorbeeld wordt de INI-configuratieprovider toegevoegd:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Zie Bestandsconfiguratieproviders in Configuratie in ASP.NET Core voor gedetailleerde informatie.
Leesconfiguratie
Standaard leest de WebApplicationBuilder de configuratie uit meerdere bronnen, waaronder:
-
appSettings.jsonenappSettings.{environment}.json - Omgevingsvariabelen
- De opdrachtregel
De volgende code leest HelloKey uit de configuratie en geeft de waarde weer op het / eindpunt. Als de configuratiewaarde null is, wordt 'Hallo' toegewezen aan message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Zie De standaardconfiguratiein Configuratie in ASP.NET Core voor een volledige lijst met gelezen configuratiebronnen
Logboekregistratieproviders toevoegen
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();
Services toevoegen
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();
De IHostBuilder aanpassen
Bestaande uitbreidingsmethoden IHostBuilder kunnen worden geopend met behulp van de eigenschap Host:
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();
De IWebHostBuilder aanpassen
Extensiemethoden zijn IWebHostBuilder toegankelijk via de eigenschap WebApplicationBuilder.WebHost .
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();
De webroot wijzigen
De webroot is standaard relatief ten opzichte van de inhoudsmap in de map wwwroot. De webroot is waar de Static File Middleware zoekt naar statische bestanden. Webhoofdmap kan worden gewijzigd met WebHostOptions, de opdrachtregel of met de UseWebRoot methode:
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Afhankelijkheidsinjectiecontainer op maat (DI)
In het volgende voorbeeld wordt Autofac gebruikt:
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();
Middleware toevoegen
Alle bestaande ASP.NET Core-middleware kan worden geconfigureerd op WebApplication.
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Zie ASP.NET Core Middleware voor meer informatie
Uitzonderingspagina voor ontwikkelaars
WebApplication.CreateBuilder initialiseert een nieuw exemplaar van de WebApplicationBuilder klasse met vooraf geconfigureerde standaardwaarden. De uitzonderingspagina voor ontwikkelaars is ingeschakeld in de vooraf geconfigureerde standaardinstellingen. Wanneer de volgende code wordt uitgevoerd in de ontwikkelomgeving, wordt er genavigeerd naar een vriendelijke pagina die de uitzondering toont.
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
De volgende tabel bevat enkele van de middleware die vaak wordt gebruikt met minimale API's.
| Middleware | Description | API |
|---|---|---|
| Authentication | Biedt ondersteuning voor verificatie. | UseAuthentication |
| Authorization | Biedt autorisatieondersteuning. | UseAuthorization |
| CORS | Hiermee wordt Cross-Origin Resource Sharing geconfigureerd. | UseCors |
| Uitzonderingshandler | Wereldwijd worden uitzonderingen verwerkt die worden gegenereerd door de middleware-pijplijn. | UseExceptionHandler |
| Doorgestuurde headers | Hiermee worden via een proxy ontvangen headers doorgestuurd naar het huidige verzoek. | UseForwardedHeaders |
| HTTPS-omleiding | Alle HTTP-aanvragen worden omgeleid naar HTTPS. | UseHttpsRedirection |
| HTTP Strict Transport Security (HSTS) | Middleware voor beveiligingsverbeteringen waarmee een speciale antwoordheader wordt toegevoegd. | UseHsts |
| Logboekregistratie aanvragen | Biedt ondersteuning voor het registreren van HTTP-aanvragen en -antwoorden. | UseHttpLogging |
| Time-outs aanvragen | Biedt ondersteuning voor het configureren van time-outs voor aanvragen, globale standaardinstellingen en per eindpunt. | UseRequestTimeouts |
| Logboekregistratie van W3C-aanvragen | Biedt ondersteuning voor het registreren van HTTP-aanvragen en -antwoorden in de W3C-indeling. | UseW3CLogging |
| Antwoordcaching | Biedt ondersteuning voor het opslaan van antwoorden in cache. | UseResponseCaching |
| Antwoordcompressie | Biedt ondersteuning voor het comprimeren van antwoorden. | UseResponseCompression |
| Session | Biedt ondersteuning voor het beheren van gebruikerssessies. | UseSession |
| Statische bestanden | Biedt ondersteuning voor het leveren van statische bestanden en bladeren door mappen. | UseStaticFiles, UseFileServer |
| WebSockets | Hiermee schakelt u het WebSockets-protocol in. | UseWebSockets |
In de volgende secties worden de verwerking van aanvragen behandeld: routering, parameterbinding en antwoorden.
Routing
Een geconfigureerde WebApplication ondersteunt Map{Verb} en MapMethods waar {Verb} een camelCase HTTP-methode is zoals Get, Post, Put of Delete:
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();
De Delegate argumenten die aan deze methoden worden doorgegeven, worden 'routehandlers' genoemd.
Route-handlers
Route-handlers zijn methoden die worden uitgevoerd wanneer de route overeenkomt. Route-handlers kunnen een lambda-expressie, een lokale functie, een instantiemethode of een statische methode zijn. Route-handlers kunnen synchroon of asynchroon zijn.
Lambda-expressie
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 functie
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Exemplaarmethode
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";
}
}
Statische methode
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", HelloHandler.Hello);
app.Run();
class HelloHandler
{
public static string Hello()
{
return "Hello static method";
}
}
Eindpunt gedefinieerd buiten Program.cs
Minimale API's hoeven zich niet in Program.cste bevinden.
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" });
});
}
}
Zie ook Routegroepen verderop in dit artikel.
Benoemde eindpunten en het genereren van koppelingen
Eindpunten kunnen namen krijgen om URL's naar het eindpunt te genereren. Als u een benoemd eindpunt gebruikt, hoeft u geen vaste codepaden in een app te gebruiken:
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();
De voorgaande code geeft The link to the hello route is /hello weer vanaf het / eindpunt.
OPMERKING: Eindpuntnamen zijn hoofdlettergevoelig.
Namen van eindpunten:
- De naam moet wereldwijd uniek zijn.
- Worden gebruikt als de OpenAPI-bewerkings-id wanneer OpenAPI-ondersteuning is ingeschakeld. Zie OpenAPI voor meer informatie.
Routeparameters
Routeparameters kunnen worden vastgelegd als onderdeel van de definitie van het routepatroon:
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();
De voorgaande code retourneert The user id is 3 and book id is 7 van de URI /users/3/books/7.
De route-handler kan de parameters declareren die moeten worden vastgelegd. Wanneer een aanvraag wordt ingediend bij een route met parameters die zijn gedeclareerd om vast te leggen, worden de parameters geparseerd en doorgegeven aan de handler. Hierdoor kunt u de waarden eenvoudig op een veilige manier vastleggen. In de voorgaande code zijn userId en bookId beide int.
Als in de voorgaande code een van beide routewaarden niet naar een intkan worden geconverteerd, wordt er een uitzondering gegenereerd. De GET-aanvraag /users/hello/books/3 genereert de volgende uitzondering:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Jokertekens en alle routes vangen
De volgende catch-all route retourneert Routing to hello van het eindpunt '/posts/hello':
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Routebeperkingen
Routebeperkingen beperken het overeenkomende gedrag van een route.
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();
In de volgende tabel ziet u de voorgaande routesjablonen en hun gedrag:
| Routesjabloon | Voorbeeld van overeenkomende URI |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Zie de naslaginformatie over routebeperkingenin Routering in ASP.NET Core voor meer informatie.
Routegroepen
Met de MapGroup-extensiemethode kunt u groepen eindpunten organiseren met een gemeenschappelijk voorvoegsel. Het vermindert terugkerende code en maakt het mogelijk om hele groepen eindpunten aan te passen met één aanroep naar methoden zoals RequireAuthorization en WithMetadata waarmee metagegevens van eindpuntenworden toegevoegd.
Met de volgende code worden bijvoorbeeld twee vergelijkbare groepen eindpunten gemaakt:
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 dit scenario kunt u een relatief adres gebruiken voor de Location-header in het 201 Created resultaat:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
De eerste groep eindpunten komt alleen overeen met aanvragen die zijn voorafgegaan door /public/todos en zijn toegankelijk zonder verificatie. De tweede groep eindpunten komt alleen overeen met aanvragen die zijn voorafgegaan door /private/todos en vereisen verificatie.
De QueryPrivateTodoseindpuntfilterfactory is een lokale functie waarmee de TodoDb parameters van de route-handler worden gewijzigd om toegang te krijgen tot privétodogegevens en deze op te slaan.
Routegroepen ondersteunen ook geneste groepen en complexe voorvoegselpatronen met routeparameters en beperkingen. In het volgende voorbeeld kan de routehandler die is toegewezen aan de user groep, de {org} en {group} routeparameters vastleggen die zijn gedefinieerd in de voorvoegsels van de buitenste groep.
Het voorvoegsel kan ook leeg zijn. Dit kan handig zijn voor het toevoegen van eindpuntmetagegevens of filters aan een groep eindpunten zonder het routepatroon te wijzigen.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Het toevoegen van filters of metagegevens aan een groep gedraagt zich op dezelfde manier als het afzonderlijk toevoegen van filters of metagegevens aan elk eindpunt voordat u extra filters of metagegevens toevoegt die mogelijk zijn toegevoegd aan een binnenste groep of specifiek eindpunt.
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);
});
In het bovenstaande voorbeeld registreert het buitenste filter de binnenkomende aanvraag voordat het binnenste filter de kans krijgt om dat te doen, hoewel het binnenste filter als tweede is toegevoegd. Omdat de filters zijn toegepast op verschillende groepen, maakt de volgorde die ze ten opzichte van elkaar zijn toegevoegd niet uit. De volgorde waarin filters worden toegevoegd, maakt uit of ze op dezelfde groep of een specifiek eindpunt worden toegepast.
Een aanvraag voor /outer/inner/ meldt het volgende:
/outer group filter
/inner group filter
MapGet filter
Parameterbinding
Parameterbinding is het proces van het converteren van aanvraaggegevens naar sterk getypte parameters die worden uitgedrukt door route-handlers. Een bindingsbron bepaalt waar parameters van afhankelijk zijn. Bindingsbronnen kunnen expliciet of afgeleid zijn op basis van de HTTP-methode en het parametertype.
Ondersteunde bindingsbronnen:
- Routewaarden
- Querystring
- Header
- Hoofdtekst (als JSON)
- Services geleverd door afhankelijkheidsinjectie
- Custom
Binding vanuit formulierwaarden wordt niet systeemeigen ondersteund in .NET 6 en 7.
De volgende GET route-handler maakt gebruik van enkele van deze parameterbindingsbronnen:
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 { }
In de volgende tabel ziet u de relatie tussen de parameters die in het voorgaande voorbeeld worden gebruikt en de bijbehorende bindingsbronnen.
| Parameter | Bindingsbron |
|---|---|
id |
routewaarde |
page |
Queryreeks |
customHeader |
header |
service |
Geleverd door afhankelijkheidsinjectie |
De HTTP-methodenGET, HEADen OPTIONSDELETE binden niet impliciet vanuit de hoofdtekst. Om te binden vanuit de body (zoals JSON) voor deze HTTP-methoden, bindt u expliciet met [FromBody] of leest u van de HttpRequest.
In het volgende voorbeeld gebruiken we een bindingsbron uit de hoofdtekst (in JSON-formaat) voor de person parameter.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/", (Person person) => { });
record Person(string Name, int Age);
De parameters in de voorgaande voorbeelden zijn allemaal automatisch afhankelijk van aanvraaggegevens. Ter illustratie van het gemak dat parameterbinding biedt, laten de volgende routehandlers zien hoe u aanvraaggegevens rechtstreeks vanuit de aanvraag kunt lezen:
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>();
// ...
});
Expliciete parameterbinding
Kenmerken kunnen worden gebruikt om expliciet te declareren waar parameters van afhankelijk zijn.
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 | Bindingsbron |
|---|---|
id |
routewaarde met de naam id |
page |
querystring met de naam "p" |
service |
Geleverd door afhankelijkheidsinjectie |
contentType |
header met de naam "Content-Type" |
Note
Binding vanuit formulierwaarden wordt niet systeemeigen ondersteund in .NET 6 en 7.
Parameterbinding met afhankelijkheidsinjectie
Parameterbinding voor minimale API's verbindt parameters via afhankelijkheidsinjectie wanneer het type is geconfigureerd als een service. Het is niet nodig om het [FromServices] kenmerk expliciet toe te passen op een parameter. In de volgende code retourneren beide acties de tijd:
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();
Optionele parameters
In route-handlers gedeclareerde parameters worden als verplicht behandeld.
- Als een aanvraag overeenkomt met de route, wordt de route-handler alleen uitgevoerd als alle vereiste parameters zijn opgegeven in de aanvraag.
- Fout bij het opgeven van alle vereiste parameters resulteert in een fout.
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 geretourneerd |
/products |
BadHttpRequestException: De vereiste parameter 'int pageNumber' is niet opgegeven uit de querytekenreeks. |
/products/1 |
HTTP 404-fout, geen overeenkomende route |
Als u optioneel wilt maken pageNumber , definieert u het type als optioneel of geeft u een standaardwaarde op:
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 geretourneerd |
/products |
1 geretourneerd |
/products2 |
1 geretourneerd |
De voorgaande null-waarde en standaardwaarde zijn van toepassing op alle bronnen:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/products", (Product? product) => { });
app.Run();
De voorgaande code roept de methode aan met een null-product als er geen verzoekbody wordt verzonden.
OPMERKING: Als er ongeldige gegevens worden opgegeven en de parameter nullable is, wordt de route-handler niet uitgevoerd.
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 Geretourneerd |
/products |
1 Geretourneerd |
/products?pageNumber=two |
BadHttpRequestException: Kan de parameter "Nullable<int> pageNumber" van 'twee' niet binden. |
/products/two |
HTTP 404-fout, geen overeenkomende route |
Zie de sectie Bindingsfouten voor meer informatie.
Speciale typen
De volgende typen zijn gebonden zonder expliciete kenmerken:
HttpContext: De context die alle informatie over de huidige HTTP-aanvraag of -reactie bevat:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));HttpRequest en HttpResponse: de HTTP-aanvraag en het HTTP-antwoord:
app.MapGet("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));CancellationToken: Het annuleringstoken dat is gekoppeld aan de huidige HTTP-aanvraag:
app.MapGet("/", async (CancellationToken cancellationToken) => await MakeLongRunningRequestAsync(cancellationToken));ClaimsPrincipal: De gebruiker die is gekoppeld aan de aanvraag, gebonden aan HttpContext.User:
app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
Verbind de verzoekinhoud als een Stream of PipeReader
De aanvraagtekst kan worden gebonden als een Stream of PipeReader om efficiënt ondersteuning te bieden voor scenario's waarin de gebruiker gegevens moet verwerken en:
- Sla de gegevens op in Blob Storage of zet de gegevens in een wachtrij bij een wachtrijprovider.
- De opgeslagen gegevens verwerken met een werkproces of een cloudfunctie.
De gegevens kunnen bijvoorbeeld worden verzonden naar Azure Queue Storage- of worden opgeslagen in Azure Blob Storage-.
Met de volgende code wordt een achtergrondwachtrij geĂŻmplementeerd:
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;
}
Met de volgende code wordt de hoofdtekst van de aanvraag gekoppeld aan een 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);
});
De volgende code toont het volledige Program.cs bestand:
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();
- Bij het lezen van gegevens is het
Streamhetzelfde object alsHttpRequest.Body. - De aanvraagbody wordt niet standaard gebufferd. Nadat de hoofdtekst is gelezen, kan het niet worden terugspoelen. De stream kan niet meerdere keren worden gelezen.
- De
StreamenPipeReaderzijn niet bruikbaar buiten de minimale actie-handler omdat de onderliggende buffers worden verwijderd of hergebruikt.
Bestandsuploads met behulp van IFormFile en IFormFileCollection
De volgende code maakt gebruik van IFormFile en IFormFileCollection om het bestand te uploaden:
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();
Geverifieerde aanvragen voor het uploaden van bestanden worden ondersteund met behulp van een Autorisatieheader, een clientcertificaatof een cookie-header.
Er is geen ingebouwde ondersteuning voor antivervalsing in ASP.NET Core in .NET 7.
Antivervalsingsfunctie is beschikbaar in ASP.NET Core in .NET 8 of hoger. Het kan echter worden geĂŻmplementeerd met behulp van de IAntiforgery-service.
Matrices en tekenreekswaarden binden vanuit headers en queryreeksen
De volgende code demonstreert het binden van queryreeksen aan een matrix van primitieve typen, tekenreeksmatrices en 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]}");
Het binden van querystrings of headerwaarden aan een array van complexe typen wordt ondersteund wanneer het type TryParse is geĂŻmplementeerd. De volgende code wordt gekoppeld aan een tekenreeksmatrix en retourneert alle items met de opgegeven tags:
// 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();
});
De volgende code toont het model en de vereiste TryParse implementatie:
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;
}
}
De volgende code wordt gekoppeld aan een int matrix:
// 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();
});
Als u de voorgaande code wilt testen, voegt u het volgende eindpunt toe om de database te vullen met Todo items:
// POST /todoitems/batch
app.MapPost("/todoitems/batch", async (Todo[] todos, TodoDb db) =>
{
await db.Todos.AddRangeAsync(todos);
await db.SaveChangesAsync();
return Results.Ok(todos);
});
Gebruik een hulpprogramma voor het testen van API's zoals HttpRepl om de volgende gegevens naar het vorige eindpunt te verzenden:
[
{
"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"
}
}
]
De volgende code wordt gekoppeld aan de headersleutel X-Todo-Id en retourneert de Todo items met overeenkomende Id waarden:
// 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
Wanneer een querystring wordt string[] gekoppeld, zal bij afwezigheid van een overeenkomende querystringwaarde een lege array ontstaan in plaats van een null-waarde.
Parameterbinding voor argumentlijsten met [AsParameters]
AsParametersAttribute maakt eenvoudige parameterbinding mogelijk voor typen en geen complexe of recursieve modelbinding.
Houd rekening met de volgende 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.
Houd rekening met het volgende GET eindpunt:
app.MapGet("/todoitems/{id}",
async (int Id, TodoDb Db) =>
await Db.Todos.FindAsync(Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
Het volgende struct kan worden gebruikt om de voorgaande gemarkeerde parameters te vervangen:
struct TodoItemRequest
{
public int Id { get; set; }
public TodoDb Db { get; set; }
}
Het geherstructureerde GET eindpunt maakt gebruik van het voorgaande struct met het kenmerk AsParameters :
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());
De volgende code toont extra eindpunten in de 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();
});
De volgende klassen worden gebruikt om de parameterlijsten te herstructureren:
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!;
}
De volgende code toont de geherstructureerde eindpunten die gebruikmaken van AsParameters en de voorgaande struct en 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();
});
De volgende record typen kunnen worden gebruikt om de voorgaande parameters te vervangen:
record TodoItemRequest(int Id, TodoDb Db);
record CreateTodoItemRequest(TodoItemDTO Dto, TodoDb Db);
record EditTodoItemRequest(int Id, TodoItemDTO Dto, TodoDb Db);
Het gebruik van een struct met AsParameters kan krachtiger zijn dan het gebruik van een record type.
De volledige voorbeeldcode in de opslagplaats AspNetCore.Docs.Samples .
Aangepaste binding
Er zijn drie manieren om parameterbinding aan te passen:
- Voor route-, query- en headerbindingsbronnen bindt u aangepaste typen door een statische
TryParsemethode voor het type toe te voegen. - Beheer het bindingsproces door een
BindAsyncmethode voor een type te implementeren. - Implementeer voor geavanceerde scenario's de IBindableFromHttpContext<TSelf> interface om specifiek aangepaste bindlogic rechtstreeks vanuit de
HttpContextte leveren.
TryParse
TryParse heeft twee API's:
public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);
De volgende code laat Point: 12.3, 10.1 zien met de URI /map?Point=12.3,10.1:
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 heeft de volgende API's:
public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);
De volgende code laat SortBy:xyz, SortDirection:Desc, CurrentPage:99 zien met de URI /products?SortBy=xyz&SortDir=Desc&Page=99:
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
}
Aangepaste parameterbinding met IBindableFromHttpContext
ASP.NET Core biedt ondersteuning voor aangepaste parameterbinding in minimale API's met behulp van de IBindableFromHttpContext<TSelf> interface. Met deze interface, geĂŻntroduceerd met de statische abstracte leden van C# 11, kunt u typen maken die rechtstreeks vanuit een HTTP-context kunnen worden gebonden in route-handlerparameters.
public interface IBindableFromHttpContext<TSelf>
where TSelf : class, IBindableFromHttpContext<TSelf>
{
static abstract ValueTask<TSelf?> BindAsync(HttpContext context, ParameterInfo parameter);
}
Door de IBindableFromHttpContext<TSelf> interface te implementeren, kunt u aangepaste typen maken die hun eigen bindingslogica verwerken vanuit httpContext. Wanneer een route-handler een parameter van dit type bevat, roept het framework automatisch de statische BindAsync-methode aan om het exemplaar te maken:
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}";
});
Hier volgt een voorbeeld van een implementatie van een aangepaste parameter die wordt gekoppeld vanuit een HTTP-header:
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
});
}
}
U kunt ook validatie implementeren binnen uw aangepaste bindingslogica:
app.MapGet("/validated", (ValidatedParameter param) =>
{
if (string.IsNullOrEmpty(param.Value))
{
return Results.BadRequest("Value cannot be empty");
}
return Results.Ok($"Validated value: {param.Value}");
});
De voorbeeldcode weergeven of downloaden (downloaden)
Bindingsfouten
Wanneer de binding mislukt, registreert het framework een foutopsporingsbericht en retourneert het verschillende statuscodes naar de client, afhankelijk van de foutmodus.
| Foutmodus | Type null-parameter | Bindingsbron | Statuscode |
|---|---|---|---|
{ParameterType}.TryParse Retourneert false |
yes | route/query/header | 400 |
{ParameterType}.BindAsync Retourneert null |
yes | custom | 400 |
{ParameterType}.BindAsync Gooit |
maakt niet uit | custom | 500 |
| Kan JSON-hoofdtekst niet deserialiseren | maakt niet uit | body | 400 |
Onjuist inhoudstype (niet application/json) |
maakt niet uit | body | 415 |
Bindingsprioriteit
De regels voor het bepalen van een bindingsbron van een parameter:
- Expliciet kenmerk dat is gedefinieerd voor de parameter (From*-kenmerken) in de volgende volgorde:
- Routewaarden:
[FromRoute] - Queryreeks:
[FromQuery] - Koptekst:
[FromHeader] - Lichaam:
[FromBody] - Dienst:
[FromServices] - Parameterwaarden:
[AsParameters]
- Routewaarden:
- Speciale typen
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)
- Het parametertype heeft een geldige statische
BindAsyncmethode. - Parametertype is een tekenreeks of heeft een geldige statische
TryParsemethode.- Als de parameternaam bestaat in de routesjabloon. In
app.Map("/todo/{id}", (int id) => {});,idis gebonden aan de route. - Afhankelijk van de querytekenreeks.
- Als de parameternaam bestaat in de routesjabloon. In
- Als het parametertype een service is die wordt geleverd door afhankelijkheidsinjectie, wordt die service als bron gebruikt.
- De parameter is afkomstig uit het lichaam.
Opties voor JSON-deserialisatie configureren voor hoofdtekstbinding
De hoofdtekstbindingsbron gebruikt System.Text.Json voor deserialisatie. Het is niet mogelijk om deze standaardinstelling te wijzigen, maar JSON-serialisatie- en deserialisatieopties kunnen worden geconfigureerd.
JSON-deserialisatieopties globaal configureren
Opties die globaal voor een app gelden, kunnen worden geconfigureerd door het aanroepen ConfigureHttpJsonOptions. Het volgende voorbeeld bevat openbare velden en indelingen voor JSON-uitvoer.
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
// }
Omdat de voorbeeldcode zowel serialisatie als deserialisatie configureert, kan deze de uitvoer-JSON lezen NameField en opnemen NameField .
JSON-deserialisatieopties configureren voor een eindpunt
ReadFromJsonAsync heeft overbelastingen die een JsonSerializerOptions object accepteren. Het volgende voorbeeld bevat openbare velden en indelingen voor JSON-uitvoer.
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
// }
Aangezien de voorgaande code de aangepaste opties alleen toepast op deserialisatie, is NameField niet opgenomen in de uitvoer-JSON.
De hoofdtekst van de aanvraag lezen
Lees de hoofdtekst van de aanvraag rechtstreeks met behulp van een HttpContext of HttpRequest parameter:
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();
De voorgaande code:
- Hiermee krijgt u toegang tot de verzoekbody met behulp van HttpRequest.BodyReader.
- Kopieert de hoofdtekst van de aanvraag naar een lokaal bestand.
Responses
Route-handlers ondersteunen de volgende typen retourwaarden:
-
IResultgebaseerd - Dit omvatTask<IResult>enValueTask<IResult> -
string- Dit omvatTask<string>enValueTask<string> -
T(Elk ander type): dit omvatTask<T>enValueTask<T>
| Retourwaarde | Behavior | Content-Type |
|---|---|---|
IResult |
Het framework roept IResult.ExecuteAsync aan | Besloten door de IResult implementatie |
string |
Het raamwerk schrijft de string rechtstreeks naar de respons | text/plain |
T (Elk ander type) |
Het framework JSON-serialiseert het antwoord | application/json |
Zie Antwoorden maken in minimale API-toepassingen voor een uitgebreidere handleiding voor het routeren van handler-retourwaarden
Voorbeeld van retourwaarden
retourwaarden van string
app.MapGet("/hello", () => "Hello World");
JSON-retourwaarden
app.MapGet("/hello", () => new { Message = "Hello World" });
Typgeresultaten retourneren
De volgende code retourneert een TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Het retourneren van TypedResults heeft de voorkeur boven het retourneren van Results. Voor meer informatie, zie TypedResults versus Results.
IResult retourwaarden
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
In het volgende voorbeeld worden de ingebouwde resultaattypen gebruikt om het antwoord aan te passen:
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" }));
Aangepaste 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");
});
Zie Antwoorden maken in Minimale API-toepassingen voor meer voorbeelden.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Ingebouwde resultaten
Algemene helpers voor resultaten bestaan in de Results en TypedResults statische klassen. Het retourneren van TypedResults heeft de voorkeur boven het retourneren van Results. Voor meer informatie, zie TypedResults versus Results.
Resultaten aanpassen
Toepassingen kunnen antwoorden beheren door een aangepast IResult type te implementeren. De volgende code is een voorbeeld van een HTML-resultaattype:
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);
}
}
U wordt aangeraden een extensiemethode toe te voegen aan Microsoft.AspNetCore.Http.IResultExtensions om deze aangepaste resultaten beter te detecteren.
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();
Getypte resultaten
De IResult interface kan waarden vertegenwoordigen die worden geretourneerd van minimale API's die niet gebruikmaken van de impliciete ondersteuning voor JSON die het geretourneerde object serialiseren naar het HTTP-antwoord. De statische resultaten klasse wordt gebruikt om verschillende IResult objecten te maken die verschillende typen antwoorden vertegenwoordigen. Stel bijvoorbeeld de antwoordstatuscode in of omleiden naar een andere URL.
De typen die worden geĂŻmplementeerd IResult , zijn openbaar, waardoor typeverklaringen kunnen worden gebruikt bij het testen. Voorbeeld:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
U kunt de retourtypen van de bijbehorende methoden in de klasse Static TypedResults bekijken om het juiste openbare IResult type te vinden waarnaar moet worden gecast.
Zie Antwoorden maken in Minimale API-toepassingen voor meer voorbeelden.
Filters
Zie Filters in minimale API-apps
Authorization
Routes kunnen worden beveiligd met autorisatiebeleid. Deze kunnen worden gedeclareerd via het [Authorize] kenmerk of met behulp van de RequireAuthorization methode:
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();
De voorgaande code kan worden geschreven met RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
In het volgende voorbeeld wordt gebruikgemaakt van autorisatie op basis van beleid:
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();
Niet-geverifieerde gebruikers toegang geven tot een eindpunt
Hiermee [AllowAnonymous] hebben niet-geverifieerde gebruikers toegang tot eindpunten:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Routes kunnen CORS-ondersteuning krijgen door gebruik te maken van CORS-beleid. CORS kan worden gedeclareerd via het [EnableCors] kenmerk of met behulp van de RequireCors methode. Met de volgende voorbeelden schakelt u CORS in:
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();
Zie CORS (Cross-Origin Requests) inschakelen in ASP.NET Core voor meer informatie
Zie ook
Dit document:
- Biedt een beknopt overzicht voor minimale API's.
- Is bedoeld voor ervaren ontwikkelaars. Zie Zelfstudie: Een minimale API maken met ASP.NET Core voor een inleiding
De minimale API's bestaan uit:
- WebApplication en WebApplicationBuilder
- Route-handlers
WebApplication
De volgende code wordt gegenereerd door een ASP.NET Core-sjabloon:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
De voorgaande code kan worden gemaakt via dotnet new web de opdrachtregel of door de lege websjabloon in Visual Studio te selecteren.
Met de volgende code wordt een WebApplication (app) gemaakt zonder expliciet een WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create initialiseert een nieuw exemplaar van de WebApplication klasse met vooraf geconfigureerde standaardwaarden.
Werken met poorten
Wanneer een web-app wordt gemaakt met Visual Studio of dotnet new, wordt er een Properties/launchSettings.json bestand gemaakt waarin de poorten worden opgegeven waarop de app reageert. In de volgende voorbeelden van poortinstellingen retourneert het uitvoeren van de app vanuit Visual Studio een foutdialoogvenster Unable to connect to web server 'AppName'. Voer de volgende voorbeelden die de poort wijzigen uit vanuit de opdrachtregel.
In de volgende secties wordt de poort ingesteld waar de app op reageert.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
In de voorgaande code reageert de app op poort 3000.
Meerdere poorten
In de volgende code reageert de app op poort 3000 en 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
De poort instellen via de opdrachtregel
Met de volgende opdracht reageert de app op 7777poort:
dotnet run --urls="https://localhost:7777"
Als het Kestrel eindpunt ook in het appsettings.json bestand is geconfigureerd, wordt de opgegeven URL van het appsettings.json bestand gebruikt. Zie de eindpuntconfiguratie voor meer informatie Kestrel
De poort uit de omgevingsvariabelen lezen
Met de volgende code wordt de poort uit de omgeving gelezen:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
De voorkeursmethode voor het instellen van de poort vanuit de omgeving is het gebruik van de ASPNETCORE_URLS omgevingsvariabele, die wordt weergegeven in de volgende sectie.
De poorten instellen via de omgevingsvariabele ASPNETCORE_URLS
De ASPNETCORE_URLS omgevingsvariabele is beschikbaar om de poort in te stellen:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS ondersteunt meerdere URL's:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Luisteren op alle interfaces
In de volgende voorbeelden ziet u hoe u luistert op alle interfaces
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();
Luisteren op alle interfaces met behulp van ASPNETCORE_URLS
De voorgaande voorbeelden kunnen ASPNETCORE_URLS gebruiken
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
HTTPS specificeren met ontwikkelingscertificaat
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Zie Het ASP.NET Core HTTPS-ontwikkelingscertificaat vertrouwen in Windows en macOS voor meer informatie over het ontwikkelingscertificaat.
HTTPS opgeven met behulp van een aangepast certificaat
In de volgende secties ziet u hoe u het aangepaste certificaat opgeeft met behulp van het appsettings.json bestand en via de configuratie.
Geef het aangepaste certificaat op met appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Het aangepaste certificaat opgeven via configuratie
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();
De certificaat-API's gebruiken
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();
De omgeving lezen
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();
Zie ASP.NET Core runtime-omgevingen voor meer informatie over het gebruik van de omgeving
Configuration
De volgende code leest uit het configuratiesysteem:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Hello";
app.MapGet("/", () => message);
app.Run();
Zie Configuratie in ASP.NET Core- voor meer informatie
Logging
Met de volgende code wordt bij de start van de toepassing een bericht naar het logboek geschreven.
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Zie Logboekregistratie in .NET en ASP.NET Core voor meer informatie
Toegang tot de Dependency Injection-container (DI)
De volgende code laat zien hoe u services uit de DI-container kunt ophalen tijdens het opstarten van de toepassing:
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();
Zie Afhankelijkheidsinjectie in ASP.NET Corevoor meer informatie.
WebApplicationBuilder
Deze sectie bevat voorbeeldcode met behulp van WebApplicationBuilder.
De inhoudsbasis, de naam van de toepassing en de omgeving wijzigen
De volgende code stelt de inhoudsmap, toepassingsnaam en omgeving in.
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 initialiseert een nieuw exemplaar van de WebApplicationBuilder-klasse met vooraf geconfigureerde standaardwaarden.
Zie ASP.NET Overzicht van basisbeginselen van Core voor meer informatie
Het wijzigen van de hoofdmap, app-naam en omgeving via omgevingsvariabelen of de opdrachtregel
In de volgende tabel ziet u de omgevingsvariabele en het opdrachtregelargument dat wordt gebruikt om de hoofdmap, de naam van de app en de omgeving te wijzigen:
| feature | Omgevingsvariabele | Opdrachtregelargument |
|---|---|---|
| Toepassingsnaam | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Naam van de omgeving | ASPNETCORE_ENVIRONMENT | --environment |
| Hoofdmap van inhoud | ASPNETCORE_CONTENTROOT | --contentRoot |
Configuratieproviders toevoegen
In het volgende voorbeeld wordt de INI-configuratieprovider toegevoegd:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Zie Bestandsconfiguratieproviders in Configuratie in ASP.NET Core voor gedetailleerde informatie.
Leesconfiguratie
Standaard leest de WebApplicationBuilder de configuratie uit meerdere bronnen, waaronder:
-
appSettings.jsonenappSettings.{environment}.json - Omgevingsvariabelen
- De opdrachtregel
Zie De standaardconfiguratiein Configuratie in ASP.NET Core voor een volledige lijst met gelezen configuratiebronnen
De volgende code leest HelloKey uit de configuratie en geeft de waarde weer op het / eindpunt. Als de configuratiewaarde null is, wordt 'Hallo' toegewezen aan message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
De omgeving lezen
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Logboekregistratieproviders toevoegen
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();
Services toevoegen
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();
De IHostBuilder aanpassen
Bestaande uitbreidingsmethoden IHostBuilder kunnen worden geopend met behulp van de eigenschap Host:
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();
De IWebHostBuilder aanpassen
Extensiemethoden zijn IWebHostBuilder toegankelijk via de eigenschap WebApplicationBuilder.WebHost .
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();
De webroot wijzigen
De webroot is standaard relatief ten opzichte van de inhoudsmap in de map wwwroot. De webroot is waar de Static File Middleware zoekt naar statische bestanden. Webhoofdmap kan worden gewijzigd met WebHostOptions, de opdrachtregel of met de UseWebRoot methode:
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Afhankelijkheidsinjectiecontainer op maat (DI)
In het volgende voorbeeld wordt Autofac gebruikt:
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();
Middleware toevoegen
Alle bestaande ASP.NET Core-middleware kan worden geconfigureerd op WebApplication.
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Zie ASP.NET Core Middleware voor meer informatie
Uitzonderingspagina voor ontwikkelaars
WebApplication.CreateBuilder initialiseert een nieuw exemplaar van de WebApplicationBuilder klasse met vooraf geconfigureerde standaardwaarden. De uitzonderingspagina voor ontwikkelaars is ingeschakeld in de vooraf geconfigureerde standaardinstellingen. Wanneer de volgende code wordt uitgevoerd in de ontwikkelomgeving, wordt er genavigeerd naar een vriendelijke pagina die de uitzondering toont.
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
De volgende tabel bevat enkele van de middleware die vaak wordt gebruikt met minimale API's.
| Middleware | Description | API |
|---|---|---|
| Authentication | Biedt ondersteuning voor verificatie. | UseAuthentication |
| Authorization | Biedt autorisatieondersteuning. | UseAuthorization |
| CORS | Hiermee wordt Cross-Origin Resource Sharing geconfigureerd. | UseCors |
| Uitzonderingshandler | Wereldwijd worden uitzonderingen verwerkt die worden gegenereerd door de middleware-pijplijn. | UseExceptionHandler |
| Doorgestuurde headers | Hiermee worden via een proxy ontvangen headers doorgestuurd naar het huidige verzoek. | UseForwardedHeaders |
| HTTPS-omleiding | Alle HTTP-aanvragen worden omgeleid naar HTTPS. | UseHttpsRedirection |
| HTTP Strict Transport Security (HSTS) | Middleware voor beveiligingsverbeteringen waarmee een speciale antwoordheader wordt toegevoegd. | UseHsts |
| Logboekregistratie aanvragen | Biedt ondersteuning voor het registreren van HTTP-aanvragen en -antwoorden. | UseHttpLogging |
| Logboekregistratie van W3C-aanvragen | Biedt ondersteuning voor het registreren van HTTP-aanvragen en -antwoorden in de W3C-indeling. | UseW3CLogging |
| Antwoordcaching | Biedt ondersteuning voor het opslaan van antwoorden in cache. | UseResponseCaching |
| Antwoordcompressie | Biedt ondersteuning voor het comprimeren van antwoorden. | UseResponseCompression |
| Session | Biedt ondersteuning voor het beheren van gebruikerssessies. | UseSession |
| Statische bestanden | Biedt ondersteuning voor het leveren van statische bestanden en bladeren door mappen. | UseStaticFiles, UseFileServer |
| WebSockets | Hiermee schakelt u het WebSockets-protocol in. | UseWebSockets |
Verwerking van aanvragen
In de volgende secties worden routering, parameterbinding en antwoorden beschreven.
Routing
Een geconfigureerde WebApplication ondersteunt Map{Verb} en 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();
Route-handlers
Route-handlers zijn methoden die worden uitgevoerd wanneer de route overeenkomt. Route-handlers kunnen een functie zijn van elke vorm, inclusief synchrone of asynchrone. Route-handlers kunnen een lambda-expressie, een lokale functie, een instantiemethode of een statische methode zijn.
Lambda-expressie
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 functie
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Exemplaarmethode
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";
}
}
Statische methode
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", HelloHandler.Hello);
app.Run();
class HelloHandler
{
public static string Hello()
{
return "Hello static method";
}
}
Benoemde eindpunten en het genereren van koppelingen
Eindpunten kunnen namen krijgen om URL's naar het eindpunt te genereren. Als u een benoemd eindpunt gebruikt, hoeft u geen vaste codepaden in een app te gebruiken:
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();
De voorgaande code geeft The link to the hello endpoint is /hello weer vanaf het / eindpunt.
OPMERKING: Eindpuntnamen zijn hoofdlettergevoelig.
Namen van eindpunten:
- De naam moet wereldwijd uniek zijn.
- Worden gebruikt als de OpenAPI-bewerkings-id wanneer OpenAPI-ondersteuning is ingeschakeld. Zie OpenAPI voor meer informatie.
Routeparameters
Routeparameters kunnen worden vastgelegd als onderdeel van de definitie van het routepatroon:
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();
De voorgaande code retourneert The user id is 3 and book id is 7 van de URI /users/3/books/7.
De route-handler kan de parameters declareren die moeten worden vastgelegd. Wanneer een verzoek wordt gedaan op een route met parameters die zijn ingesteld om vast te leggen, worden de parameters geparseerd en doorgegeven aan de handler. Hierdoor kunt u de waarden eenvoudig op een veilige manier vastleggen. In de voorgaande code zijn userId en bookId beide int.
Als in de voorgaande code een van beide routewaarden niet naar een intkan worden geconverteerd, wordt er een uitzondering gegenereerd. De GET-aanvraag /users/hello/books/3 genereert de volgende uitzondering:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Jokertekens en alle routes vangen
De volgende catch-all route retourneert Routing to hello van het eindpunt '/posts/hello':
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Routebeperkingen
Routebeperkingen beperken het overeenkomende gedrag van een route.
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();
In de volgende tabel ziet u de voorgaande routesjablonen en hun gedrag:
| Routesjabloon | Voorbeeld van overeenkomende URI |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Zie de naslaginformatie over routebeperkingenin Routering in ASP.NET Core voor meer informatie.
Parameterbinding
Parameterbinding is het proces van het converteren van aanvraaggegevens naar sterk getypte parameters die worden uitgedrukt door route-handlers. Een bindingsbron bepaalt waar parameters van afhankelijk zijn. Bindingsbronnen kunnen expliciet of afgeleid zijn op basis van de HTTP-methode en het parametertype.
Ondersteunde bindingsbronnen:
- Routewaarden
- Querystring
- Header
- Hoofdtekst (als JSON)
- Services geleverd door afhankelijkheidsinjectie
- Custom
Note
Binding vanuit formulierwaarden wordt niet systeemeigen ondersteund in .NET.
In het volgende voorbeeld van een GET-routehandler worden enkele van deze parameterbindingsbronnen gebruikt.
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 { }
In de volgende tabel ziet u de relatie tussen de parameters die in het voorgaande voorbeeld worden gebruikt en de bijbehorende bindingsbronnen.
| Parameter | Bindingsbron |
|---|---|
id |
routewaarde |
page |
Queryreeks |
customHeader |
header |
service |
Geleverd door afhankelijkheidsinjectie |
De HTTP-methodenGET, HEADen OPTIONSDELETE binden niet impliciet vanuit de hoofdtekst. Om te binden vanuit de body (zoals JSON) voor deze HTTP-methoden, bindt u expliciet met [FromBody] of leest u van de HttpRequest.
In het volgende voorbeeld gebruiken we een bindingsbron uit de hoofdtekst (in JSON-formaat) voor de person parameter.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/", (Person person) => { });
record Person(string Name, int Age);
De parameters in de voorgaande voorbeelden zijn allemaal automatisch afhankelijk van aanvraaggegevens. Ter illustratie van het gemak dat parameterbinding biedt, laten de volgende voorbeeldroute-handlers zien hoe u aanvraaggegevens rechtstreeks vanuit de aanvraag kunt lezen:
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>();
// ...
});
Expliciete parameterbinding
Kenmerken kunnen worden gebruikt om expliciet te declareren waar parameters van afhankelijk zijn.
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 | Bindingsbron |
|---|---|
id |
routewaarde met de naam id |
page |
querystring met de naam "p" |
service |
Geleverd door afhankelijkheidsinjectie |
contentType |
header met de naam "Content-Type" |
Note
Binding vanuit formulierwaarden wordt niet systeemeigen ondersteund in .NET.
Parameterbinding met DI
Parameterbinding voor minimale API's verbindt parameters via afhankelijkheidsinjectie wanneer het type is geconfigureerd als een service. Het is niet nodig om het [FromServices] kenmerk expliciet toe te passen op een parameter. In de volgende code retourneren beide acties de tijd:
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();
Optionele parameters
In route-handlers gedeclareerde parameters worden als verplicht behandeld.
- Als een aanvraag overeenkomt met de route, wordt de route-handler alleen uitgevoerd als alle vereiste parameters zijn opgegeven in de aanvraag.
- Fout bij het opgeven van alle vereiste parameters resulteert in een fout.
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 geretourneerd |
/products |
BadHttpRequestException: De vereiste parameter 'int pageNumber' is niet opgegeven uit de querytekenreeks. |
/products/1 |
HTTP 404-fout, geen overeenkomende route |
Als u optioneel wilt maken pageNumber , definieert u het type als optioneel of geeft u een standaardwaarde op:
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 geretourneerd |
/products |
1 geretourneerd |
/products2 |
1 geretourneerd |
De voorgaande null-waarde en standaardwaarde zijn van toepassing op alle bronnen:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/products", (Product? product) => { });
app.Run();
De voorgaande code roept de methode aan met een null-product als er geen verzoekbody wordt verzonden.
OPMERKING: Als er ongeldige gegevens worden opgegeven en de parameter nullable is, wordt de route-handler niet uitgevoerd.
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 Geretourneerd |
/products |
1 Geretourneerd |
/products?pageNumber=two |
BadHttpRequestException: Kan de parameter "Nullable<int> pageNumber" van 'twee' niet binden. |
/products/two |
HTTP 404-fout, geen overeenkomende route |
Zie de sectie Bindingsfouten voor meer informatie.
Speciale typen
De volgende typen zijn gebonden zonder expliciete kenmerken:
HttpContext: De context die alle informatie over de huidige HTTP-aanvraag of -reactie bevat:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));HttpRequest en HttpResponse: de HTTP-aanvraag en het HTTP-antwoord:
app.MapGet("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));CancellationToken: Het annuleringstoken dat is gekoppeld aan de huidige HTTP-aanvraag:
app.MapGet("/", async (CancellationToken cancellationToken) => await MakeLongRunningRequestAsync(cancellationToken));ClaimsPrincipal: De gebruiker die is gekoppeld aan de aanvraag, gebonden aan HttpContext.User:
app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
Aangepaste binding
Er zijn twee manieren om parameterbinding aan te passen:
- Voor route-, query- en headerbindingsbronnen bindt u aangepaste typen door een statische
TryParsemethode voor het type toe te voegen. - Beheer het bindingsproces door een
BindAsyncmethode voor een type te implementeren.
TryParse
TryParse heeft twee API's:
public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);
De volgende code laat Point: 12.3, 10.1 zien met de URI /map?Point=12.3,10.1:
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 heeft de volgende API's:
public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);
De volgende code laat SortBy:xyz, SortDirection:Desc, CurrentPage:99 zien met de URI /products?SortBy=xyz&SortDir=Desc&Page=99:
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
}
Bindingsfouten
Wanneer de binding mislukt, registreert het framework een foutopsporingsbericht en retourneert het verschillende statuscodes naar de client, afhankelijk van de foutmodus.
| Foutmodus | Type null-parameter | Bindingsbron | Statuscode |
|---|---|---|---|
{ParameterType}.TryParse Retourneert false |
yes | route/query/header | 400 |
{ParameterType}.BindAsync Retourneert null |
yes | custom | 400 |
{ParameterType}.BindAsync Gooit |
maakt niet uit | custom | 500 |
| Kan JSON-hoofdtekst niet deserialiseren | maakt niet uit | body | 400 |
Onjuist inhoudstype (niet application/json) |
maakt niet uit | body | 415 |
Bindingsprioriteit
De regels voor het bepalen van een bindingsbron van een parameter:
- Expliciet kenmerk dat is gedefinieerd voor de parameter (From*-kenmerken) in de volgende volgorde:
- Routewaarden:
[FromRoute] - Queryreeks:
[FromQuery] - Koptekst:
[FromHeader] - Lichaam:
[FromBody] - Dienst:
[FromServices]
- Routewaarden:
- Speciale typen
- Het parametertype heeft een geldige
BindAsyncmethode. - Parametertype is een tekenreeks of heeft een geldige
TryParsemethode.- Als de parameternaam bestaat in de routesjabloon. In
app.Map("/todo/{id}", (int id) => {});,idis gebonden aan de route. - Afhankelijk van de querytekenreeks.
- Als de parameternaam bestaat in de routesjabloon. In
- Als het parametertype een service is die wordt geleverd door afhankelijkheidsinjectie, wordt die service als bron gebruikt.
- De parameter is afkomstig uit het lichaam.
JSON-binding aanpassen
De hoofdtekstbindingsbron maakt gebruik van System.Text.Json voor deserialisatie. Het is niet mogelijk om deze standaardinstelling te wijzigen, maar de binding kan worden aangepast met behulp van andere technieken die eerder zijn beschreven. Als u opties voor JSON-serializer wilt aanpassen, gebruikt u code die vergelijkbaar is met de volgende:
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;
}
De voorgaande code:
- Hiermee configureert u zowel de standaard-JSON-opties voor invoer als uitvoer.
- Retourneert de volgende JSON
Bij het plaatsen{ "id": 1, "name": "Joe Smith" }{ "Id": 1, "Name": "Joe Smith" }
De hoofdtekst van de aanvraag lezen
Lees de hoofdtekst van de aanvraag rechtstreeks met behulp van een HttpContext of HttpRequest parameter:
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();
De voorgaande code:
- Hiermee krijgt u toegang tot de verzoekbody met behulp van HttpRequest.BodyReader.
- Kopieert de hoofdtekst van de aanvraag naar een lokaal bestand.
Responses
Route-handlers ondersteunen de volgende typen retourwaarden:
-
IResultgebaseerd - Dit omvatTask<IResult>enValueTask<IResult> -
string- Dit omvatTask<string>enValueTask<string> -
T(Elk ander type): dit omvatTask<T>enValueTask<T>
| Retourwaarde | Behavior | Content-Type |
|---|---|---|
IResult |
Het framework roept IResult.ExecuteAsync aan | Besloten door de IResult implementatie |
string |
Het raamwerk schrijft de string rechtstreeks naar de respons | text/plain |
T (Elk ander type) |
Het framework zal JSON het antwoord serialiseren | application/json |
Voorbeeld van retourwaarden
retourwaarden van string
app.MapGet("/hello", () => "Hello World");
JSON-retourwaarden
app.MapGet("/hello", () => new { Message = "Hello World" });
IResult retourwaarden
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
In het volgende voorbeeld worden de ingebouwde resultaattypen gebruikt om het antwoord aan te passen:
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" }));
Aangepaste 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"));
Ingebouwde resultaten
Algemene helpers voor resultaten bestaan in de Microsoft.AspNetCore.Http.Results statische klasse.
| Description | Antwoordtype | Statuscode | API |
|---|---|---|---|
| Een JSON-antwoord schrijven met geavanceerde opties | application/json | 200 | Results.Json |
| Een JSON-antwoord schrijven | application/json | 200 | Results.Ok |
| Een tekstantwoord schrijven | platte tekst (standaard), deze is configureerbaar | 200 | Results.Text |
| Het antwoord schrijven als bytes | application/octet-stream (standaard), configureerbaar | 200 | Results.Bytes |
| Een stroom aan bytes naar de respons schrijven | application/octet-stream (standaard), configureerbaar | 200 | Results.Stream |
| Een bestand naar de respons streamen om te downloaden met de content-disposition header | application/octet-stream (standaard), configureerbaar | 200 | Results.File |
| Stel de statuscode in op 404, met een optioneel JSON-antwoord | N/A | 404 | Results.NotFound |
| De statuscode instellen op 204 | N/A | 204 | Results.NoContent |
| Stel de statuscode in op 422, met een optioneel JSON-antwoord | N/A | 422 | Results.UnprocessableEntity |
| Stel de statuscode in op 400, met een optioneel JSON-antwoord | N/A | 400 | Results.BadRequest |
| Stel de statuscode in op 409, met een optioneel JSON-antwoord | N/A | 409 | Results.Conflict |
| Een JSON-object met details van een probleem naar het antwoord schrijven | N/A | 500 (standaard), configureerbaar | Results.Problem |
| Schrijf een probleemdetails-JSON-object met validatiefouten naar het antwoord. | N/A | N.b., configureerbaar | Results.ValidationProblem |
Resultaten aanpassen
Toepassingen kunnen antwoorden beheren door een aangepast IResult type te implementeren. De volgende code is een voorbeeld van een HTML-resultaattype:
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);
}
}
U wordt aangeraden een extensiemethode toe te voegen aan Microsoft.AspNetCore.Http.IResultExtensions om deze aangepaste resultaten beter te detecteren.
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
Routes kunnen worden beveiligd met autorisatiebeleid. Deze kunnen worden gedeclareerd via het [Authorize] kenmerk of met behulp van de RequireAuthorization methode:
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();
De voorgaande code kan worden geschreven met RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
In het volgende voorbeeld wordt gebruikgemaakt van autorisatie op basis van beleid:
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();
Niet-geverifieerde gebruikers toegang geven tot een eindpunt
Hiermee [AllowAnonymous] hebben niet-geverifieerde gebruikers toegang tot eindpunten:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Routes kunnen CORS-ondersteuning krijgen door gebruik te maken van CORS-beleid. CORS kan worden gedeclareerd via het [EnableCors] kenmerk of met behulp van de RequireCors methode. Met de volgende voorbeelden schakelt u CORS in:
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();
Zie CORS (Cross-Origin Requests) inschakelen in ASP.NET Core voor meer informatie