Delen via


Snelzoekgids voor minimale API's

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:

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:

  • UseDeveloperExceptionPage wordt eerst toegevoegd wanneer het HostingEnvironment is "Development".
  • UseRouting wordt als tweede toegevoegd als de gebruikerscode UseRouting nog niet is aangeroepen en als er geconfigureerde eindpunten zijn, bijvoorbeeld app.MapGet.
  • UseEndpoints wordt toegevoegd aan het einde van de middleware-pijplijn als er eindpunten zijn geconfigureerd.
  • UseAuthentication wordt onmiddellijk toegevoegd na UseRouting als de gebruikerscode UseAuthentication nog niet is aangeroepen en als IAuthenticationSchemeProvider kan worden gedetecteerd in de serviceprovider. IAuthenticationSchemeProvider wordt standaard toegevoegd wanneer u AddAuthentication gebruikt, en services worden gedetecteerd met behulp van IServiceProviderIsService.
  • UseAuthorization wordt vervolgens toegevoegd als de gebruikerscode nog niet heeft aangeroepen UseAuthorization en als IAuthorizationHandlerProvider kan worden gedetecteerd in de serviceprovider. IAuthorizationHandlerProvider wordt standaard toegevoegd wanneer u AddAuthorization gebruikt, en services worden gedetecteerd met behulp van IServiceProviderIsService.
  • Door de gebruiker geconfigureerde middleware en eindpunten worden toegevoegd tussen UseRouting en UseEndpoints.

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 UseRouting en UseEndpoints aanroepen 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.json en appSettings.{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.

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 voor IFormFile en IFormFileCollection voor bestandsuploads.
  • Complexe typen: Binden aan verzamelingen en complexe typen uit formulieren, queryreeksen en headers.
  • Aangepaste binding: implementeer aangepaste bindingslogica met behulp van TryParse, BindAsyncof de IBindableFromHttpContext<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, , , Streamen PipeReader.

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:

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:

  1. IResult gebaseerd - Dit omvat Task<IResult> en ValueTask<IResult>
  2. string - Dit omvat Task<string> en ValueTask<string>
  3. T (Elk ander type): dit omvat Task<T> en ValueTask<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

Dit document:

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:

  • UseDeveloperExceptionPage wordt eerst toegevoegd wanneer het HostingEnvironment is "Development".
  • UseRouting wordt als tweede toegevoegd als de gebruikerscode UseRouting nog niet is aangeroepen en als er geconfigureerde eindpunten zijn, bijvoorbeeld app.MapGet.
  • UseEndpoints wordt toegevoegd aan het einde van de middleware-pijplijn als er eindpunten zijn geconfigureerd.
  • UseAuthentication wordt onmiddellijk toegevoegd na UseRouting als de gebruikerscode UseAuthentication nog niet is aangeroepen en als IAuthenticationSchemeProvider kan worden gedetecteerd in de serviceprovider. IAuthenticationSchemeProvider wordt standaard toegevoegd wanneer u AddAuthentication gebruikt, en services worden gedetecteerd met behulp van IServiceProviderIsService.
  • UseAuthorization wordt vervolgens toegevoegd als de gebruikerscode nog niet heeft aangeroepen UseAuthorization en als IAuthorizationHandlerProvider kan worden gedetecteerd in de serviceprovider. IAuthorizationHandlerProvider wordt standaard toegevoegd wanneer u AddAuthorization gebruikt, en services worden gedetecteerd met behulp van IServiceProviderIsService.
  • Door de gebruiker geconfigureerde middleware en eindpunten worden toegevoegd tussen UseRouting en UseEndpoints.

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 UseRouting en UseEndpoints aanroepen 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.json en appSettings.{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.

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 voor IFormFile en IFormFileCollection voor bestandsuploads.
  • Complexe typen: Binden aan verzamelingen en complexe typen uit formulieren, queryreeksen en headers.
  • Aangepaste binding: implementeer aangepaste bindingslogica met behulp van TryParse, BindAsyncof de IBindableFromHttpContext<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, , , Streamen PipeReader.

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:

  1. IResult gebaseerd - Dit omvat Task<IResult> en ValueTask<IResult>
  2. string - Dit omvat Task<string> en ValueTask<string>
  3. T (Elk ander type): dit omvat Task<T> en ValueTask<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

Dit document:

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:

  • UseDeveloperExceptionPage wordt eerst toegevoegd wanneer het HostingEnvironment is "Development".
  • UseRouting wordt als tweede toegevoegd als de gebruikerscode UseRouting nog niet is aangeroepen en als er geconfigureerde eindpunten zijn, bijvoorbeeld app.MapGet.
  • UseEndpoints wordt toegevoegd aan het einde van de middleware-pijplijn als er eindpunten zijn geconfigureerd.
  • UseAuthentication wordt onmiddellijk toegevoegd na UseRouting als de gebruikerscode UseAuthentication nog niet is aangeroepen en als IAuthenticationSchemeProvider kan worden gedetecteerd in de serviceprovider. IAuthenticationSchemeProvider wordt standaard toegevoegd wanneer u AddAuthentication gebruikt, en services worden gedetecteerd met behulp van IServiceProviderIsService.
  • UseAuthorization wordt vervolgens toegevoegd als de gebruikerscode nog niet heeft aangeroepen UseAuthorization en als IAuthorizationHandlerProvider kan worden gedetecteerd in de serviceprovider. IAuthorizationHandlerProvider wordt standaard toegevoegd wanneer u AddAuthorization gebruikt, en services worden gedetecteerd met behulp van IServiceProviderIsService.
  • Door de gebruiker geconfigureerde middleware en eindpunten worden toegevoegd tussen UseRouting en UseEndpoints.

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 UseRouting en UseEndpoints aanroepen 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.json en appSettings.{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.

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 voor IFormFile en IFormFileCollection voor bestandsuploads.
  • Complexe typen: Binden aan verzamelingen en complexe typen uit formulieren, queryreeksen en headers.
  • Aangepaste binding: implementeer aangepaste bindingslogica met behulp van TryParse, BindAsyncof de IBindableFromHttpContext<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, , , Streamen PipeReader.

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:

  1. IResult gebaseerd - Dit omvat Task<IResult> en ValueTask<IResult>
  2. string - Dit omvat Task<string> en ValueTask<string>
  3. T (Elk ander type): dit omvat Task<T> en ValueTask<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

Dit document:

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:

  • UseDeveloperExceptionPage wordt eerst toegevoegd wanneer het HostingEnvironment is "Development".
  • UseRouting wordt als tweede toegevoegd als de gebruikerscode UseRouting nog niet is aangeroepen en als er geconfigureerde eindpunten zijn, bijvoorbeeld app.MapGet.
  • UseEndpoints wordt toegevoegd aan het einde van de middleware-pijplijn als er eindpunten zijn geconfigureerd.
  • UseAuthentication wordt onmiddellijk toegevoegd na UseRouting als de gebruikerscode UseAuthentication nog niet is aangeroepen en als IAuthenticationSchemeProvider kan worden gedetecteerd in de serviceprovider. IAuthenticationSchemeProvider wordt standaard toegevoegd wanneer u AddAuthentication gebruikt, en services worden gedetecteerd met behulp van IServiceProviderIsService.
  • UseAuthorization wordt vervolgens toegevoegd als de gebruikerscode nog niet heeft aangeroepen UseAuthorization en als IAuthorizationHandlerProvider kan worden gedetecteerd in de serviceprovider. IAuthorizationHandlerProvider wordt standaard toegevoegd wanneer u AddAuthorization gebruikt, en services worden gedetecteerd met behulp van IServiceProviderIsService.
  • Door de gebruiker geconfigureerde middleware en eindpunten worden toegevoegd tussen UseRouting en UseEndpoints.

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 UseRouting en UseEndpoints aanroepen 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.json en appSettings.{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.

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 Stream hetzelfde object als HttpRequest.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 Stream en PipeReader zijn 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:

  1. Voor route-, query- en headerbindingsbronnen bindt u aangepaste typen door een statische TryParse methode voor het type toe te voegen.
  2. Beheer het bindingsproces door een BindAsync methode voor een type te implementeren.
  3. Implementeer voor geavanceerde scenario's de IBindableFromHttpContext<TSelf> interface om specifiek aangepaste bindlogic rechtstreeks vanuit de HttpContext te 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:

  1. Expliciet kenmerk dat is gedefinieerd voor de parameter (From*-kenmerken) in de volgende volgorde:
    1. Routewaarden: [FromRoute]
    2. Queryreeks: [FromQuery]
    3. Koptekst: [FromHeader]
    4. Lichaam: [FromBody]
    5. Dienst: [FromServices]
    6. Parameterwaarden: [AsParameters]
  2. Speciale typen
    1. HttpContext
    2. HttpRequest (HttpContext.Request)
    3. HttpResponse (HttpContext.Response)
    4. ClaimsPrincipal (HttpContext.User)
    5. CancellationToken (HttpContext.RequestAborted)
    6. IFormFileCollection (HttpContext.Request.Form.Files)
    7. IFormFile (HttpContext.Request.Form.Files[paramName])
    8. Stream (HttpContext.Request.Body)
    9. PipeReader (HttpContext.Request.BodyReader)
  3. Het parametertype heeft een geldige statische BindAsync methode.
  4. Parametertype is een tekenreeks of heeft een geldige statische TryParse methode.
    1. Als de parameternaam bestaat in de routesjabloon. In app.Map("/todo/{id}", (int id) => {});, id is gebonden aan de route.
    2. Afhankelijk van de querytekenreeks.
  5. Als het parametertype een service is die wordt geleverd door afhankelijkheidsinjectie, wordt die service als bron gebruikt.
  6. 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:

  1. IResult gebaseerd - Dit omvat Task<IResult> en ValueTask<IResult>
  2. string - Dit omvat Task<string> en ValueTask<string>
  3. T (Elk ander type): dit omvat Task<T> en ValueTask<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:

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.

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.json en appSettings.{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";
    }
}

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:

  1. Voor route-, query- en headerbindingsbronnen bindt u aangepaste typen door een statische TryParse methode voor het type toe te voegen.
  2. Beheer het bindingsproces door een BindAsync methode 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:

  1. Expliciet kenmerk dat is gedefinieerd voor de parameter (From*-kenmerken) in de volgende volgorde:
    1. Routewaarden: [FromRoute]
    2. Queryreeks: [FromQuery]
    3. Koptekst: [FromHeader]
    4. Lichaam: [FromBody]
    5. Dienst: [FromServices]
  2. Speciale typen
    1. HttpContext
    2. HttpRequest (HttpContext.Request)
    3. HttpResponse (HttpContext.Response)
    4. ClaimsPrincipal (HttpContext.User)
    5. CancellationToken (HttpContext.RequestAborted)
  3. Het parametertype heeft een geldige BindAsync methode.
  4. Parametertype is een tekenreeks of heeft een geldige TryParse methode.
    1. Als de parameternaam bestaat in de routesjabloon. In app.Map("/todo/{id}", (int id) => {});, id is gebonden aan de route.
    2. Afhankelijk van de querytekenreeks.
  5. Als het parametertype een service is die wordt geleverd door afhankelijkheidsinjectie, wordt die service als bron gebruikt.
  6. 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
    {
      "id": 1,
      "name": "Joe Smith"
    }
    
    Bij het plaatsen
    {
      "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:

  1. IResult gebaseerd - Dit omvat Task<IResult> en ValueTask<IResult>
  2. string - Dit omvat Task<string> en ValueTask<string>
  3. T (Elk ander type): dit omvat Task<T> en ValueTask<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

Zie ook

OpenAPI-ondersteuning in minimale API's