Snabbreferens för minimala API:er
Not
Det här är inte den senaste versionen av den här artikeln. Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.
Varning
Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i .NET och .NET Core Support Policy. Den aktuella versionen finns i .NET 9-versionen av den här artikeln .
Viktig
Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.
Den aktuella versionen av den här artikeln finns i .NET 9 version.
Det här dokumentet:
- Innehåller en snabbreferens för minimala API:er.
- Är avsedd för erfarna utvecklare. För en introduktion, se Självstudie: Skapa ett minimalt API med ASP.NET Core.
De minimala API:erna består av:
WebApplication
Följande kod genereras av en ASP.NET Core-mall:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Föregående kod kan skapas via dotnet new web
på kommandoraden eller genom att välja mallen Tom webb i Visual Studio.
Följande kod skapar en WebApplication (app
) utan att uttryckligen skapa en WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create
initierar en ny instans av klassen WebApplication med förkonfigurerade standardvärden.
WebApplication
lägger automatiskt till följande mellanprogram i Minimal API applications
beroende på vissa villkor:
-
UseDeveloperExceptionPage
läggs till först närHostingEnvironment
är"Development"
. -
UseRouting
läggs till som nummer två om användarkoden inte redan har anropatUseRouting
och om det finns konfigurerade slutpunkter, till exempelapp.MapGet
. -
UseEndpoints
läggs till i slutet av pipelinen för mellanprogram om några slutpunkter har konfigurerats. -
UseAuthentication
läggs till omedelbart efterUseRouting
om användarkoden inte redan anropadeUseAuthentication
och omIAuthenticationSchemeProvider
kan identifieras i tjänstleverantören.IAuthenticationSchemeProvider
läggs till som standard när du använderAddAuthentication
och tjänster identifieras med hjälp avIServiceProviderIsService
. -
UseAuthorization
läggs till nästa om användarkoden inte redan anroparUseAuthorization
och omIAuthorizationHandlerProvider
kan identifieras i tjänstleverantören.IAuthorizationHandlerProvider
läggs till som standard när du använderAddAuthorization
och tjänster identifieras med hjälp avIServiceProviderIsService
. - Användar konfigurerade mellanprogram och slutpunkter läggs till mellan
UseRouting
ochUseEndpoints
.
Följande kod är i praktiken vad det automatiska mellanprogrammet som läggs till i appen genererar:
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 => {});
I vissa fall är standardkonfigurationen för mellanprogram inte korrekt för appen och kräver ändringar. Till exempel ska UseCors anropas före UseAuthentication och UseAuthorization. Appen måste anropa UseAuthentication
och UseAuthorization
om UseCors
anropas:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Om mellanprogram ska köras innan routningsmatchning sker ska UseRouting anropas och mellanprogrammet placeras innan anropet till UseRouting
.
UseEndpoints krävs inte i det här fallet eftersom det läggs till automatiskt enligt beskrivningen ovan:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
När du lägger till ett terminalmellanprogram:
- Mellanprogrammet måste läggas till efter
UseEndpoints
. - Appen måste anropa
UseRouting
ochUseEndpoints
så att terminalmellanprogrammet kan placeras på rätt plats.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Terminalmellanprogram är mellanprogram som körs om ingen slutpunkt hanterar begäran.
Arbeta med portar
När en webbapp skapas med Visual Studio eller dotnet new
skapas en Properties/launchSettings.json
fil som anger de portar som appen svarar på. I portinställningsexemplen som följer returnerar körning av appen från Visual Studio en feldialogruta Unable to connect to web server 'AppName'
. Visual Studio returnerar ett fel eftersom den förväntar sig att porten som anges i Properties/launchSettings.json
, men appen använder den port som anges av app.Run("http://localhost:3000")
. Kör följande portförändrande exempel från kommandoraden.
Följande avsnitt anger den port som appen svarar på.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
I föregående kod svarar appen på port 3000
.
Flera portar
I följande kod svarar appen på port 3000
och 4000
.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Ange porten från kommandoraden
Följande kommando gör att appen svarar på port 7777
:
dotnet run --urls="https://localhost:7777"
Om slutpunkten Kestrel också har konfigurerats i filen appsettings.json
, används den URL som specificerats i filen appsettings.json
. Mer information finns i Kestrel slutpunktskonfiguration
Läsa porten från miljön
Följande kod läser porten från miljön:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Det bästa sättet att ange porten från miljön är att använda ASPNETCORE_URLS
miljövariabeln, som visas i följande avsnitt.
Ange portarna via miljövariabeln ASPNETCORE_URLS
Miljövariabeln ASPNETCORE_URLS
är tillgänglig för att ange porten:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS
stöder flera URL:er:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Lyssna på alla gränssnitt
Följande exempel visar hur alla gränssnitt avlyssnas
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();
Lyssna på alla gränssnitt med hjälp av ASPNETCORE_URLS
Föregående exempel kan använda ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Lyssna på alla gränssnitt med ASPNETCORE_HTTPS_PORTS
Föregående exempel kan använda ASPNETCORE_HTTPS_PORTS
och ASPNETCORE_HTTP_PORTS
.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Mer information finns i Konfigurera slutpunkter för webbservern ASP.NET Core Kestrel
Ange HTTPS med utvecklingscertifikat
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Mer information om utvecklingscertifikatet finns i Trust the ASP.NET Core HTTPS development certificate on Windows and macOS.
Ange HTTPS med ett anpassat certifikat
Följande avsnitt visar hur du anger det anpassade certifikatet med hjälp av appsettings.json
-filen och via konfiguration.
Ange det anpassade certifikatet med appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Ange det anpassade certifikatet via konfiguration
var builder = WebApplication.CreateBuilder(args);
// Configure the cert and the key
builder.Configuration["Kestrel:Certificates:Default:Path"] = "cert.pem";
builder.Configuration["Kestrel:Certificates:Default:KeyPath"] = "key.pem";
var app = builder.Build();
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Använda certifikat-API:erna
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();
Förstå miljön
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();
Mer information om hur du använder miljön finns i Använda flera miljöer i ASP.NET Core
Konfiguration
Följande kod läser från konfigurationssystemet:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Mer information finns i Configuration i ASP.NET Core
Skogsavverkning
Följande kod skriver ett meddelande till loggen vid programstart:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Mer information finns i Loggning i .NET Core och ASP.NET Core
Få åtkomst till beroendeinjektionscontainern (DI)
Följande kod visar hur du hämtar tjänster från DI-containern under programstarten:
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();
Följande kod visar hur du kommer åt nycklar från DI-containern med hjälp av attributet [FromKeyedServices]
:
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.";
}
Mer information om DI finns i Beroendeinmatning i ASP.NET Core.
WebApplicationBuilder
Det här avsnittet innehåller exempelkod med hjälp av WebApplicationBuilder.
Ändra innehållsrot, programnamn och miljö
Följande kod anger innehållsrot, programnamn och miljö:
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 initierar en ny instans av klassen WebApplicationBuilder med förkonfigurerade standardvärden.
Mer information finns i översikten över grunderna i ASP.NET Core
Ändra innehållsrot, appnamn och miljö med hjälp av miljövariabler eller kommandorad
I följande tabell visas miljövariabeln och kommandoradsargumentet som används för att ändra innehållsrot, appnamn och miljö:
funktion | Miljövariabel | Kommandoradsargument |
---|---|---|
Programnamn | ASPNETCORE_APPLICATIONNAME | --applicationName |
Miljönamn | ASPNETCORE_ENVIRONMENT | --miljö |
Innehållsbas | ASPNETCORE_CONTENTROOT | --contentRoot |
Lägga till konfigurationsprovidrar
Följande exempel lägger till INI-konfigurationsprovidern:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Detaljerad information finns i Filkonfigurationsproviders i Configuration i ASP.NET Core.
Läs konfiguration
Som standard läser WebApplicationBuilder konfigurationen från flera källor, inklusive:
-
appSettings.json
ochappSettings.{environment}.json
- Miljövariabler
- Kommandoraden
En fullständig lista över konfigurationskällor finns i Standardkonfiguration i Configuration i ASP.NET Core.
Följande kod läser HelloKey
från konfigurationen och visar värdet vid /
slutpunkten. Om konfigurationsvärdet är null tilldelas "Hello" till message
:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Läsa av miljön
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine($"Running in development.");
}
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Lägg till loggningsleverantörer
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();
Lägga till tjänster
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();
Anpassa IHostBuilder
Befintliga tilläggsmetoder på IHostBuilder kan nås med egenskapen 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();
Anpassa IWebHostBuilder
Tilläggsmetoder på IWebHostBuilder kan nås med hjälp av egenskapen 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();
Ändra webbplatsens rot
Som standard är webbroten relativ till innehållsroten i mappen wwwroot
. Webbroten är där mellanprogrammet för statiska filer letar efter statiska filer. Webbroten kan ändras med WebHostOptions
, kommandoraden eller med metoden UseWebRoot:
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Container för anpassad beroendeinjektion (DI)
I följande exempel används Autofac:
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();
Lägg till mellanprogram
Alla befintliga ASP.NET Core-mellanprogram kan konfigureras på WebApplication
:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Mer information finns i ASP.NET Core Middleware
Undantagssida för utvecklare
WebApplication.CreateBuilder initierar en ny instans av klassen WebApplicationBuilder med förkonfigurerade standardvärden. Undantagssidan för utvecklare är aktiverad i de förkonfigurerade standardvärdena. När följande kod körs i utvecklingsmiljönrenderar navigering till /
en vänlig sida som visar undantaget.
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
I följande tabell visas några av de mellanprogram som ofta används med minimala API:er.
Mellanprogram | Beskrivning | API |
---|---|---|
autentisering | Tillhandahåller autentiseringsstöd. | UseAuthentication |
Behörighet | Tillhandahåller auktoriseringsstöd. | UseAuthorization |
CORS | Konfigurerar resursdelning mellan ursprung. | UseCors |
undantagshanterare | Globalt hanterar undantag som genereras av pipelinen för mellanprogram. | UseExceptionHandler |
vidarebefordrade rubriker | Skickar vidare proxierade huvudrader till den nuvarande begäran. | UseForwardedHeaders |
HTTPS-omdirigering | Omdirigerar alla HTTP-begäranden till HTTPS. | UseHttpsRedirection |
HTTP Strict Transport Security (HSTS) | Mellanprogram för säkerhetsförbättringar som lägger till ett särskilt svarshuvud. | UseHsts |
Begäran Loggning | Ger stöd för loggning av HTTP-begäranden och svar. | UseHttpLogging |
Tidsgränser för begäranden | Ger stöd för att konfigurera tidsgränser för begäranden, global standard och per slutpunkt. | UseRequestTimeouts |
W3C-begärandeloggning | Ger stöd för loggning av HTTP-begäranden och svar i W3C-format. | UseW3CLogging |
Cachelagring av Svar | Ger stöd för att cachelagra svar. | UseResponseCaching |
komprimering av svar | Ger stöd för att komprimera svar. | UseResponseCompression |
Session | Ger stöd för hantering av användarsessioner. | UseSession |
Statiska Filer | Ger stöd för att hantera statiska filer och katalogbläddring. | UseStaticFiles, UseFileServer |
WebSockets | Aktiverar WebSockets-protokollet. | UseWebSockets |
Följande avsnitt beskriver hantering av begäranden: routning, parameterbindning och svar.
Routning
En konfigurerad WebApplication
stöder Map{Verb}
och MapMethods där {Verb}
är en kamelstilskriven HTTP-metod som Get
, Post
, Put
eller 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 argument som skickas till dessa metoder kallas "routningshanterare".
Routningshanterare
Routhanterare är metoder som körs när rutten matchar. Routningshanterare kan vara ett lambda-uttryck, en lokal funktion, en instansmetod eller en statisk metod. Routningshanterare kan vara synkrona eller asynkrona.
Lambda-uttryck
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();
Lokal funktion
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Instansmetod
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";
}
}
Statisk metod
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";
}
}
Slutpunkt definierad utanför Program.cs
Minimala API:er behöver inte finnas i Program.cs
.
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" });
});
}
}
Se även Ruttgrupper senare i den här artikeln.
Namngivna slutpunkter och länkgenerering
Slutpunkter kan ges namn för att generera URL:er till slutpunkten. Om du använder en namngiven slutpunkt undviker du att behöva hårdkoda sökvägar i en app:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/hello", () => "Hello named route")
.WithName("hi");
app.MapGet("/", (LinkGenerator linker) =>
$"The link to the hello route is {linker.GetPathByName("hi", values: null)}");
app.Run();
Den föregående koden visar The link to the hello route is /hello
från slutpunkten /
.
NOTE: Ändpunktsnamn är skiftlägeskänsliga.
Slutpunktsnamn:
- Måste vara globalt unikt.
- Används som OpenAPI-åtgärds-ID när OpenAPI-stöd är aktiverat. Mer information finns i OpenAPI-.
Routningsparametrar
Routningsparametrar kan samlas in som en del av routningsmönsterdefinitionen:
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();
Föregående kod returnerar The user id is 3 and book id is 7
från URI-/users/3/books/7
.
Routningshanteraren kan deklarera parametrarna som ska avbildas. När en begäran görs till en väg med parametrar som deklarerats för avbildning parsas parametrarna och skickas till hanteraren. Det gör det enkelt att samla in värdena på ett säkert sätt. I föregående kod är både userId
och bookId
int
.
Om något av routningsvärdena inte kan konverteras till en int
i föregående kod genereras ett undantag. GET-begäran /users/hello/books/3
genererar följande undantag:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Jokertecken och fånga alla vägar
Följande catch all route returnerar Routing to hello
från slutpunkten "/posts/hello":
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Vägbegränsningar
Routningsbegränsningar begränsar matchningsbeteendet för en väg.
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();
I följande tabell visas föregående vägmallar och deras beteende:
Routningsmall | Exempel på matchande URI |
---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Mer information finns i referens för routningsbegränsningar i Routning i ASP.NET Core.
Ruttgrupper
Tilläggsmetoden MapGroup hjälper att organisera grupper av slutpunkter med ett gemensamt prefix. Det minskar repetitiv kod och gör det möjligt att anpassa hela grupper av slutpunkter med ett enda anrop till metoder som RequireAuthorization och WithMetadata som lägger till slutpunktsmetadata.
Följande kod skapar till exempel två liknande grupper av slutpunkter:
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;
}
I det här scenariot kan du använda en relativ adress för Location
-rubriken i 201 Created
-resultatet.
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Den första gruppen med slutpunkter matchar endast begäranden som är prefix med /public/todos
och är tillgängliga utan autentisering. Den andra gruppen med slutpunkter matchar endast begäranden som är prefix med /private/todos
och kräver autentisering.
QueryPrivateTodos
slutpunktfilterfabriken är en lokal funktion som ändrar rout-hanterarens TodoDb
parametrar för att tillåta åtkomst till och lagring av privata data.
Routningsgrupper stöder också kapslade grupper och komplexa prefixmönster med vägparametrar och begränsningar. I följande exempel kan routningshanteraren som mappas till den user
gruppen avbilda de {org}
- och {group}
routningsparametrar som definierats i de yttre gruppprefixen.
Prefixet kan också vara tomt. Detta kan vara användbart för att lägga till slutpunktsmetadata eller filter till en grupp slutpunkter utan att ändra vägmönstret.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Att lägga till filter eller metadata i en grupp fungerar på samma sätt som att lägga till dem individuellt i varje slutpunkt innan du lägger till extra filter eller metadata som kan ha lagts till i en inre grupp eller en specifik slutpunkt.
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);
});
I exemplet ovan loggar det yttre filtret den inkommande begäran före det inre filtret trots att det lades till tvåa. Eftersom filtren tillämpades på olika grupper spelar det ingen roll vilken ordning de lades till i förhållande till varandra. Orderfiltren som läggs till spelar roll om de tillämpas på samma grupp eller specifika slutpunkt.
En begäran om att /outer/inner/
loggar följande:
/outer group filter
/inner group filter
MapGet filter
Parameterbindning
Parameterbindning är processen att konvertera begärandedata till starkt skrivna parametrar som uttrycks av routningshanterare. En bindningskälla avgör var parametrarna är bundna från. Bindningskällor kan vara explicita eller härledda baserat på HTTP-metod och parametertyp.
Bindningskällor som stöds:
- Vägvärden
- Frågesträng
- Rubrik
- Kropp (som JSON)
- Formulärvärden
- Tjänster som tillhandahålls av dependency injection
- Anpassad
Följande GET
routningshanterare använder några av dessa parameterbindningskällor:
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 { }
I följande tabell visas relationen mellan de parametrar som används i föregående exempel och de associerade bindningskällorna.
Parameter | Bindningskälla |
---|---|
id |
ruttvärde |
page |
Frågesträngen |
customHeader |
rubrik |
service |
Tillhandahålls av beroendeinjektion |
HTTP-metoderna GET
, HEAD
, OPTIONS
och DELETE
binder inte implicit från brödtexten. Om du vill binda från brödtexten (som JSON) för dessa HTTP-metoder du uttryckligen binda med [FromBody]
eller läsa från HttpRequest.
I följande exempel använder POST-routningshanteraren en bindningskälla för brödtext (som JSON) för parametern person
:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/", (Person person) => { });
record Person(string Name, int Age);
Parametrarna i föregående exempel är alla bundna från begärandedata automatiskt. För att demonstrera bekvämligheten med parameterbindningen visar följande routningshanterare hur du läser begärandedata direkt från begäran:
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>();
// ...
});
Explicit parameterbindning
Attribut kan användas för att explicit deklarera var parametrarna är bundna från.
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 | Bindningskälla |
---|---|
id |
routningsvärde med namnet id |
page |
frågesträng med namnet "p" |
service |
Tillhandahålls av beroendeinjektion |
contentType |
rubrik med namnet "Content-Type" |
Explicit bindning från formulärvärden
Det [FromForm]
attributet binder formulärvärden:
app.MapPost("/todos", async ([FromForm] string name,
[FromForm] Visibility visibility, IFormFile? attachment, TodoDb db) =>
{
var todo = new Todo
{
Name = name,
Visibility = visibility
};
if (attachment is not null)
{
var attachmentName = Path.GetRandomFileName();
using var stream = File.Create(Path.Combine("wwwroot", attachmentName));
await attachment.CopyToAsync(stream);
}
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Ok();
});
// Remaining code removed for brevity.
Ett alternativ är att använda attributet [AsParameters]
med en anpassad typ som har egenskaper som kommenterats med [FromForm]
. Följande kod binder, till exempel, från formulärvärden till egenskaper för NewTodoRequest
record struct:
app.MapPost("/ap/todos", async ([AsParameters] NewTodoRequest request, TodoDb db) =>
{
var todo = new Todo
{
Name = request.Name,
Visibility = request.Visibility
};
if (request.Attachment is not null)
{
var attachmentName = Path.GetRandomFileName();
using var stream = File.Create(Path.Combine("wwwroot", attachmentName));
await request.Attachment.CopyToAsync(stream);
todo.Attachment = attachmentName;
}
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Ok();
});
// Remaining code removed for brevity.
public record struct NewTodoRequest([FromForm] string Name,
[FromForm] Visibility Visibility, IFormFile? Attachment);
Mer information finns i avsnittet om AsParameters senare i den här artikeln.
Den fullständiga exempelkoden finns på lagringsplatsen AspNetCore.Docs.Samples.
Säker bindning från IFormFile och IFormFileCollection
Komplex formulärbindning stöds med hjälp av IFormFile och IFormFileCollection med hjälp av [FromForm]
:
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
// Generate a form with an anti-forgery token and an /upload endpoint.
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
var html = MyUtils.GenerateHtmlForm(token.FormFieldName, token.RequestToken!);
return Results.Content(html, "text/html");
});
app.MapPost("/upload", async Task<Results<Ok<string>, BadRequest<string>>>
([FromForm] FileUploadForm fileUploadForm, HttpContext context,
IAntiforgery antiforgery) =>
{
await MyUtils.SaveFileWithName(fileUploadForm.FileDocument!,
fileUploadForm.Name!, app.Environment.ContentRootPath);
return TypedResults.Ok($"Your file with the description:" +
$" {fileUploadForm.Description} has been uploaded successfully");
});
app.Run();
Parametrar som är bundna till begäran med [FromForm]
innehåller ett antiförfalskningstoken. Antiforgery-token verifieras när begäran bearbetas. Mer information finns i Antiforgery med minimala API:er.
Mer information finns i Formulärbindning i minimala API:er.
Den fullständiga exempelkoden finns på lagringsplatsen AspNetCore.Docs.Samples.
Parameterbindning med beroendeinmatning
Parameterbindning för minimala API:er binder parametrar via beroendeinmatning när typen konfigureras som en tjänst. Det är inte nödvändigt att uttryckligen tillämpa attributet [FromServices]
på en parameter. I följande kod returnerar båda åtgärderna tiden:
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();
Valfria parametrar
Parametrar som deklareras i routningshanterare behandlas efter behov:
- Om en begäran matchar vägen körs routningshanteraren endast om alla obligatoriska parametrar anges i begäran.
- Om du inte anger alla obligatoriska parametrar resulterar det i ett fel.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int pageNumber) => $"Requesting page {pageNumber}");
app.Run();
URI | resultat |
---|---|
/products?pageNumber=3 |
3 returnerade |
/products |
BadHttpRequestException : Den obligatoriska parametern "int pageNumber" angavs inte från frågesträngen. |
/products/1 |
HTTP 404-fel, ingen matchande väg |
Om du vill göra pageNumber
valfri definierar du typen som valfri eller anger ett standardvärde:
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 | resultat |
---|---|
/products?pageNumber=3 |
3 returnerade |
/products |
1 returnerade |
/products2 |
1 returnerade |
Föregående null- och standardvärde gäller för alla källor:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/products", (Product? product) => { });
app.Run();
Föregående kod anropar metoden med en null-produkt om ingen begärandetext skickas.
NOTE: Om ogiltiga data anges och parametern är null kan routningshanteraren inte köras.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
app.Run();
URI | resultat |
---|---|
/products?pageNumber=3 |
3 har returnerats |
/products |
1 returnerade |
/products?pageNumber=two |
BadHttpRequestException : Det gick inte att binda parametern "Nullable<int> pageNumber" från "två". |
/products/two |
HTTP 404-fel, ingen matchande väg |
För mer information, se avsnittet bindningsfel.
Särskilda typer
Följande typer är bundna utan explicita attribut:
HttpContext: Kontexten som innehåller all information om den aktuella HTTP-begäran eller -svaret:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));
HttpRequest och HttpResponse: HTTP-begäran och HTTP-svar:
app.MapGet("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));
CancellationToken: Den annulleringstoken som är associerad med den aktuella HTTP-begäran:
app.MapGet("/", async (CancellationToken cancellationToken) => await MakeLongRunningRequestAsync(cancellationToken));
ClaimsPrincipal: Användaren som är associerad med begäran, bunden från HttpContext.User:
app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
Binda begärandetexten som en Stream
eller PipeReader
Begärandetexten kan bindas som en Stream
eller PipeReader
för att effektivt stödja scenarier där användaren måste bearbeta data och:
- Lagra data i bloblagring eller köa data till en köleverantör.
- Bearbeta lagrade data med en arbetsprocess eller molnfunktion.
Data kan till exempel läggas i kö för lagring i Azure Queue Storage eller lagras i Azure Blob Storage.
Följande kod implementerar en bakgrundskö:
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;
}
Följande kod binder begärandetexten till en 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);
});
Följande kod visar den fullständiga Program.cs
filen:
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();
- När du läser data är
Stream
samma objekt somHttpRequest.Body
. - Begärandetexten buffrades inte som standard. När brödtexten har lästs kan den inte spolas tillbaka. Dataströmmen kan inte läsas flera gånger.
-
Stream
ochPipeReader
kan inte användas utanför den minimala åtgärdshanteraren eftersom de underliggande buffertarna tas bort eller återanvänds.
Filuppladdningar med IFormFile och IFormFileCollection
Följande kod använder IFormFile och IFormFileCollection för att ladda upp filen:
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();
Autentiserade filuppladdningsbegäranden stöds med hjälp av ett auktoriseringshuvud, ett klientcertifikateller ett cookie-huvud.
Binda till formulär med IFormCollection, IFormFile och IFormFileCollection
Bindning från formulärbaserade parametrar med hjälp av IFormCollection, IFormFileoch IFormFileCollection stöds. OpenAPI- metadata härleds för formulärparametrar för att stödja integration med Swagger UI.
Följande kod laddar upp filer med hjälp av härledd bindning från typen IFormFile
.
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http.HttpResults;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
string GetOrCreateFilePath(string fileName, string filesDirectory = "uploadFiles")
{
var directoryPath = Path.Combine(app.Environment.ContentRootPath, filesDirectory);
Directory.CreateDirectory(directoryPath);
return Path.Combine(directoryPath, fileName);
}
async Task UploadFileWithName(IFormFile file, string fileSaveName)
{
var filePath = GetOrCreateFilePath(fileSaveName);
await using var fileStream = new FileStream(filePath, FileMode.Create);
await file.CopyToAsync(fileStream);
}
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
var html = $"""
<html>
<body>
<form action="/upload" method="POST" enctype="multipart/form-data">
<input name="{token.FormFieldName}" type="hidden" value="{token.RequestToken}"/>
<input type="file" name="file" placeholder="Upload an image..." accept=".jpg,
.jpeg, .png" />
<input type="submit" />
</form>
</body>
</html>
""";
return Results.Content(html, "text/html");
});
app.MapPost("/upload", async Task<Results<Ok<string>,
BadRequest<string>>> (IFormFile file, HttpContext context, IAntiforgery antiforgery) =>
{
var fileSaveName = Guid.NewGuid().ToString("N") + Path.GetExtension(file.FileName);
await UploadFileWithName(file, fileSaveName);
return TypedResults.Ok("File uploaded successfully!");
});
app.Run();
Varning: När formulär implementeras måste appen förhindraXSRF-attacker (Cross-Site Request Forgery). I föregående kod används IAntiforgery-tjänsten för att förhindra XSRF-attacker genom att generera och verifiera en antiforgery-token:
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http.HttpResults;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
string GetOrCreateFilePath(string fileName, string filesDirectory = "uploadFiles")
{
var directoryPath = Path.Combine(app.Environment.ContentRootPath, filesDirectory);
Directory.CreateDirectory(directoryPath);
return Path.Combine(directoryPath, fileName);
}
async Task UploadFileWithName(IFormFile file, string fileSaveName)
{
var filePath = GetOrCreateFilePath(fileSaveName);
await using var fileStream = new FileStream(filePath, FileMode.Create);
await file.CopyToAsync(fileStream);
}
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
var html = $"""
<html>
<body>
<form action="/upload" method="POST" enctype="multipart/form-data">
<input name="{token.FormFieldName}" type="hidden" value="{token.RequestToken}"/>
<input type="file" name="file" placeholder="Upload an image..." accept=".jpg,
.jpeg, .png" />
<input type="submit" />
</form>
</body>
</html>
""";
return Results.Content(html, "text/html");
});
app.MapPost("/upload", async Task<Results<Ok<string>,
BadRequest<string>>> (IFormFile file, HttpContext context, IAntiforgery antiforgery) =>
{
var fileSaveName = Guid.NewGuid().ToString("N") + Path.GetExtension(file.FileName);
await UploadFileWithName(file, fileSaveName);
return TypedResults.Ok("File uploaded successfully!");
});
app.Run();
Mer information om XSRF-attacker finns i Antiforgery med minimala API:er
Mer information finns i Formulärbindning i minimala API:er;
Binda till samlingar och komplexa typer från formulär
Stöd för bindning gäller:
- Samlingar, till exempel List och Dictionary
- Komplexa typer, till exempel
Todo
ellerProject
Följande kod visar:
- En minimal slutpunkt som binder en formulärinmatning i flera delar till ett komplext objekt.
- Hur man använder antiforgery-tjänsterna för att stödja generering och validering av antiforgery-token.
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
var html = $"""
<html><body>
<form action="/todo" method="POST" enctype="multipart/form-data">
<input name="{token.FormFieldName}"
type="hidden" value="{token.RequestToken}" />
<input type="text" name="name" />
<input type="date" name="dueDate" />
<input type="checkbox" name="isCompleted" value="true" />
<input type="submit" />
<input name="isCompleted" type="hidden" value="false" />
</form>
</body></html>
""";
return Results.Content(html, "text/html");
});
app.MapPost("/todo", async Task<Results<Ok<Todo>, BadRequest<string>>>
([FromForm] Todo todo, HttpContext context, IAntiforgery antiforgery) =>
{
try
{
await antiforgery.ValidateRequestAsync(context);
return TypedResults.Ok(todo);
}
catch (AntiforgeryValidationException e)
{
return TypedResults.BadRequest("Invalid antiforgery token");
}
});
app.Run();
class Todo
{
public string Name { get; set; } = string.Empty;
public bool IsCompleted { get; set; } = false;
public DateTime DueDate { get; set; } = DateTime.Now.Add(TimeSpan.FromDays(1));
}
I föregående kod:
- Målparametern måste kommenteras med attributet
[FromForm]
för att skilja sig från parametrar som ska läsas från JSON-brödtexten. - Bindning från komplexa typer eller samlingstyper stöds inte för minimala API:er som kompileras med generatorn för begärandedelegat.
- Markeringen visar ytterligare dolda indata med namnet
isCompleted
och värdetfalse
. Om kryssrutanisCompleted
markeras när formuläret skickas skickas både värdentrue
ochfalse
som värden. Om kryssrutan är avmarkerad skickas endast det dolda indatavärdetfalse
. Den ASP.NET Core-modellbindningsprocessen läser bara det första värdet vid bindning till ettbool
värde, vilket resulterar itrue
för markerade kryssrutor ochfalse
för avmarkerade kryssrutor.
Ett exempel på formulärdata som skickas till föregående slutpunkt ser ut så här:
__RequestVerificationToken: CfDJ8Bveip67DklJm5vI2PF2VOUZ594RC8kcGWpTnVV17zCLZi1yrs-CSz426ZRRrQnEJ0gybB0AD7hTU-0EGJXDU-OaJaktgAtWLIaaEWMOWCkoxYYm-9U9eLV7INSUrQ6yBHqdMEE_aJpD4AI72gYiCqc
name: Walk the dog
dueDate: 2024-04-06
isCompleted: true
isCompleted: false
Binda matriser och strängvärden från rubriker och frågesträngar
Följande kod visar bindning av frågesträngar till en matris med primitiva typer, strängmatriser och 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]}");
Bindning av frågesträngar eller sidhuvudvärden till en matris med komplexa typer stöds när typen har TryParse
implementerats. Följande kod binder till en strängmatris och returnerar alla objekt med de angivna taggarna:
// 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();
});
Följande kod visar modellen och den nödvändiga TryParse
implementeringen:
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;
}
}
Följande kod binder till en int
matris:
// 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();
});
Om du vill testa koden ovan lägger du till följande slutpunkt för att fylla databasen med Todo
objekt:
// POST /todoitems/batch
app.MapPost("/todoitems/batch", async (Todo[] todos, TodoDb db) =>
{
await db.Todos.AddRangeAsync(todos);
await db.SaveChangesAsync();
return Results.Ok(todos);
});
Använd ett verktyg som HttpRepl
för att skicka följande data till föregående slutpunkt:
[
{
"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"
}
}
]
Följande kod binder till huvudnyckeln X-Todo-Id
och returnerar Todo
objekt med matchande Id
värden:
// 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();
});
Anteckning
När du binder en string[]
från en frågesträng resulterar frånvaron av ett matchande frågesträngsvärde i en tom matris i stället för ett null-värde.
Parameterbindning för argumentlistor med [AsParameters]
AsParametersAttribute möjliggör enkel parameterbindning till typer och inte komplex eller rekursiv modellbindning.
Överväg följande kod:
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.
Överväg följande GET
slutpunkt:
app.MapGet("/todoitems/{id}",
async (int Id, TodoDb Db) =>
await Db.Todos.FindAsync(Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
Följande struct
kan användas för att ersätta de föregående markerade parametrarna:
struct TodoItemRequest
{
public int Id { get; set; }
public TodoDb Db { get; set; }
}
Den omstrukturerade GET
-slutpunkten använder föregående struct
med attributet 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());
Följande kod visar ytterligare slutpunkter i appen:
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();
});
Följande klasser används för att omstrukturera parameterlistorna:
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!;
}
Följande kod visar de refaktorerade slutpunkterna med hjälp av AsParameters
samt de tidigare struct
och klasser.
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();
});
Följande record
typer kan användas för att ersätta de föregående parametrarna:
record TodoItemRequest(int Id, TodoDb Db);
record CreateTodoItemRequest(TodoItemDTO Dto, TodoDb Db);
record EditTodoItemRequest(int Id, TodoItemDTO Dto, TodoDb Db);
Att använda en struct
med AsParameters
kan vara mer högpresterande än att använda en record
typ.
Den kompletta exempelkoden i repositoryn AspNetCore.Docs.Samples.
Anpassad bindning
Det finns två sätt att anpassa parameterbindning:
- För routnings-, fråge- och rubrikbindningskällor binder du anpassade typer genom att lägga till en statisk
TryParse
metod för typen. - Kontrollera bindningsprocessen genom att implementera en
BindAsync
-metod för en typ.
TryParse
TryParse
har två API:er:
public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);
Följande kod visar Point: 12.3, 10.1
med 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
har följande API:er:
public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);
Följande kod visar SortBy:xyz, SortDirection:Desc, CurrentPage:99
med 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
}
Bindningsfel
När bindningen misslyckas loggar ramverket ett felsökningsmeddelande och returnerar olika statuskoder till klienten beroende på felläget.
Felläge | Parametertyp som kan vara null | Bindningskälla | Statuskod |
---|---|---|---|
{ParameterType}.TryParse returnerar false |
Ja | rutt/förfrågan/huvud | 400 |
{ParameterType}.BindAsync returnerar null |
Ja | anpassad | 400 |
{ParameterType}.BindAsync kastar |
spelar ingen roll | anpassad | 500 |
Det gick inte att deserialisera JSON-data | spelar ingen roll | kropp | 400 |
Fel innehållstyp (inte application/json ) |
spelar ingen roll | kropp | 415 |
Bindningsprioritet
Reglerna för att fastställa en bindningskälla från en parameter:
- Den explicita attribut som definieras på parametern (From*-attribut) i följande ordning:
- Routvärden:
[FromRoute]
- Frågesträng:
[FromQuery]
- Rubrik:
[FromHeader]
- Brödtext:
[FromBody]
- Formulär:
[FromForm]
- Tjänst:
[FromServices]
- Parametervärden:
[AsParameters]
- Routvärden:
- Särskilda typer
HttpContext
-
HttpRequest
(HttpContext.Request
) -
HttpResponse
(HttpContext.Response
) -
ClaimsPrincipal
(HttpContext.User
) -
CancellationToken
(HttpContext.RequestAborted
) -
IFormCollection
(HttpContext.Request.Form
) -
IFormFileCollection
(HttpContext.Request.Form.Files
) -
IFormFile
(HttpContext.Request.Form.Files[paramName]
) -
Stream
(HttpContext.Request.Body
) -
PipeReader
(HttpContext.Request.BodyReader
)
- Parametertypen har en giltig statisk
BindAsync
-metod. - Parametertypen är en sträng eller har en giltig statisk
TryParse
-metod.- Om parameternamnet finns i routemallen, till exempel
app.Map("/todo/{id}", (int id) => {});
, då binds det från routen. - Bunden till frågesträngen.
- Om parameternamnet finns i routemallen, till exempel
- Om parametertypen är en tjänst som tillhandahålls av beroendeinmatning använder den tjänsten som källa.
- Parametern kommer från kroppen.
Konfigurera JSON-deserialiseringsalternativ för brödtextbindning
Kroppsbindningskällan använder System.Text.Json för deserialisering. Det är inte möjligt att ändra den här standardinställningen, men JSON-serialiserings- och deserialiseringsalternativ kan konfigureras.
Konfigurera JSON-deserialiseringsalternativ globalt
Alternativ som gäller globalt för en app kan konfigureras genom att anropa ConfigureHttpJsonOptions. Följande exempel innehåller offentliga fält och formaterar JSON-utdata.
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
// }
Eftersom exempelkoden konfigurerar både serialisering och deserialisering kan den läsa NameField
och inkludera NameField
i utdata-JSON.
Konfigurera JSON-deserialiseringsalternativ för en slutpunkt
ReadFromJsonAsync har överbelastningar som accepterar ett JsonSerializerOptions-objekt. Följande exempel innehåller offentliga fält och formaterar JSON-utdata.
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
// }
Eftersom föregående kod endast tillämpar de anpassade alternativen för deserialisering utesluter utdata-JSON NameField
.
Läs begärandetexten
Läs begärandetexten direkt med hjälp av en HttpContext- eller 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();
Föregående kod:
- Åtkomst till begärandetexten genom HttpRequest.BodyReader.
- Kopierar begärandetexten till en lokal fil.
Svaren
Routningshanterare stöder följande typer av returvärden:
-
IResult
-baserad – Detta omfattarTask<IResult>
ochValueTask<IResult>
-
string
– Detta inkluderarTask<string>
ochValueTask<string>
-
T
(alla andra typer) – Detta inkluderarTask<T>
ochValueTask<T>
Returvärde | Uppförande | Innehållstyp |
---|---|---|
IResult |
Ramverket anropar IResult.ExecuteAsync | Bestäms av implementeringen av IResult |
string |
Ramverket skriver strängen direkt till svaret | text/plain |
T (alla andra typer) |
Ramverket JSON-serialiserar svaret | application/json |
En mer djupgående guide till routningshanterarens returvärden finns i Skapa svar i minimala API-program
Exempel på returvärden
strängreturvärden
app.MapGet("/hello", () => "Hello World");
JSON-returvärden
app.MapGet("/hello", () => new { Message = "Hello World" });
Returnera TypedResults
Följande kod returnerar en TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Att returnera TypedResults
är att föredra framför att returnera Results. Mer information finns i TypedResults vs Results.
IResult-returvärden
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
I följande exempel används de inbyggda resultattyperna för att anpassa svaret:
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" }));
Anpassad statuskod
app.MapGet("/405", () => Results.StatusCode(405));
Text
app.MapGet("/text", () => Results.Text("This is some text"));
Ström
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");
});
Se Skapa svar i Minimala API-program för fler exempel.
Omdirigera
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
Fil
app.MapGet("/download", () => Results.File("myfile.text"));
Inbyggda resultat
Vanliga resultathjälpare finns i Results- och TypedResults statiska klasser. Det är att föredra att returnera TypedResults
framför att returnera Results
. Mer information finns i TypedResults vs Results.
Ändra rubriker
Använd HttpResponse
-objektet för att ändra svarshuvuden:
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";
});
Anpassa resultat
Program kan styra svar genom att implementera en anpassad IResult typ. Följande kod är ett exempel på en HTML-resultattyp:
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);
}
}
Vi rekommenderar att du lägger till en tilläggsmetod till Microsoft.AspNetCore.Http.IResultExtensions för att göra dessa anpassade resultat lättare att hitta.
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();
Inskrivna resultat
Det IResult gränssnittet kan representera värden som returneras från minimala API:er som inte använder implicit stöd för JSON-serialisering av det returnerade objektet till HTTP-svaret. Klassen static Results används för att skapa varierande IResult
objekt som representerar olika typer av svar. Du kan till exempel ange svarsstatuskoden eller omdirigera till en annan URL.
De typer som implementerar IResult
är offentliga, vilket möjliggör typkontroller vid testning. Till exempel:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Du kan titta på returtyperna för motsvarande metoder på den statiska TypedResults-klassen för att hitta rätt offentlig IResult
typ att casta till.
Se Skapa svar i Minimala API-program för fler exempel.
Filter
Mer information finns i Filter i Minimal API-appar.
Tillstånd
Vägar kan skyddas med hjälp av auktoriseringsprinciper. Dessa kan deklareras via attributet [Authorize]
eller med hjälp av metoden RequireAuthorization:
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();
Föregående kod kan skrivas med RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
Följande exempel använder principbaserad auktorisering:
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();
Tillåt oautentiserade användare att komma åt en slutpunkt
Med [AllowAnonymous]
kan oautentiserade användare komma åt slutpunkter:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Rutterna kan CORS- aktiveras med CORS-policyer. CORS kan deklareras via attributet [EnableCors]
eller med hjälp av metoden RequireCors. Följande exempel aktiverar 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("/",() => "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();
Mer information finns i Aktivera CORS (Cross-Origin Requests) i ASP.NET Core
ValidateScopes och ValidateOnBuild
ValidateScopes och ValidateOnBuild aktiveras som standard i miljön Development men inaktiveras i andra miljöer.
När ValidateOnBuild
är true
validerar DI-containern tjänstkonfigurationen vid byggtillfället. Om tjänstkonfigurationen är ogiltig misslyckas bygget vid appstart i stället för vid körning när tjänsten begärs.
När ValidateScopes
är true
verifierar DI-containern att en begränsad tjänst inte matchas från rotomfånget. Om du löser en begränsad tjänst från rotomfånget kan det leda till en minnesläcka eftersom tjänsten behålls i minnet längre än omfånget för begäran.
ValidateScopes
och ValidateOnBuild
är som standard falska i icke-utvecklingslägen av prestandaskäl.
Följande kod visar ValidateScopes
är aktiverat som standard i utvecklingsläge men inaktiverat i versionsläge:
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 { }
Följande kod visar ValidateOnBuild
är aktiverat som standard i utvecklingsläge men inaktiverat i versionsläge:
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 { }
Följande kod inaktiverar ValidateScopes
och ValidateOnBuild
i 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;
});
}
Se även
- Snabbguide Minimal API:er
- Generera OpenAPI-dokument
- Skapa svar i minimala API-program
- filter i minimala API-appar
- Hantera fel i minimala API:er
- autentisering och auktorisering i minimala API:er
- Testa minimala API-appar
- Kortslutningsruttning
- Identity API-slutpunkter
- container för nyckelbaserad tjänstberoendeinmatning stöder
- En titt bakom kulisserna på minimala API-slutpunkter
- Organisera ASP.NET core minimala API:er
- Fluent-valideringsdiskussion på GitHub
Det här dokumentet:
- Innehåller en snabbreferens för minimala API:er.
- Är avsedd för erfarna utvecklare. En introduktion finns i Självstudie: Skapa ett minimalt API med ASP.NET Core
De minimala API:erna består av:
WebApplication
Följande kod genereras av en ASP.NET Core-mall:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Föregående kod kan skapas via dotnet new web
på kommandoraden eller genom att välja mallen Tom webb i Visual Studio.
Följande kod skapar en WebApplication (app
) utan att uttryckligen skapa en WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create
initierar en ny instans av klassen WebApplication med förkonfigurerade standardvärden.
WebApplication
lägger automatiskt till följande mellanprogram i Minimal API applications
beroende på vissa villkor:
-
UseDeveloperExceptionPage
läggs till först närHostingEnvironment
är"Development"
. -
UseRouting
läggs till som andra om användarkoden inte redan har anropatUseRouting
och om det finns konfigurerade slutpunkter, till exempelapp.MapGet
. -
UseEndpoints
läggs till i slutet av pipelinen för mellanprogram om några slutpunkter har konfigurerats. -
UseAuthentication
läggs till omedelbart efterUseRouting
om användarkoden inte redan anropadeUseAuthentication
och omIAuthenticationSchemeProvider
kan identifieras i tjänstleverantören.IAuthenticationSchemeProvider
läggs till som standard när du använderAddAuthentication
och tjänster identifieras med hjälp avIServiceProviderIsService
. -
UseAuthorization
läggs till nästa om användarkoden inte redan anroparUseAuthorization
och omIAuthorizationHandlerProvider
kan identifieras i tjänstleverantören.IAuthorizationHandlerProvider
läggs till som standard när du använderAddAuthorization
och tjänster identifieras med hjälp avIServiceProviderIsService
. - Användar konfigurerade mellanprogram och slutpunkter läggs till mellan
UseRouting
ochUseEndpoints
.
Följande kod är i praktiken vad det automatiska mellanprogrammet som läggs till i appen genererar:
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 => {});
I vissa fall är standardkonfigurationen för mellanprogram inte korrekt för appen och kräver ändringar. Till exempel ska UseCors anropas före UseAuthentication och UseAuthorization. Appen måste anropa UseAuthentication
och UseAuthorization
om UseCors
anropas:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Om mellanprogram ska köras innan routningsmatchning sker ska UseRouting anropas och mellanprogrammet placeras innan anropet till UseRouting
.
UseEndpoints krävs inte i det här fallet eftersom det läggs till automatiskt enligt beskrivningen ovan:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
När du lägger till ett terminalmellanprogram:
- Mellanprogrammet måste läggas till efter
UseEndpoints
. - Appen måste anropa
UseRouting
ochUseEndpoints
så att terminalmellanprogrammet kan placeras på rätt plats.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Terminalmellanprogram är mellanprogram som körs om ingen slutpunkt hanterar begäran.
Arbeta med portar
När en webbapp skapas med Visual Studio eller dotnet new
skapas en Properties/launchSettings.json
fil som anger de portar som appen svarar på. I portinställningsexemplen som följer returnerar körning av appen från Visual Studio en feldialogruta Unable to connect to web server 'AppName'
. Visual Studio returnerar ett fel eftersom den förväntar sig att porten som anges i Properties/launchSettings.json
, men appen använder den port som anges av app.Run("http://localhost:3000")
. Kör följande portförändrande exempel från kommandoraden.
Följande avsnitt anger den port som appen svarar på.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
I föregående kod svarar appen på port 3000
.
Flera portar
I följande kod svarar appen på port 3000
och 4000
.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Ange porten från kommandoraden
Följande kommando gör att appen svarar på port 7777
:
dotnet run --urls="https://localhost:7777"
Om slutpunkten Kestrel också har konfigurerats i appsettings.json
-filen, används URL:en som anges i appsettings.json
-filen. Mer information finns i Kestrel slutpunktskonfiguration
Läs porten från miljövariabeln
Följande kod läser porten från miljön:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Det bästa sättet att ange porten från miljön är att använda ASPNETCORE_URLS
miljövariabeln, som visas i följande avsnitt.
Ange portarna via miljövariabeln ASPNETCORE_URLS
Miljövariabeln ASPNETCORE_URLS
är tillgänglig för att ange porten:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS
stöder flera URL:er:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Mer information om hur du använder miljön finns i Använda flera miljöer i ASP.NET Core
Lyssna på alla gränssnitt
Följande exempel visar hur man lyssnar på alla gränssnitt
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();
Lyssna på alla gränssnitt med hjälp av ASPNETCORE_URLS
Föregående exempel kan använda ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Ange HTTPS med utvecklingscertifikat
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Mer information om utvecklingscertifikatet finns i Trust the ASP.NET Core HTTPS development certificate on Windows and macOS.
Ange HTTPS med ett anpassat certifikat
Följande avsnitt visar hur du anger det anpassade certifikatet med hjälp av appsettings.json
-filen och via konfiguration.
Ange det anpassade certifikatet med appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Ange det anpassade certifikatet via konfiguration
var builder = WebApplication.CreateBuilder(args);
// Configure the cert and the key
builder.Configuration["Kestrel:Certificates:Default:Path"] = "cert.pem";
builder.Configuration["Kestrel:Certificates:Default:KeyPath"] = "key.pem";
var app = builder.Build();
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Använda certifikat-API:erna
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();
Konfiguration
Följande kod läser från konfigurationssystemet:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Mer information finns i Configuration i ASP.NET Core
Skogsavverkning
Följande kod skriver ett meddelande till loggen vid programstart:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Mer information finns i Loggning i .NET Core och ASP.NET Core
Få åtkomst till Dependency Injection (DI)-containern
Följande kod visar hur du hämtar tjänster från DI-containern under programstarten:
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();
Mer information finns i Beroendeinmatning i ASP.NET Core.
WebApplicationBuilder
Det här avsnittet innehåller exempelkod med hjälp av WebApplicationBuilder.
Ändra innehållsrot, programnamn och miljö
Följande kod anger innehållsrot, programnamn och miljö:
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 initierar en ny instans av klassen WebApplicationBuilder med förkonfigurerade standardvärden.
Mer information finns i översikten över grunderna i ASP.NET Core
Ändra innehållsrot, appnamn och miljö efter miljövariabler eller kommandorad
I följande tabell visas miljövariabeln och kommandoradsargumentet som används för att ändra innehållsrot, appnamn och miljö:
egenskap | Miljövariabel | Kommandoradsargument |
---|---|---|
Programnamn | ASPNETCORE_APPLICATIONNAME | --applicationName |
Miljönamn | ASPNETCORE_ENVIRONMENT | --miljö |
Innehållsrot | ASPNETCORE_CONTENTROOT | --contentRoot |
Lägga till konfigurationsprovidrar
Följande exempel lägger till INI-konfigurationsprovidern:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Detaljerad information finns i Filkonfigurationsleverantörer i Konfiguration i ASP.NET Core.
Läs konfiguration
Som standard läser WebApplicationBuilder konfigurationen från flera källor, inklusive:
-
appSettings.json
ochappSettings.{environment}.json
- Miljövariabler
- Kommandoraden
Följande kod läser HelloKey
från konfigurationen och visar värdet vid /
slutpunkten. Om konfigurationsvärdet är null tilldelas "Hello" till message
:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
En fullständig lista över konfigurationskällor finns i Standardkonfiguration i Configuration i ASP.NET Core
Lägga till loggningsleverantörer
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();
Lägga till tjänster
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();
Anpassa IHostBuilder
Befintliga tilläggsmetoder på IHostBuilder kan nås med egenskapen 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();
Anpassa IWebHostBuilder
Tilläggsmetoder på IWebHostBuilder kan nås med hjälp av egenskapen 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();
Ändra webbroten
Som standard är webbroten relativ till innehållsroten i mappen wwwroot
. Webbroten är den plats där middleware för statiska filer letar efter dessa filer. Webbroten kan ändras med WebHostOptions
, kommandoraden eller med metoden UseWebRoot:
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Container för anpassad beroendeinmatning (DI)
I följande exempel används Autofac:
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();
Lägg till mellanprogram
Alla befintliga ASP.NET Core-mellanprogram kan konfigureras på WebApplication
:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Mer information finns i ASP.NET Core Middleware
Undantagssida för utvecklare
WebApplication.CreateBuilder initierar en ny instans av klassen WebApplicationBuilder med förkonfigurerade standardvärden. Undantagssidan för utvecklare är aktiverad i de förkonfigurerade standardvärdena. När följande kod körs i utvecklingsmiljönrenderar navigering till /
en vänlig sida som visar undantaget.
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
I följande tabell visas några av de mellanprogram som ofta används med minimala API:er.
Middleware | Beskrivning | API |
---|---|---|
autentisering | Tillhandahåller autentiseringsstöd. | UseAuthentication |
Auktorisering | Tillhandahåller auktoriseringsstöd. | UseAuthorization |
CORS | Konfigurerar resursdelning mellan ursprung. | UseCors |
undantagshanterare | Hanterar globalt undantag som genereras av middleware-pipelinen. | UseExceptionHandler |
vidarebefordrade rubriker | Skickar vidare proxy-rubriker till den aktuella begäran. | UseForwardedHeaders |
HTTPS-omdirigering | Omdirigerar alla HTTP-begäranden till HTTPS. | UseHttpsRedirection |
HTTP Strict Transport Security (HSTS) | Mellanprogram för säkerhetsförbättringar som lägger till ett särskilt svarshuvud. | UseHsts |
Begäran Loggning | Ger stöd för loggning av HTTP-begäranden och svar. | UseHttpLogging |
Tidsgränser för förfrågningar | Ger stöd för att konfigurera tidsgränser för begäranden, global standard och per slutpunkt. | UseRequestTimeouts |
W3C-begärandeloggning | Ger stöd för loggning av HTTP-begäranden och svar i W3C-format. | UseW3CLogging |
Cachelagring av svar | Ger stöd för att cachelagra svar. | UseResponseCaching |
komprimering av svar | Ger stöd för att komprimera svar. | UseResponseCompression |
Session | Ger stöd för hantering av användarsessioner. | UseSession |
Static Files | Ger stöd för att hantera statiska filer och katalogbläddring. | UseStaticFiles, UseFileServer |
WebSockets | Aktiverar WebSockets-protokollet. | UseWebSockets |
Följande avsnitt beskriver hantering av begäranden: routning, parameterbindning och svar.
Routning
En konfigurerad WebApplication
stöder Map{Verb}
och MapMethods där {Verb}
är en HTTP-metod med camel-case som Get
, Post
, Put
eller 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 argument som skickas till dessa metoder kallas "routningshanterare".
Routerhanterare
Routethanterare är metoder som körs när rutten matchar. Routningshanterare kan vara ett lambda-uttryck, en lokal funktion, en instansmetod eller en statisk metod. Routningshanterare kan vara synkrona eller asynkrona.
Lambda-uttryck
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();
Lokal funktion
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Instansmetod
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";
}
}
Statisk metod
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";
}
}
Slutpunkt definierad utanför Program.cs
Minimala API:er behöver inte finnas i Program.cs
.
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" });
});
}
}
Se även Ruttgrupper senare i den här artikeln.
Namngivna slutpunkter och länkgenerering
Slutpunkter kan ges namn för att generera URL:er till slutpunkten. Om du använder en namngiven slutpunkt undviker du att behöva hårdkoda sökvägar i en app:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/hello", () => "Hello named route")
.WithName("hi");
app.MapGet("/", (LinkGenerator linker) =>
$"The link to the hello route is {linker.GetPathByName("hi", values: null)}");
app.Run();
Föregående kod visar The link to the hello route is /hello
från slutpunkten /
.
NOTE: Slutpunktsnamn är skiftlägeskänsliga.
Slutpunktsnamn:
- Måste vara globalt unikt.
- Används som OpenAPI-åtgärds-ID när OpenAPI-stöd är aktiverat. Mer information finns i OpenAPI-.
Routningsparametrar
Routningsparametrar kan samlas in som en del av routningsmönsterdefinitionen:
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();
Föregående kod returnerar The user id is 3 and book id is 7
från URI-/users/3/books/7
.
Routningshanteraren kan deklarera parametrarna som ska avbildas. När en begäran görs till en väg med parametrar som deklarerats för avbildning parsas parametrarna och skickas till hanteraren. Det gör det enkelt att samla in värdena på ett säkert sätt. I föregående kod är både userId
och bookId
int
.
Om något av routningsvärdena inte kan konverteras till en int
i föregående kod genereras ett undantag. GET-begäran /users/hello/books/3
genererar följande undantag:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Jokertecken och fånga alla vägar
Följande catch all route returnerar Routing to hello
från slutpunkten "/posts/hello":
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Vägbegränsningar
Routningsbegränsningar begränsar matchningsbeteendet för en väg.
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();
I följande tabell visas föregående vägmallar och deras beteende:
Routningsmall | Exempel på matchande URI |
---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Mer information finns i referens för routningsbegränsningar i Routning i ASP.NET Core.
Ruttgrupper
Metodtillägget MapGroup hjälper till att organisera grupper av slutpunkter med ett gemensamt prefix. Det minskar repetitiv kod och gör det möjligt att anpassa hela grupper av slutpunkter med ett enda anrop till metoder som RequireAuthorization och WithMetadata som lägger till slutpunktsmetadata.
Följande kod skapar till exempel två liknande grupper av slutpunkter:
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;
}
I det här scenariot kan du använda en relativ adress för Location
-huvudet i 201 Created
-resultatet.
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Den första gruppen med slutpunkter matchar endast begäranden som är prefix med /public/todos
och är tillgängliga utan autentisering. Den andra gruppen med slutpunkter matchar endast begäranden som är prefix med /private/todos
och kräver autentisering.
QueryPrivateTodos
slutpunktsfilterfabriken är en lokal funktion som ändrar routningshanterarens TodoDb
parametrar för att tillåta åtkomst till och lagra privata data.
Routningsgrupper stöder också kapslade grupper och komplexa prefixmönster med vägparametrar och begränsningar. I följande exempel kan en routinghanterare som mappas till user
-gruppen fånga upp {org}
- och {group}
-rutparametrarna som definierats i de yttre gruppprefixen.
Prefixet kan också vara tomt. Detta kan vara användbart för att lägga till slutpunktsmetadata eller filter till en grupp slutpunkter utan att ändra vägmönstret.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Att lägga till filter eller metadata i en grupp fungerar på samma sätt som att lägga till dem individuellt i varje slutpunkt innan du lägger till extra filter eller metadata som kan ha lagts till i en inre grupp eller en specifik slutpunkt.
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);
});
I exemplet ovan loggar det yttre filtret den inkommande begäran före det inre filtret trots att det lades till tvåa. Eftersom filtren tillämpades på olika grupper spelar det ingen roll vilken ordning de lades till i förhållande till varandra. Orderfiltren som läggs till spelar roll om de tillämpas på samma grupp eller specifika slutpunkt.
En begäran om att /outer/inner/
loggar följande:
/outer group filter
/inner group filter
MapGet filter
Parameterbindning
Parameterbindning är processen att konvertera begärandedata till starkt skrivna parametrar som uttrycks av routningshanterare. En bindningskälla avgör var parametrarna är bundna från. Bindningskällor kan vara explicita eller härledda baserat på HTTP-metod och parametertyp.
Bindningskällor som stöds:
- Vägvärden
- Frågesträngen
- Rubrik
- Body (som JSON)
- Tjänster som tillhandahålls av beroendeinjektion
- Anpassad
Bindning från formulärvärden är inte stöds internt i .NET 6 och 7.
Följande GET
routningshanterare använder några av dessa parameterbindningskällor:
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 { }
I följande tabell visas relationen mellan de parametrar som används i föregående exempel och de associerade bindningskällorna.
Parameter | Bindningskälla |
---|---|
id |
routvärde |
page |
Frågesträngen |
customHeader |
rubrik |
service |
Tillhandahålls genom dependency injection |
HTTP-metoderna GET
, HEAD
, OPTIONS
och DELETE
binder inte implicit från brödtexten. Om du vill binda från brödtexten (som JSON) för dessa HTTP-metoder du uttryckligen binda med [FromBody]
eller läsa från HttpRequest.
I följande exempel använder POST-routningshanteraren en bindningskälla för brödtext (som JSON) för parametern person
:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/", (Person person) => { });
record Person(string Name, int Age);
Parametrarna i föregående exempel är alla bundna från begärandedata automatiskt. För att demonstrera bekvämligheten med parameterbindningen visar följande routningshanterare hur du läser begärandedata direkt från begäran:
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>();
// ...
});
Explicit parameterbindning
Attribut kan användas för att explicit deklarera var parametrarna är bundna från.
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 | Bindningskälla |
---|---|
id |
routningsvärde med namnet id |
page |
frågesträng med namnet "p" |
service |
Tillhandahålls av beroendeinmatning |
contentType |
rubrik med namnet "Content-Type" |
Anteckning
Bindning från formulärvärden är inte stöds internt i .NET 6 och 7.
Parameterbindning med beroendeinmatning
Parameterbindning för minimala API:er binder parametrar via beroendeinmatning när typen konfigureras som en tjänst. Det är inte nödvändigt att uttryckligen tillämpa attributet [FromServices]
på en parameter. I följande kod returnerar båda åtgärderna tiden:
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();
Valfria parametrar
Parametrar som deklareras i routningshanterare behandlas efter behov:
- Om en begäran matchar vägen körs routningshanteraren endast om alla obligatoriska parametrar anges i begäran.
- Om du inte anger alla obligatoriska parametrar resulterar det i ett fel.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int pageNumber) => $"Requesting page {pageNumber}");
app.Run();
URI | resultat |
---|---|
/products?pageNumber=3 |
3 returnerade |
/products |
BadHttpRequestException : Den obligatoriska parametern "int pageNumber" angavs inte från frågesträngen. |
/products/1 |
HTTP 404-fel, ingen matchande väg |
Om du vill göra pageNumber
valfri definierar du typen som valfri eller anger ett standardvärde:
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 | resultat |
---|---|
/products?pageNumber=3 |
3 returnerade |
/products |
1 returnerade |
/products2 |
1 returnerade |
Föregående null- och standardvärde gäller för alla källor:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/products", (Product? product) => { });
app.Run();
Föregående kod anropar metoden med en null-produkt om ingen begärandetext skickas.
NOTE: Om ogiltiga data anges och parametern är nullable, körs routningshanteraren inte.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
app.Run();
URI | resultat |
---|---|
/products?pageNumber=3 |
3 har returnerats |
/products |
1 returneras |
/products?pageNumber=two |
BadHttpRequestException : Det gick inte att binda parametern "Nullable<int> pageNumber" från "två". |
/products/two |
HTTP 404-fel, ingen matchande väg |
Se avsnittet Bindningsfel för mer information.
Särskilda typer
Följande typer är bundna utan explicita attribut:
HttpContext: Kontexten som innehåller all information om den aktuella HTTP-begäran eller -svaret:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));
HttpRequest och HttpResponse: HTTP-begäran och HTTP-svar:
app.MapGet("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));
CancellationToken: Den annulleringstoken som är associerad med den aktuella HTTP-begäran:
app.MapGet("/", async (CancellationToken cancellationToken) => await MakeLongRunningRequestAsync(cancellationToken));
ClaimsPrincipal: Användaren som är associerad med begäran, bunden från HttpContext.User:
app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
Binda begärandetexten som en Stream
eller PipeReader
Begärandetexten kan bindas som en Stream
eller PipeReader
för att effektivt stödja scenarier där användaren måste bearbeta data och:
- Lagra data i bloblagring eller lägg data i en köleverantör.
- Bearbeta lagrade data med en arbetsprocess eller molnfunktion.
Data kan till exempel läggas i kö till Azure Queue Storage eller lagras i Azure Blob Storage.
Följande kod implementerar en bakgrundskö:
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;
}
Följande kod binder begärandetexten till en 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);
});
Följande kod visar den fullständiga Program.cs
filen:
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();
- När du läser data är
Stream
samma objekt somHttpRequest.Body
. - Begärandetexten buffrades inte som standard. När brödtexten har lästs kan den inte spolas tillbaka. Dataströmmen kan inte läsas flera gånger.
-
Stream
ochPipeReader
kan inte användas utanför den minimala åtgärdshanteraren eftersom de underliggande buffertarna tas bort eller återanvänds.
Filuppladdningar med IFormFile och IFormFileCollection
Följande kod använder IFormFile och IFormFileCollection för att ladda upp filen:
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();
Autentiserade filuppladdningsbegäranden stöds med hjälp av ett auktoriseringshuvud, ett klientcertifikateller ett cookie-huvud.
Det finns inget inbyggt stöd för antiforgery i ASP.NET Core 7.0.
Antiforgery finns i ASP.NET Core 8.0 och senare. Den kan dock implementeras med hjälp av IAntiforgery
-tjänsten.
Binda matriser och strängvärden från rubriker och frågesträngar
Följande kod visar bindning av frågesträngar till en matris med primitiva typer, strängmatriser och 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]}");
Bindning av frågesträngar eller sidhuvudvärden till en matris med komplexa typer stöds när typen har TryParse
implementerats. Följande kod binder till en strängmatris och returnerar alla objekt med de angivna taggarna:
// 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();
});
Följande kod visar modellen och den nödvändiga TryParse
implementeringen:
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;
}
}
Följande kod binder till en int
matris:
// 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();
});
Om du vill testa koden ovan lägger du till följande slutpunkt för att fylla databasen med Todo
objekt:
// POST /todoitems/batch
app.MapPost("/todoitems/batch", async (Todo[] todos, TodoDb db) =>
{
await db.Todos.AddRangeAsync(todos);
await db.SaveChangesAsync();
return Results.Ok(todos);
});
Använd ett API-testverktyg som HttpRepl
för att skicka följande data till föregående slutpunkt:
[
{
"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"
}
}
]
Följande kod binder till huvudnyckeln X-Todo-Id
och returnerar Todo
objekt med matchande Id
värden:
// 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();
});
Not
När du binder en string[]
från en frågesträng resulterar frånvaron av ett matchande frågesträngsvärde i en tom matris i stället för ett null-värde.
Parameterbindning för argumentlistor med [AsParameters]
AsParametersAttribute möjliggör enkel parameterbindning till typer och inte komplex eller rekursiv modellbindning.
Överväg följande kod:
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.
Överväg följande GET
slutpunkt:
app.MapGet("/todoitems/{id}",
async (int Id, TodoDb Db) =>
await Db.Todos.FindAsync(Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
Följande struct
kan användas för att ersätta de föregående markerade parametrarna:
struct TodoItemRequest
{
public int Id { get; set; }
public TodoDb Db { get; set; }
}
Den omstrukturerade GET
-slutpunkten använder föregående struct
med attributet 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());
Följande kod visar ytterligare slutpunkter i appen:
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();
});
Följande klasser används för att omstrukturera parameterlistorna:
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!;
}
Följande kod visar de omstrukturerade slutpunkterna med hjälp av AsParameters
och den föregående struct
samt klasser:
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();
});
Följande record
typer kan användas för att ersätta de föregående parametrarna:
record TodoItemRequest(int Id, TodoDb Db);
record CreateTodoItemRequest(TodoItemDTO Dto, TodoDb Db);
record EditTodoItemRequest(int Id, TodoItemDTO Dto, TodoDb Db);
Att använda en struct
med AsParameters
kan vara mer högpresterande än att använda en record
typ.
Den fullständiga exempelkoden i lagringsplatsen AspNetCore.Docs.Samples.
Anpassad bindning
Det finns två sätt att anpassa parameterbindning:
- För routnings-, fråge- och rubrikbindningskällor binder du anpassade typer genom att lägga till en statisk
TryParse
metod för typen. - Kontrollera bindningsprocessen genom att implementera en
BindAsync
-metod för en typ.
TryParse
TryParse
har två API:er:
public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);
Följande kod visar Point: 12.3, 10.1
med 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
har följande API:er:
public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);
Följande kod visar SortBy:xyz, SortDirection:Desc, CurrentPage:99
med 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
}
Bindningsfel
När bindningen misslyckas loggar ramverket ett felsökningsmeddelande och returnerar olika statuskoder till klienten beroende på felläget.
Felläge | Parametertyp som kan vara null | Bindningskälla | Statuskod |
---|---|---|---|
{ParameterType}.TryParse returnerar false |
Ja | rutt/förfrågan/rubrik | 400 |
{ParameterType}.BindAsync returnerar null |
Ja | anpassad | 400 |
{ParameterType}.BindAsync kastar |
spelar ingen roll | anpassad | 500 |
Det gick inte att deserialisera JSON-innehåll | spelar ingen roll | kropp | 400 |
Fel innehållstyp (inte application/json ) |
spelar ingen roll | kropp | 415 |
Bindningsprioritet
Reglerna för att fastställa en bindningskälla från en parameter:
- Explicit attribut definierat för parametern (From*-attribut) i följande ordning:
- Ruttvärden:
[FromRoute]
- Frågesträng:
[FromQuery]
- Rubrik:
[FromHeader]
- Brödtext:
[FromBody]
- Tjänst:
[FromServices]
- Parametervärden:
[AsParameters]
- Ruttvärden:
- Särskilda typer
HttpContext
-
HttpRequest
(HttpContext.Request
) -
HttpResponse
(HttpContext.Response
) -
ClaimsPrincipal
(HttpContext.User
) -
CancellationToken
(HttpContext.RequestAborted
) -
IFormFileCollection
(HttpContext.Request.Form.Files
) -
IFormFile
(HttpContext.Request.Form.Files[paramName]
) -
Stream
(HttpContext.Request.Body
) -
PipeReader
(HttpContext.Request.BodyReader
)
- Parametertypen har en giltig statisk
BindAsync
-metod. - Parametertypen är en sträng eller har en giltig statisk
TryParse
-metod.- Om parameternamnet finns i vägmallen. På
app.Map("/todo/{id}", (int id) => {});
ärid
bunden från rutten. - Bunden från frågesträngen.
- Om parameternamnet finns i vägmallen. På
- Om parametertypen är en tjänst som tillhandahålls av beroendeinmatning använder den tjänsten som källa.
- Parametern är från kroppen.
Konfigurera JSON-deserialiseringsalternativ för brödtextbindning
Kroppsbindningskällan använder System.Text.Json för deserialisering. Det är inte möjligt att ändra den här standardinställningen, men JSON-serialiserings- och deserialiseringsalternativ kan konfigureras.
Konfigurera JSON-deserialiseringsalternativ globalt
Alternativ som gäller globalt för en app kan konfigureras genom att anropa ConfigureHttpJsonOptions. Följande exempel innehåller offentliga fält och formaterar JSON-utdata.
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
// }
Eftersom exempelkoden konfigurerar både serialisering och deserialisering kan den läsa NameField
och inkludera NameField
i utdata-JSON.
Konfigurera JSON-deserialiseringsalternativ för en slutpunkt
ReadFromJsonAsync har överlagringar som accepterar ett JsonSerializerOptions-objekt. Följande exempel innehåller offentliga fält och formaterar JSON-utdata.
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
// }
Eftersom föregående kod endast tillämpar de anpassade alternativen för deserialisering utesluter utdata-JSON NameField
.
Läs begärandetexten
Läs begärandetexten direkt med hjälp av en HttpContext- eller 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();
Föregående kod:
- Använder begärandetexten med hjälp av HttpRequest.BodyReader.
- Kopierar begärandetexten till en lokal fil.
Svaren
Routningshanterare stöder följande typer av returvärden:
-
IResult
baserat – Detta omfattarTask<IResult>
ochValueTask<IResult>
-
string
– Detta inkluderarTask<string>
ochValueTask<string>
-
T
(alla andra typer) – Detta inkluderarTask<T>
ochValueTask<T>
Returvärde | Uppförande | Innehållstyp |
---|---|---|
IResult |
Ramverket anropar IResult.ExecuteAsync | Bestäms av IResult -implementeringen |
string |
Ramverket skriver strängen direkt till svaret | text/plain |
T (alla andra typer) |
Ramverket JSON-serialiserar svaret | application/json |
En mer djupgående guide till routningshanterarens returvärden finns i Skapa svar i minimala API-program
Exempel på returvärden
strängreturvärden
app.MapGet("/hello", () => "Hello World");
JSON-returvärden
app.MapGet("/hello", () => new { Message = "Hello World" });
Returnera TypedResults
Följande kod returnerar en TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Det är att föredra att returnera TypedResults
framför att returnera Results. Mer information finns i TypedResults vs Results.
IResult-returvärden
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
I följande exempel används de inbyggda resultattyperna för att anpassa svaret:
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" }));
Anpassad statuskod
app.MapGet("/405", () => Results.StatusCode(405));
Text
app.MapGet("/text", () => Results.Text("This is some text"));
Strömning
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");
});
Se Skapa svar i Minimala API-program för fler exempel.
Omdirigera
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
Fil
app.MapGet("/download", () => Results.File("myfile.text"));
Inbyggda resultat
Vanliga resultathjälpare finns i Results- och TypedResults statiska klasser. Att returnera TypedResults
föredras framför att returnera Results
. Mer information finns i TypedResults vs Results.
Anpassa resultat
Program kan styra svar genom att implementera en anpassad IResult typ. Följande kod är ett exempel på en HTML-resultattyp:
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);
}
}
Vi rekommenderar att du lägger till en tilläggsmetod till Microsoft.AspNetCore.Http.IResultExtensions för att göra dessa anpassade resultat mer lättupptäckta.
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();
Inskrivna resultat
Det IResult gränssnittet kan representera värden som returneras från minimala API:er som inte använder implicit stöd för JSON-serialisering av det returnerade objektet till HTTP-svaret. Klassen static Results används för att skapa varierande IResult
objekt som representerar olika typer av svar. Du kan till exempel ange svarsstatuskoden eller omdirigera till en annan URL.
De typer som implementerar IResult
är offentliga, vilket möjliggör typkontroller vid testning. Till exempel:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Du kan titta på returtyperna för motsvarande metoder på den statiska TypedResults-klassen för att hitta rätt offentlig IResult
typ att casta till.
Se Skapa svar i Minimala API-program för fler exempel.
Filter
Se filter i minimala API-appar
Tillstånd
Vägar kan skyddas med hjälp av auktoriseringsprinciper. Dessa kan deklareras via attributet [Authorize]
eller med hjälp av metoden RequireAuthorization:
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();
Föregående kod kan skrivas med RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
Följande exempel använder principbaserad auktorisering:
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();
Tillåt oautentiserade användare att komma åt en slutpunkt
Med [AllowAnonymous]
kan oautentiserade användare komma åt slutpunkter:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Vägar kan CORS- aktiveras med CORS-principer. CORS kan deklareras via attributet [EnableCors]
eller med hjälp av metoden RequireCors. Följande exempel aktiverar 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("/",() => "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();
Mer information finns i Aktivera CORS (Cross-Origin Requests) i ASP.NET Core
Se även
Det här dokumentet:
- Innehåller en snabbreferens för minimala API:er.
- Är avsedd för erfarna utvecklare. En introduktion finns i Självstudie: Skapa ett minimalt API med ASP.NET Core
De minimala API:erna består av:
- WebApplication och WebApplicationBuilder
- Routningshanterare
WebApplication
Följande kod genereras av en ASP.NET Core-mall:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Föregående kod kan skapas via dotnet new web
på kommandoraden eller genom att välja mallen Tom webb i Visual Studio.
Följande kod skapar en WebApplication (app
) utan att uttryckligen skapa en WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create
initierar en ny instans av klassen WebApplication med förkonfigurerade standardvärden.
Arbeta med portar
När en webbapp skapas med Visual Studio eller dotnet new
skapas en Properties/launchSettings.json
fil som anger de portar som appen svarar på. I portinställningsexemplen som följer returnerar körning av appen från Visual Studio en feldialogruta Unable to connect to web server 'AppName'
. Kör följande portförändrande exempel från kommandoraden.
Följande avsnitt anger den port som appen svarar på.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
I föregående kod svarar appen på port 3000
.
Flera portar
I följande kod svarar appen på port 3000
och 4000
.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Ange porten från kommandoraden
Följande kommando gör att appen svarar på port 7777
:
dotnet run --urls="https://localhost:7777"
Om slutpunkten Kestrel också har konfigurerats i appsettings.json
-filen, används den URL som anges av appsettings.json
. Mer information finns i Kestrel slutpunktskonfiguration
Läsa porten från miljön
Följande kod läser porten från miljön:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Det bästa sättet att ange porten från miljön är att använda ASPNETCORE_URLS
miljövariabeln, som visas i följande avsnitt.
Ange portarna via miljövariabeln ASPNETCORE_URLS
Miljövariabeln ASPNETCORE_URLS
är tillgänglig för att ange porten:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS
stöder flera URL:er:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Lyssna på alla gränssnitt
Följande exempel visar att du lyssnar på alla gränssnitt
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();
Lyssna på alla gränssnitt med hjälp av ASPNETCORE_URLS
Föregående exempel kan använda ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Ange HTTPS med utvecklingscertifikat
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Mer information om utvecklingscertifikatet finns i Trust the ASP.NET Core HTTPS development certificate on Windows and macOS.
Ange HTTPS med ett anpassat certifikat
Följande avsnitt visar hur du anger det anpassade certifikatet med hjälp av appsettings.json
-filen och via konfiguration.
Ange det anpassade certifikatet med appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Ange det anpassade certifikatet via konfiguration
var builder = WebApplication.CreateBuilder(args);
// Configure the cert and the key
builder.Configuration["Kestrel:Certificates:Default:Path"] = "cert.pem";
builder.Configuration["Kestrel:Certificates:Default:KeyPath"] = "key.pem";
var app = builder.Build();
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Använda certifikat-API:erna
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();
Uppfatta miljön
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();
Mer information om hur du använder miljön finns i Använda flera miljöer i ASP.NET Core
Konfiguration
Följande kod läser från konfigurationssystemet:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Hello";
app.MapGet("/", () => message);
app.Run();
Mer information finns i Configuration i ASP.NET Core
Skogsavverkning
Följande kod skriver ett meddelande till loggen vid programstart:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Mer information finns i Loggning i .NET Core och ASP.NET Core
Få åtkomst till beroendeinjektionscontainern (DI)
Följande kod visar hur du hämtar tjänster från DI-containern under programstarten:
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();
Mer information finns i Beroendeinmatning i ASP.NET Core.
WebApplicationBuilder
Det här avsnittet innehåller exempelkod med hjälp av WebApplicationBuilder.
Ändra innehållsrot, programnamn och miljö
Följande kod anger innehållsrot, programnamn och miljö:
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 initierar en ny instans av klassen WebApplicationBuilder med förkonfigurerade standardvärden.
Mer information finns i översikten över grunderna i ASP.NET Core
Ändra innehållsrot, appnamn och miljö efter miljövariabler eller kommandorad
I följande tabell visas miljövariabeln och kommandoradsargumentet som används för att ändra innehållsrot, appnamn och miljö:
egenskap | Miljövariabel | Kommandoradsargument |
---|---|---|
Programnamn | ASPNETCORE_APPLICATIONNAME | --applicationName |
Miljönamn | ASPNETCORE_ENVIRONMENT | --miljö |
Innehållsrot | ASPNETCORE_CONTENTROOT | --contentRoot |
Lägga till konfigurationsprovidrar
Följande exempel lägger till INI-konfigurationsprovidern:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Detaljerad information finns i Filkonfigurationsproviders i Configuration i ASP.NET Core.
Läs konfiguration
Som standard läser WebApplicationBuilder konfigurationen från flera källor, inklusive:
-
appSettings.json
ochappSettings.{environment}.json
- Miljövariabler
- Kommandoraden
En fullständig lista över konfigurationskällor finns i Standardkonfiguration i Configuration i ASP.NET Core
Följande kod läser HelloKey
från konfigurationen och visar värdet vid /
slutpunkten. Om konfigurationsvärdet är null tilldelas "Hello" till message
:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Analysera miljön
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Lägg till loggningstjänster
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();
Lägga till tjänster
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();
Anpassa IHostBuilder
Befintliga tilläggsmetoder på IHostBuilder kan nås med egenskapen 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();
Anpassa IWebHostBuilder
Tilläggsmetoder på IWebHostBuilder kan nås med hjälp av egenskapen 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();
Ändra webbplatsroten
Som standard är webbroten relativ till innehållsroten i mappen wwwroot
. Webbroten är platsen där mellanprogrammet för statiska filer letar efter statiska filer. Webbroten kan ändras med WebHostOptions
, kommandoraden eller med metoden UseWebRoot:
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Container för anpassad beroendeinjektion (DI)
I följande exempel används Autofac:
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();
Lägg till mellanprogram
Alla befintliga ASP.NET Core-mellanprogram kan konfigureras på WebApplication
:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Mer information finns i ASP.NET Core Middleware
Undantagssida för utvecklare
WebApplication.CreateBuilder initierar en ny instans av klassen WebApplicationBuilder med förkonfigurerade standardvärden. Undantagssidan för utvecklare är aktiverad i de förkonfigurerade standardvärdena. När följande kod körs i utvecklingsmiljön, resulterar navigering till /
i en vänlig sida som visar undantaget.
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
I följande tabell visas några av de mellanprogram som ofta används med minimala API:er.
Mellanprogram | Beskrivning | API |
---|---|---|
autentisering | Tillhandahåller autentiseringsstöd. | UseAuthentication |
Auktorisering | Tillhandahåller auktoriseringsstöd. | UseAuthorization |
CORS | Konfigurerar resursdelning mellan ursprung. | UseCors |
undantagshanterare | Globalt hanterar undantag som genereras av pipelinen för mellanprogram. | UseExceptionHandler |
vidarebefordrade rubriker | Vidarebefordrar proxyhuvuden till den aktuella begäran. | UseForwardedHeaders |
HTTPS-omdirigering | Omdirigerar alla HTTP-begäranden till HTTPS. | UseHttpsRedirection |
HTTP Strict Transport Security (HSTS) | Mellanprogramvara för säkerhetsförbättringar som lägger till ett särskilt svarshuvud. | UseHsts |
Begärandeloggning | Ger stöd för loggning av HTTP-begäranden och svar. | UseHttpLogging |
W3C-begärandeloggning | Ger stöd för loggning av HTTP-begäranden och svar i W3C-format. | UseW3CLogging |
cachelagring av svar | Ger stöd för cachning av svar. | UseResponseCaching |
komprimering av svar | Ger stöd för att komprimera svar. | UseResponseCompression |
session | Ger stöd för hantering av användarsessioner. | UseSession |
Statiska filer | Ger stöd för att hantera statiska filer och katalogbläddring. | UseStaticFiles, UseFileServer |
WebSockets | Aktiverar WebSockets-protokollet. | UseWebSockets |
Hantering av begäranden
Följande avsnitt beskriver routning, parameterbindning och svar.
Routning
En konfigurerad WebApplication
stöder Map{Verb}
och 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();
Routningshanterare
Rutt-hanterare är metoder som körs när rutten matchar. Routhanterare kan vara en funktion av vilken form som helst, inklusive synkrona eller asynkrona. Routningshanterare kan vara ett lambda-uttryck, en lokal funktion, en instansmetod eller en statisk metod.
Lambda-uttryck
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();
Lokal funktion
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Instansmetod
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";
}
}
Statisk metod
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";
}
}
Namngivna slutpunkter och länkgenerering
Slutpunkter kan ges namn för att generera URL:er till slutpunkten. Om du använder en namngiven slutpunkt undviker du att behöva hårdkoda sökvägar i en app:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/hello", () => "Hello named route")
.WithName("hi");
app.MapGet("/", (LinkGenerator linker) =>
$"The link to the hello route is {linker.GetPathByName("hi", values: null)}");
app.Run();
Föregående kod visar The link to the hello endpoint is /hello
från slutpunkten /
.
NOTE: Slutpunktsnamn är skiftlägeskänsliga.
Slutpunktsnamn:
- Måste vara globalt unikt.
- Används som OpenAPI-åtgärds-ID när OpenAPI-stöd är aktiverat. Mer information finns i OpenAPI-.
Routningsparametrar
Routningsparametrar kan samlas in som en del av routningsmönsterdefinitionen:
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();
Föregående kod returnerar The user id is 3 and book id is 7
från URI-/users/3/books/7
.
Routningshanteraren kan deklarera parametrarna som ska avbildas. När en begäran görs till en väg med parametrar som deklarerats för att fånga, parsas parametrarna och skickas till hanteraren. Det gör det enkelt att samla in värdena på ett säkert sätt. I föregående kod är både userId
och bookId
int
.
Om något av routningsvärdena inte kan konverteras till en int
i föregående kod genereras ett undantag. GET-begäran /users/hello/books/3
genererar följande undantag:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Jokertecken och fånga alla vägar
Följande catch all route returnerar Routing to hello
från slutpunkten "/posts/hello":
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Vägbegränsningar
Routningsbegränsningar begränsar matchningsbeteendet för en väg.
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();
I följande tabell visas föregående vägmallar och deras beteende:
Routningsmall | Exempel på matchande URI |
---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Mer information finns i referens för routningsbegränsningar i Routning i ASP.NET Core.
Parameterbindning
Parameterbindning är processen att konvertera begärandedata till starkt skrivna parametrar som uttrycks av routningshanterare. En bindningskälla avgör var parametrarna är bundna från. Bindningskällor kan vara explicita eller härledda baserat på HTTP-metod och parametertyp.
Bindningskällor som stöds:
- Vägvärden
- Frågesträngen
- Rubrik
- Kropp (som JSON)
- Tjänster som tillhandahålls av beroendeinjektion
- Anpassad
Not
Bindning från formulärvärden stöds inte internt i .NET.
I följande exempel använder GET-routningshanteraren några av dessa parameterbindningskällor:
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 { }
I följande tabell visas relationen mellan de parametrar som används i föregående exempel och de associerade bindningskällorna.
Parameter | Bindningskälla |
---|---|
id |
ruttvärde |
page |
Frågesträngen |
customHeader |
rubrik |
service |
Tillhandahålls av beroendeinjektion |
HTTP-metoderna GET
, HEAD
, OPTIONS
och DELETE
binder inte implicit från brödtexten. Om du vill binda från brödtexten (som JSON) för dessa HTTP-metoder du uttryckligen binda med [FromBody]
eller läsa från HttpRequest.
I följande exempel använder POST-routningshanteraren en bindningskälla för brödtext (som JSON) för parametern person
:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/", (Person person) => { });
record Person(string Name, int Age);
Parametrarna i föregående exempel är alla bundna från begärandedata automatiskt. För att demonstrera bekvämligheten med parameterbindningen visar följande exempel routningshanterare hur du läser begärandedata direkt från begäran:
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>();
// ...
});
Explicit parameterbindning
Attribut kan användas för att explicit deklarera var parametrarna är bundna från.
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 | Bindningskälla |
---|---|
id |
routvärde med namnet id |
page |
frågesträng med namnet "p" |
service |
Tillhandahålls av beroendeinjektion |
contentType |
rubrik med namnet "Content-Type" |
Not
Bindning från formulärvärden stöds inte internt i .NET.
Parameterbindning med DI
Parameterbindning för minimala API:er binder parametrar via beroendeinmatning när typen konfigureras som en tjänst. Det är inte nödvändigt att uttryckligen tillämpa attributet [FromServices]
på en parameter. I följande kod returnerar båda åtgärderna tiden:
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();
Valfria parametrar
Parametrar som deklareras i routningshanterare behandlas efter behov:
- Om en begäran matchar vägen körs routningshanteraren endast om alla obligatoriska parametrar anges i begäran.
- Om du inte anger alla obligatoriska parametrar resulterar det i ett fel.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int pageNumber) => $"Requesting page {pageNumber}");
app.Run();
URI | resultat |
---|---|
/products?pageNumber=3 |
3 returnerade |
/products |
BadHttpRequestException : Den obligatoriska parametern "int pageNumber" angavs inte från frågesträngen. |
/products/1 |
HTTP 404-fel, ingen matchande väg |
Om du vill göra pageNumber
valfri definierar du typen som valfri eller anger ett standardvärde:
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 | resultat |
---|---|
/products?pageNumber=3 |
3 returnerade |
/products |
1 returnerade |
/products2 |
1 returnerade |
Föregående null- och standardvärde gäller för alla källor:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/products", (Product? product) => { });
app.Run();
Föregående kod anropar metoden med en null-produkt om ingen begärandetext skickas.
NOTE: Om ogiltiga data anges och parametern är null kan routningshanteraren inte köras.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
app.Run();
URI | resultat |
---|---|
/products?pageNumber=3 |
3 har återvänt |
/products |
1 returnerade |
/products?pageNumber=two |
BadHttpRequestException : Det gick inte att binda parametern "Nullable<int> pageNumber" från "två". |
/products/two |
HTTP 404-fel, ingen matchande väg |
Mer information finns i avsnittet Bindningsfel.
Särskilda typer
Följande typer är bundna utan explicita attribut:
HttpContext: Kontexten som innehåller all information om den aktuella HTTP-begäran eller -svaret:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));
HttpRequest och HttpResponse: HTTP-begäran och HTTP-svar:
app.MapGet("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));
CancellationToken: Den annulleringstoken som är associerad med den aktuella HTTP-begäran:
app.MapGet("/", async (CancellationToken cancellationToken) => await MakeLongRunningRequestAsync(cancellationToken));
ClaimsPrincipal: Användaren som är associerad med begäran, bunden från HttpContext.User:
app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
Anpassad bindning
Det finns två sätt att anpassa parameterbindning:
- För routnings-, fråge- och rubrikbindningskällor binder du anpassade typer genom att lägga till en statisk
TryParse
metod för typen. - Kontrollera bindningsprocessen genom att implementera en
BindAsync
-metod för en typ.
TryParse
TryParse
har två API:er:
public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);
Följande kod visar Point: 12.3, 10.1
med 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
har följande API:er:
public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);
Följande kod visar SortBy:xyz, SortDirection:Desc, CurrentPage:99
med 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
}
Bindningsfel
När bindningen misslyckas loggar ramverket ett felsökningsmeddelande och returnerar olika statuskoder till klienten beroende på felläget.
Felläge | Parametertyp som kan vara null | Bindningskälla | Statuskod |
---|---|---|---|
{ParameterType}.TryParse returnerar false |
Ja | route/query/header | 400 |
{ParameterType}.BindAsync returnerar null |
Ja | anpassad | 400 |
{ParameterType}.BindAsync kastar |
spelar ingen roll | anpassat | 500 |
Det gick inte att deserialisera JSON-kropp | spelar ingen roll | kropp | 400 |
Fel innehållstyp (inte application/json ) |
spelar ingen roll | kropp | 415 |
Bindningsprioritet
Reglerna för att fastställa en bindningskälla från en parameter:
- Explicit attribut definierat på parametern (From*-attribut) i följande ordning:
- Vägvärden:
[FromRoute]
- Frågesträng:
[FromQuery]
- Rubrik:
[FromHeader]
- Brödtext:
[FromBody]
- Tjänst:
[FromServices]
- Vägvärden:
- Särskilda typer
- Parametertypen har en giltig
BindAsync
-metod. - Parametertypen är en sträng eller har en giltig
TryParse
-metod.- Om parameternamnet finns i vägmallen. På
app.Map("/todo/{id}", (int id) => {});
ärid
bunden från rutten. - Bunden från frågesträngen.
- Om parameternamnet finns i vägmallen. På
- Om parametertypen är en tjänst som tillhandahålls av beroendeinjektion, används den tjänsten som källa.
- Parametern kommer från kroppen.
Anpassa JSON-koppling
Datainkapslingskällan använder System.Text.Json för deserialisering. Det är inte möjligt att ändra den här standardinställningen, men bindningen kan anpassas med hjälp av andra tekniker som beskrevs tidigare. Om du vill anpassa JSON-serialiseraralternativ använder du kod som liknar följande:
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;
}
Föregående kod:
- Konfigurerar JSON-standardalternativen för indata och utdata.
- Returnerar följande JSON
När du publicerar{ "id": 1, "name": "Joe Smith" }
{ "Id": 1, "Name": "Joe Smith" }
Läs begärandetexten
Läs begärandetexten direkt med hjälp av en HttpContext- eller 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();
Föregående kod:
- "Åtkomst till begärandetexten med HttpRequest.BodyReader."
- Kopierar begärandetexten till en lokal fil.
Svaren
Routningshanterare stöder följande typer av returvärden:
-
IResult
baserat – Detta omfattarTask<IResult>
ochValueTask<IResult>
-
string
– Detta inkluderarTask<string>
ochValueTask<string>
-
T
(alla andra typer) – Detta inkluderarTask<T>
ochValueTask<T>
Returvärde | Uppförande | Innehållstyp |
---|---|---|
IResult |
Ramverket anropar IResult.ExecuteAsync | Bestäms av IResult implementeringen |
string |
Ramverket skriver strängen direkt till svaret | text/plain |
T (alla andra typer) |
Ramverket serialiserar svaret med JSON | application/json |
Exempel på returvärden
strängreturvärden
app.MapGet("/hello", () => "Hello World");
JSON-returvärden
app.MapGet("/hello", () => new { Message = "Hello World" });
IResult-returvärden
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
I följande exempel används de inbyggda resultattyperna för att anpassa svaret:
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" }));
Anpassad statuskod
app.MapGet("/405", () => Results.StatusCode(405));
Text
app.MapGet("/text", () => Results.Text("This is some text"));
Strömning
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();
Omdirigera
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
Fil
app.MapGet("/download", () => Results.File("myfile.text"));
Inbyggda resultat
Vanliga resultathjälpare finns i den Microsoft.AspNetCore.Http.Results
statiska klassen.
Beskrivning | Svarstyp | Statuskod | API |
---|---|---|---|
Skriva ett JSON-svar med avancerade alternativ | application/json | 200 | Results.Json |
Skriva ett JSON-svar | application/json | 200 | Resultat.Okej |
Skriva ett textsvar | text/oformaterad (standard), konfigurerbar | 200 | Resultat.Text |
Skriv svaret som byte | application/octet-stream (standard), konfigurerbar | 200 | Results.Bytes |
Skriva en ström med bytes till svaret | application/octet-stream (standard), konfigurerbar | 200 | Results.Stream |
Strömma en fil till svaret för nedladdning med content-disposition-huvudet | application/octet-stream (standard), konfigurerbar | 200 | Results.File |
Ange statuskoden till 404 med ett valfritt JSON-svar | Ej tillämpligt | 404 | Resultat.InteHittat |
Ange statuskoden till 204 | Ej tillämpligt | 204 | Resultat.IngenInnehåll |
Ange statuskoden till 422 med ett valfritt JSON-svar | Ej tillämpligt | 422 | Results.UnprocessableEntity |
Ange statuskoden till 400 med ett valfritt JSON-svar | Ej tillämpligt | 400 | Results.BadRequest |
Ange statuskoden till 409 med ett valfritt JSON-svar | Ej tillämpligt | 409 | Results.Conflict |
Skriva ett JSON-objekt för probleminformation till svaret | Ej tillämpligt | 500 (standard) kan konfigureras | Resultat.Problem |
Skriva ett JSON-objekt för probleminformation till svaret med valideringsfel | Ej tillämpligt | N/A, kan konfigureras | Results.ValidationProblem |
Anpassa resultat
Program kan styra svar genom att implementera en anpassad IResult typ. Följande kod är ett exempel på en HTML-resultattyp:
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);
}
}
Vi rekommenderar att du lägger till en tilläggsmetod till Microsoft.AspNetCore.Http.IResultExtensions för att göra dessa anpassade resultat mer upptäckbara.
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();
Tillstånd
Vägar kan skyddas med hjälp av auktoriseringsprinciper. Dessa kan deklareras via attributet [Authorize]
eller med hjälp av metoden RequireAuthorization:
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();
Föregående kod kan skrivas med RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
Följande exempel använder principbaserad auktorisering:
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();
Tillåt oautentiserade användare att komma åt en slutpunkt
Med [AllowAnonymous]
kan oautentiserade användare komma åt slutpunkter:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Rutter kan CORS aktiveras med CORS-principer. CORS kan deklareras via attributet [EnableCors]
eller med hjälp av metoden RequireCors. Följande exempel aktiverar 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("/",() => "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();
Mer information finns i Aktivera CORS (Cross-Origin Requests) i ASP.NET Core
Se även
ASP.NET Core