Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Note
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z aktualną wersją, zobacz artykuł w wersji .NET 10.
Ten dokument:
- Zawiera skróconą dokumentację dotyczącą minimalnych interfejsów API.
- Jest przeznaczony dla doświadczonych deweloperów. Aby zapoznać się z wprowadzeniem, zobacz Samouczek: tworzenie minimalnego interfejsu API przy użyciu platformy ASP.NET Core.
Minimalne interfejsy API składają się z następujących elementów:
WebApplication
Następujący kod jest generowany przez szablon ASP.NET Core:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Powyższy kod można utworzyć za pomocą wiersza dotnet new web polecenia lub wybrać pusty szablon sieci Web w programie Visual Studio.
Poniższy kod tworzy element WebApplication (app) bez jawnego utworzenia elementu WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create Inicjuje nowe wystąpienie WebApplication klasy ze wstępnie skonfigurowanymi wartościami domyślnymi.
WebApplication program automatycznie dodaje następujące oprogramowanie pośredniczące w aplikacjach interfejsu API w zależności od określonych warunków:
-
UseDeveloperExceptionPageelement jest dodawany jako pierwszy, gdy parametr ma wartośćHostingEnvironment"Development". -
UseRoutingJest dodawany drugi, jeśli kod użytkownika nie został jeszcze wywołanyUseRoutingi jeśli istnieją skonfigurowane punkty końcowe, na przykładapp.MapGet. -
UseEndpointsJest dodawany na końcu potoku oprogramowania pośredniczącego, jeśli są skonfigurowane jakiekolwiek punkty końcowe. -
UseAuthenticationjest dodawany natychmiast poUseRoutingtym, jak kod użytkownika nie został jeszcze wywołanyUseAuthenticationi czyIAuthenticationSchemeProvidermożna go wykryć u dostawcy usług.IAuthenticationSchemeProviderprogram jest domyślnie dodawany podczas korzystania z usługAddAuthentication, a usługa jest wykrywana przy użyciu poleceniaIServiceProviderIsService. -
UseAuthorizationZostanie dodany dalej, jeśli kod użytkownika nie został jeszcze wywołanyUseAuthorizationi czyIAuthorizationHandlerProvidermożna go wykryć u dostawcy usług.IAuthorizationHandlerProviderprogram jest domyślnie dodawany podczas korzystania z usługAddAuthorization, a usługa jest wykrywana przy użyciu poleceniaIServiceProviderIsService. - Oprogramowanie pośredniczące skonfigurowane przez użytkownika i punkty końcowe są dodawane między elementami
UseRoutingiUseEndpoints.
Poniższy kod jest w rzeczywistości tym, co tworzy automatyczne oprogramowanie pośredniczące dodawane do aplikacji:
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 => {});
W niektórych przypadkach domyślna konfiguracja oprogramowania pośredniczącego nie jest poprawna dla aplikacji i wymaga modyfikacji. Na przykład UseCors należy wywołać metodę przed UseAuthentication i UseAuthorization. Aplikacja musi wywołać metodę UseAuthentication , a UseAuthorization jeśli UseCors jest wywoływana:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Jeśli oprogramowanie pośredniczące powinno być uruchamiane przed rozpoczęciem dopasowywania tras, UseRouting należy wywołać metodę , a oprogramowanie pośredniczące powinno zostać umieszczone przed wywołaniem metody UseRouting.
UseEndpoints nie jest wymagany w tym przypadku, ponieważ jest automatycznie dodawany zgodnie z wcześniejszym opisem:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Podczas dodawania oprogramowania pośredniczącego terminalu:
- Oprogramowanie pośredniczące musi zostać dodane po .
UseEndpoints - Aplikacja musi wywołać metodę
UseRoutingiUseEndpointstak, aby oprogramowanie pośredniczące terminalu można było umieścić w odpowiedniej lokalizacji.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Oprogramowanie pośredniczące terminala to oprogramowanie pośredniczące uruchamiane, jeśli żaden punkt końcowy nie obsługuje żądania.
Praca z portami
Po utworzeniu aplikacji internetowej za pomocą programu Visual Studio lub dotnet newProperties/launchSettings.json zostanie utworzony plik, który określa porty, na które odpowiada aplikacja. W poniższych przykładach ustawień portów uruchomienie aplikacji z programu Visual Studio zwraca okno dialogowe Unable to connect to web server 'AppName'błędu . Program Visual Studio zwraca błąd, ponieważ oczekuje portu określonego w Properties/launchSettings.jsonelemecie , ale aplikacja używa portu określonego przez app.Run("http://localhost:3000"). Uruchom następujący port, zmieniając przykłady z wiersza polecenia.
W poniższych sekcjach ustawiono port, na który odpowiada aplikacja.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
W poprzednim kodzie aplikacja odpowiada na port 3000.
Wiele portów
W poniższym kodzie aplikacja odpowiada na port 3000 i 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Ustawianie portu z wiersza polecenia
Następujące polecenie powoduje, że aplikacja odpowiada na port 7777:
dotnet run --urls="https://localhost:7777"
Kestrel Jeśli punkt końcowy jest również skonfigurowany w appsettings.json pliku, appsettings.json używany jest określony adres URL. Aby uzyskać więcej informacji, zobacz Kestrel Konfiguracja punktu końcowego
Odczytywanie portu ze środowiska
Poniższy kod odczytuje port ze środowiska:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Preferowanym sposobem ustawienia portu ze środowiska jest użycie ASPNETCORE_URLS zmiennej środowiskowej, która jest pokazana w poniższej sekcji.
Ustawianie portów za pomocą zmiennej środowiskowej ASPNETCORE_URLS
Zmienna ASPNETCORE_URLS środowiskowa jest dostępna do ustawienia portu:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS obsługuje wiele adresów URL:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Nasłuchiwanie we wszystkich interfejsach
W poniższych przykładach pokazano nasłuchiwanie we wszystkich interfejsach
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();
Nasłuchiwanie we wszystkich interfejsach przy użyciu ASPNETCORE_URLS
Powyższe przykłady mogą być używane ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Nasłuchiwanie we wszystkich interfejsach przy użyciu ASPNETCORE_HTTPS_PORTS
Powyższe przykłady mogą używać elementów ASPNETCORE_HTTPS_PORTS i ASPNETCORE_HTTP_PORTS.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Aby uzyskać więcej informacji, zobacz Konfigurowanie punktów końcowych dla serwera internetowego platformy ASP.NET Core Kestrel
Określanie protokołu HTTPS przy użyciu certyfikatu programistycznego
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Aby uzyskać więcej informacji na temat certyfikatu programistycznego, zobacz Trust the ASP.NET Core HTTPS development certificate on Windows and macOS (Ufaj certyfikatowi programistycznemu ASP.NET Core HTTPS w systemach Windows i macOS).
Określanie protokołu HTTPS przy użyciu certyfikatu niestandardowego
W poniższych sekcjach pokazano, jak określić certyfikat niestandardowy przy użyciu appsettings.json pliku i za pośrednictwem konfiguracji.
Określanie certyfikatu niestandardowego za pomocą polecenia appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Określanie certyfikatu niestandardowego za pomocą konfiguracji
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();
Korzystanie z interfejsów API certyfikatów
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();
Odczytywanie środowiska
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();
Aby uzyskać więcej informacji na temat korzystania ze środowiska, zobacz ASP.NET Core runtime environments (Środowiska uruchomieniowe ASP.NET Core)
Configuration
Poniższy kod odczytuje z systemu konfiguracji:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Aby uzyskać więcej informacji, zobacz Configuration in ASP.NET Core (Konfiguracja w programie ASP.NET Core)
Logging
Poniższy kod zapisuje komunikat podczas uruchamiania aplikacji logowania:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Aby uzyskać więcej informacji, zobacz Rejestrowanie na platformie .NET i ASP.NET Core
Uzyskiwanie dostępu do kontenera wstrzykiwania zależności (DI)
Poniższy kod pokazuje, jak pobrać usługi z kontenera DI podczas uruchamiania aplikacji:
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();
Poniższy kod pokazuje, jak uzyskać dostęp do kluczy z kontenera DI przy użyciu atrybutu [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.";
}
Aby uzyskać więcej informacji na temat di, zobacz Wstrzykiwanie zależności w ASP.NET Core.
WebApplicationBuilder
Ta sekcja zawiera przykładowy kod przy użyciu polecenia WebApplicationBuilder.
Zmienianie katalogu głównego zawartości, nazwy aplikacji i środowiska
Poniższy kod ustawia katalog główny zawartości, nazwę aplikacji i środowisko:
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 inicjuje nowe wystąpienie klasy WebApplicationBuilder ze wstępnie skonfigurowanymi wartościami domyślnymi.
Aby uzyskać więcej informacji, zobacz omówienie podstaw platformy ASP.NET Core
Zmienianie katalogu głównego zawartości, nazwy aplikacji i środowiska przy użyciu zmiennych środowiskowych lub wiersza polecenia
W poniższej tabeli przedstawiono zmienną środowiskową i argument wiersza polecenia używany do zmiany katalogu głównego zawartości, nazwy aplikacji i środowiska:
| cecha | Zmienna środowiskowa | Argument wiersza polecenia |
|---|---|---|
| Nazwa aplikacji | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Nazwa środowiska | ASPNETCORE_ENVIRONMENT | --environment |
| Katalog główny zawartości | ASPNETCORE_CONTENTROOT | --contentRoot |
Dodawanie dostawców konfiguracji
Poniższy przykład dodaje dostawcę konfiguracji INI:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Aby uzyskać szczegółowe informacje, zobacz Dostawcy konfiguracji plików w konfiguracji w programie ASP.NET Core.
Konfiguracja odczytu
Domyślnie WebApplicationBuilder konfiguracja odczytu z wielu źródeł, w tym:
-
appSettings.jsoniappSettings.{environment}.json - Zmienne środowiskowe
- Wiersz polecenia
Aby uzyskać pełną listę źródeł konfiguracji, zobacz Konfiguracja domyślna w konfiguracji w programie ASP.NET Core.
Poniższy kod odczytuje HelloKey z konfiguracji i wyświetla wartość w punkcie / końcowym. Jeśli wartość konfiguracji ma wartość null, "Hello" zostanie przypisana do elementu message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Odczytywanie środowiska
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine($"Running in development.");
}
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Dodawanie dostawców rejestrowania
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();
Dodawanie usług
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();
Dostosowywanie elementu IHostBuilder
Dostęp do istniejących metod rozszerzeń IHostBuilder można uzyskać przy użyciu właściwości 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();
Dostosowywanie obiektu IWebHostBuilder
Dostęp do metod rozszerzeń IWebHostBuilder można uzyskać przy użyciu właściwości 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();
Zmienianie katalogu głównego sieci Web
Domyślnie katalog główny sieci Web jest powiązany z katalogem głównym zawartości w folderze wwwroot . Katalog główny sieci Web to miejsce, w którym oprogramowanie pośredniczące plików statycznych szuka plików statycznych. Katalog główny sieci Web można zmienić za pomocą WebHostOptionspolecenia , wiersza polecenia lub UseWebRoot metody :
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Niestandardowy kontener wstrzykiwania zależności (DI)
W poniższym przykładzie użyto funkcji 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();
Dodawanie oprogramowania pośredniczącego
W programie WebApplicationmożna skonfigurować dowolne istniejące oprogramowanie pośredniczące ASP.NET Core:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Aby uzyskać więcej informacji, zobacz ASP.NET Core Middleware
Strona wyjątku dla deweloperów
WebApplication.CreateBuilder Inicjuje nowe wystąpienie WebApplicationBuilder klasy ze wstępnie skonfigurowanymi wartościami domyślnymi. Strona wyjątku dewelopera jest włączona w wstępnie skonfigurowanych wartościach domyślnych. Po uruchomieniu następującego kodu w środowisku deweloperskim przejście do / strony renderuje przyjazną stronę, która pokazuje wyjątek.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () =>
{
throw new InvalidOperationException("Oops, the '/' route has thrown an exception.");
});
app.Run();
Oprogramowanie pośredniczące platformy ASP.NET Core
W poniższej tabeli wymieniono niektóre oprogramowanie pośredniczące często używane z minimalnymi interfejsami API.
| Middleware | Description | API |
|---|---|---|
| Authentication | Zapewnia obsługę uwierzytelniania. | UseAuthentication |
| Authorization | Zapewnia obsługę autoryzacji. | UseAuthorization |
| CORS | Konfiguruje współużytkowanie zasobów między źródłami. | UseCors |
| Procedura obsługi wyjątków | Globalnie obsługuje wyjątki zgłaszane przez potok oprogramowania pośredniczącego. | UseExceptionHandler |
| Przekierowane nagłówki | Przekazuje nagłówki przesłane przez serwer proxy do bieżącego żądania. | UseForwardedHeaders |
| Przekierowywanie HTTPS | Przekierowuje wszystkie żądania HTTP do protokołu HTTPS. | UseHttpsRedirection |
| Http Strict Transport Security (HSTS) | Oprogramowanie pośredniczące rozszerzenia zabezpieczeń, które dodaje specjalny nagłówek odpowiedzi. | UseHsts |
| Rejestrowanie żądań | Zapewnia obsługę rejestrowania żądań HTTP i odpowiedzi. | UseHttpLogging |
| Limity czasu żądania | Zapewnia obsługę konfigurowania limitów czasu żądań, wartości domyślnych globalnych i poszczególnych punktów końcowych. | UseRequestTimeouts |
| Rejestrowanie żądań W3C | Zapewnia obsługę rejestrowania żądań HTTP i odpowiedzi w formacie W3C. | UseW3CLogging |
| Buforowanie odpowiedzi | Zapewnia obsługę buforowania odpowiedzi. | UseResponseCaching |
| Kompresja odpowiedzi | Zapewnia obsługę kompresowania odpowiedzi. | UseResponseCompression |
| Session | Zapewnia obsługę zarządzania sesjami użytkowników. | UseSession |
| Pliki statyczne | Zapewnia obsługę plików statycznych i przeglądania katalogów. | UseStaticFiles, UseFileServer |
| WebSockets | Włącza protokoły WebSocket. | UseWebSockets |
W poniższych sekcjach omówiono obsługę żądań: routing, powiązanie parametrów i odpowiedzi.
Routing
Skonfigurowana WebApplication obsługuje Map{Verb} metodę MapMethods{Verb} HTTP typu camel-cased, npGet. , Post, Putlub 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();
Argumenty Delegate przekazywane do tych metod są nazywane "procedurami obsługi tras".
Programy obsługi tras
Programy obsługi tras to metody, które są wykonywane, gdy trasa jest zgodna. Programy obsługi tras mogą być wyrażeniem lambda, funkcją lokalną, metodą wystąpienia lub metodą statyczną. Programy obsługi tras mogą być synchroniczne lub asynchroniczne.
Wyrażenie lambda
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();
Funkcja lokalna
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Metoda wystąpienia
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";
}
}
Metoda statyczna
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";
}
}
Punkt końcowy zdefiniowany poza Program.cs
Minimalne interfejsy API nie muszą znajdować się w lokalizacji 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" });
});
}
}
Zobacz również Grupy tras w dalszej części tego artykułu.
Nazwane punkty końcowe i generowanie linków
Punkty końcowe mogą mieć nazwy w celu wygenerowania adresów URL do punktu końcowego. Użycie nazwanego punktu końcowego pozwala uniknąć konieczności stosowania twardych ścieżek kodu w aplikacji:
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();
Powyższy kod jest wyświetlany The link to the hello route is /hello z punktu końcowego / .
UWAGA: W nazwach punktów końcowych jest rozróżniana wielkość liter.
Nazwy punktów końcowych:
- Musi ona być unikatowa w skali globalnej.
- Są używane jako identyfikator operacji interfejsu OpenAPI, gdy jest włączona obsługa interfejsu OpenAPI. Aby uzyskać więcej informacji, zobacz OpenAPI.
Parametry trasy
Parametry trasy można przechwycić w ramach definicji wzorca trasy:
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();
Powyższy kod zwraca The user id is 3 and book id is 7 z identyfikatora URI /users/3/books/7.
Procedura obsługi tras może zadeklarować parametry do przechwycenia. Po wysłaniu żądania do trasy z zadeklarowanymi parametrami do przechwycenia parametry są analizowane i przekazywane do programu obsługi. Ułatwia to przechwytywanie wartości w bezpieczny sposób typu. W poprzednim kodzie userId i bookId mają wartość int.
W poprzednim kodzie, jeśli nie można przekonwertować żadnej wartości trasy na intwartość , zgłaszany jest wyjątek. Żądanie /users/hello/books/3 GET zgłasza następujący wyjątek:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Symbol wieloznaczny i przechwyć wszystkie trasy
Następujące przechwycenie wszystkich tras zwracanych Routing to hello z punktu końcowego "/posts/hello":
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Ograniczenia trasy
Ograniczenia trasy ograniczają zgodne zachowanie trasy.
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();
W poniższej tabeli przedstawiono powyższe szablony tras i ich zachowanie:
| Szablon trasy | Przykładowy pasujący identyfikator URI |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Aby uzyskać więcej informacji, zobacz Route constraint reference in Routing in ASP.NET Core (Dokumentacja ograniczeń tras w usłudze Routing w usłudze ASP.NET Core).
Grupy tras
Metoda MapGroup rozszerzenia ułatwia organizowanie grup punktów końcowych za pomocą wspólnego prefiksu. Zmniejsza powtarzalny kod i umożliwia dostosowywanie całych grup punktów końcowych za pomocą jednego wywołania metod, takich jak RequireAuthorization i WithMetadata które dodają metadane punktu końcowego.
Na przykład poniższy kod tworzy dwie podobne grupy punktów końcowych:
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;
}
W tym scenariuszu możesz użyć względnego adresu nagłówka Location w 201 Created wyniku:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Pierwsza grupa punktów końcowych będzie pasowała tylko do żądań z prefiksem /public/todos i jest dostępna bez żadnego uwierzytelniania. Druga grupa punktów końcowych będzie pasowała tylko do żądań poprzedzonych prefiksem /private/todos i wymaga uwierzytelniania.
QueryPrivateTodos
Fabryka filtrów punktów końcowych to funkcja lokalna, która modyfikuje parametry programu obsługi TodoDb tras, aby umożliwić dostęp do prywatnych danych zadań do wykonania i przechowywanie ich.
Grupy tras obsługują również grupy zagnieżdżone i złożone wzorce prefiksów z parametrami i ograniczeniami trasy. W poniższym przykładzie program obsługi tras zamapowany na grupę user może przechwytywać {org} parametry trasy i {group} zdefiniowane w prefiksach grup zewnętrznych.
Prefiks może być również pusty. Może to być przydatne w przypadku dodawania metadanych lub filtrów punktu końcowego do grupy punktów końcowych bez zmiany wzorca trasy.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Dodanie filtrów lub metadanych do grupy działa tak samo jak dodawanie ich indywidualnie do każdego punktu końcowego przed dodaniem dodatkowych filtrów lub metadanych, które mogły zostać dodane do grupy wewnętrznej lub określonego punktu końcowego.
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);
});
W powyższym przykładzie zewnętrzny filtr będzie rejestrować żądanie przychodzące przed filtrem wewnętrznym, mimo że został dodany w sekundzie. Ponieważ filtry zostały zastosowane do różnych grup, kolejność, którą zostały dodane względem siebie, nie ma znaczenia. Filtry kolejności są dodawane niezależnie od tego, czy są stosowane do tej samej grupy lub określonego punktu końcowego.
Żądanie, aby zarejestrować /outer/inner/ następujące elementy:
/outer group filter
/inner group filter
MapGet filter
Powiązanie parametrów
Powiązanie parametrów to proces konwertowania danych żądania na silnie typizowane parametry, które są wyrażane przez programy obsługi tras. Źródło powiązania określa, skąd są powiązane parametry. Źródła powiązań mogą być jawne lub wnioskowane na podstawie metody HTTP i typu parametru.
Obsługiwane źródła powiązań:
- Wartości tras
- Ciąg zapytania
- Header
- Treść (jako kod JSON)
- Wartości formularza
- Usługi udostępniane przez wstrzykiwanie zależności
- Custom
Poniższa procedura obsługi tras GET używa niektórych z tych źródeł powiązań parametrów:
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 { }
Kluczowe funkcje powiązania parametrów
-
Powiązanie jawne: użyj atrybutów, takich jak
[FromRoute], ,[FromQuery][FromHeader][FromBody], ,[FromForm]i[FromServices], aby jawnie określić źródła powiązań. -
Powiązanie formularza: powiązanie wartości formularza za pomocą atrybutu
[FromForm], w tym wsparcie dlaIFormFileiIFormFileCollectionprzy przesyłaniu plików. - Typy złożone: Łącz z kolekcjami i złożonymi typami z formularzy, stringów zapytania i nagłówków.
-
Powiązanie niestandardowe: Zaimplementuj niestandardową logikę powiązania przy użyciu
TryParse,BindAsynclub interfejsuIBindableFromHttpContext<T>. - Parametry opcjonalne: obsługują typy dopuszczane do wartości null i wartości domyślne dla parametrów opcjonalnych.
- Wstrzykiwanie zależności: parametry są automatycznie powiązane z usługami zarejestrowanymi w kontenerze DI.
-
Typy specjalne: automatyczne powiązanie dla
HttpContext, ,HttpRequest,HttpResponseCancellationToken,ClaimsPrincipal,StreamiPipeReader.
Dowiedz się więcej: Aby uzyskać szczegółowe informacje na temat powiązania parametrów, w tym zaawansowanych scenariuszy, walidacji, pierwszeństwa powiązania i rozwiązywania problemów, zobacz Powiązanie parametrów w minimalnych aplikacjach interfejsu API.
Proces deserializacji przy użyciu Json+PipeReader w minimalnych API
Począwszy od platformy .NET 10, następujące obszary funkcjonalne ASP.NET Core używają przeciążeń JsonSerializer.DeserializeAsync opartych na PipeReader zamiast Stream:
- Minimalne interfejsy API (powiązanie parametrów, treść żądania odczytu)
- MVC (formatery danych wejściowych, model)
- Metody rozszerzeń HttpRequestJsonExtensions do odczytywania ciała żądania w formacie JSON.
W przypadku większości aplikacji przejście z użycia Stream do PipeReader zapewnia lepszą wydajność bez konieczności wprowadzania zmian w kodzie aplikacji. Jeśli jednak aplikacja ma konwerter niestandardowy, konwerter może nie obsługiwać Utf8JsonReader.HasValueSequence poprawnie. Jeśli tak nie jest, wynikiem mogą być błędy, takie jak ArgumentOutOfRangeException lub brakujące dane podczas deserializacji. Masz następujące opcje, aby Twój konwerter działał bez błędów związanych z PipeReader.
Opcja 1. Tymczasowe obejście
Szybkie obejście to powrót do korzystania z Stream bez obsługi PipeReader. Aby zaimplementować tę opcję, ustaw przełącznik "Microsoft.AspNetCore.UseStreamBasedJsonParsing" AppContext na wartość "true". Zalecamy wykonanie tej czynności tylko jako tymczasowe rozwiązanie, a następnie zaktualizowanie konwertera, aby obsługiwał HasValueSequence tak szybko, jak to możliwe. Przełącznik może zostać usunięty na platformie .NET 11. Jedynym celem było zapewnienie deweloperom czasu na zaktualizowanie konwerterów.
Opcja 2. Szybka poprawka implementacji JsonConverter
W przypadku tej poprawki należy przydzielić tablicę z obiektu ReadOnlySequence. W tym przykładzie pokazano, jak wygląda kod:
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
// previous code
}
Opcja 3. Bardziej skomplikowana, ale lepsza wydajność poprawki
Ta poprawka polega na skonfigurowaniu oddzielnej ścieżki kodu dla obsługi ReadOnlySequence.
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.HasValueSequence)
{
reader.ValueSequence;
// ReadOnlySequence optimized path
}
else
{
reader.ValueSpan;
// ReadOnlySpan optimized path
}
}
Aby uzyskać więcej informacji, zobacz
Obsługa walidacji w minimalnych interfejsach API
Włączenie walidacji umożliwia środowisku uruchomieniowemu ASP.NET Core wykonywanie walidacji zdefiniowanych w następujących elementach:
- Query
- Header
- Ciało żądania
Walidacje są definiowane przy użyciu atrybutów w DataAnnotations przestrzeni nazw.
Gdy parametr do minimalnego punktu końcowego interfejsu API jest klasą lub typem rekordu, atrybuty weryfikacji są stosowane automatycznie. Przykład:
public record Product(
[Required] string Name,
[Range(1, 1000)] int Quantity);
Deweloperzy dostosują zachowanie systemu weryfikacji, wykonując następujące działania:
- Tworzenie implementacji atrybutów niestandardowych
[Validation]. - Implementowanie interfejsu dla złożonej
IValidatableObjectlogiki walidacji.
Jeśli walidacja nie powiedzie się, środowisko uruchomieniowe zwraca odpowiedź 400 — nieprawidłowe żądanie ze szczegółami błędów walidacji.
Włączanie wbudowanej obsługi walidacji dla minimalnych interfejsów API
Włącz wbudowaną obsługę walidacji dla minimalnych interfejsów API, wywołując metodę AddValidation rozszerzenia w celu zarejestrowania wymaganych usług w kontenerze usługi dla aplikacji:
builder.Services.AddValidation();
Implementacja automatycznie odnajduje typy zdefiniowane w minimalnych programach obsługi interfejsu API lub jako podstawowe typy typów zdefiniowanych w minimalnych programach obsługi interfejsu API. Filtr punktu końcowego przeprowadza walidację tych typów i jest dodawany dla każdego punktu końcowego.
Walidację można wyłączyć dla określonych punktów końcowych przy użyciu DisableValidation metody rozszerzenia, jak w poniższym przykładzie:
app.MapPost("/products",
([EvenNumber(ErrorMessage = "Product ID must be even")] int productId, [Required] string name)
=> TypedResults.Ok(productId))
.DisableValidation();
Dostosowywanie odpowiedzi na błędy walidacji przy użyciu usługi IProblemDetailsService
Dostosowywanie odpowiedzi na błędy z poziomu logiki minimalnej weryfikacji interfejsu API przy użyciu IProblemDetailsService implementacji. Zarejestruj tę usługę w kolekcji usług aplikacji, aby umożliwić bardziej spójne i specyficzne dla użytkownika odpowiedzi na błędy. Obsługa minimalnej weryfikacji interfejsu API została wprowadzona w programie ASP.NET Core na platformie .NET 10.
Aby zaimplementować niestandardowe odpowiedzi na błędy walidacji:
- Implementowanie IProblemDetailsService lub używanie implementacji domyślnej
- Rejestrowanie usługi w kontenerze DI
- System sprawdzania poprawności automatycznie używa zarejestrowanej usługi do formatowania odpowiedzi na błędy weryfikacji
Aby uzyskać więcej informacji na temat dostosowywania odpowiedzi na błędy walidacji za pomocą usługi IProblemDetailsService, zobacz Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API.
Responses
Programy obsługi tras obsługują następujące typy zwracanych wartości:
-
IResultoparte — obejmujeTask<IResult>to iValueTask<IResult> -
string- Obejmuje toTask<string>iValueTask<string> -
T(Dowolny inny typ) — obejmujeTask<T>to iValueTask<T>
| Wartość zwracana | Behavior | Content-Type |
|---|---|---|
IResult |
Struktura wywołuje metodę IResult.ExecuteAsync | Decyzja o wdrożeniu IResult |
string |
Struktura zapisuje ciąg bezpośrednio w odpowiedzi | text/plain |
T (Dowolny inny typ) |
Struktura JSON serializuje odpowiedź | application/json |
Aby uzyskać bardziej szczegółowy przewodnik po zwracaniu wartości procedury obsługi tras, zobacz Tworzenie odpowiedzi w aplikacjach interfejsu API w minimalnej liczbie
Przykładowe wartości zwracane
ciąg zwracane wartości
app.MapGet("/hello", () => "Hello World");
Wartości zwracane w formacie JSON
app.MapGet("/hello", () => new { Message = "Hello World" });
Zwracane wartości TypedResults
Poniższy kod zwraca element TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Zwracanie jest preferowane TypedResults do zwracania Resultswartości . Aby uzyskać więcej informacji, zobacz TypedResults vs Results (TypdResults a wyniki).
Zwracane wartości IResult
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
W poniższym przykładzie użyto wbudowanych typów wyników, aby dostosować odpowiedź:
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" }));
Niestandardowy kod stanu
app.MapGet("/405", () => Results.StatusCode(405));
Tekst
app.MapGet("/text", () => Results.Text("This is some text"));
Stream
var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
Aby uzyskać więcej przykładów, zobacz Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Wbudowane wyniki
Typowe pomocniki wyników istnieją w Results klasach statycznych i .TypedResults Zwracanie jest preferowane TypedResults do zwracania Resultswartości . Aby uzyskać więcej informacji, zobacz TypedResults vs Results (TypdResults a wyniki).
Modyfikowanie nagłówków
Użyj obiektu HttpResponse, aby zmodyfikować nagłówki odpowiedzi:
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";
});
Dostosowywanie wyników
Aplikacje mogą kontrolować odpowiedzi, implementując typ niestandardowy IResult . Poniższy kod jest przykładem typu wyniku HTML:
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);
}
}
Zalecamy dodanie metody rozszerzenia w celu Microsoft.AspNetCore.Http.IResultExtensions zwiększenia możliwości odnajdywania tych niestandardowych wyników.
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();
Wpisane wyniki
Interfejs IResult może reprezentować wartości zwracane z minimalnych interfejsów API, które nie korzystają z niejawnej obsługi serializowania zwracanego obiektu do odpowiedzi HTTP.
Statyczna klasa Results służy do tworzenia różnych IResult obiektów reprezentujących różne typy odpowiedzi. Na przykład ustawienie kodu stanu odpowiedzi lub przekierowanie do innego adresu URL.
Implementowane IResult typy są publiczne, co umożliwia asercji typów podczas testowania. Przykład:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Możesz przyjrzeć się typom zwracanych odpowiednich metod w statycznej klasie TypedResults , aby znaleźć prawidłowy typ publiczny IResult do rzutowania.
Aby uzyskać więcej przykładów, zobacz Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API.
Filters
Aby uzyskać więcej informacji, zobacz Filtry w minimalnych aplikacjach interfejsu API.
Authorization
Trasy mogą być chronione przy użyciu zasad autoryzacji. Można je zadeklarować za pomocą atrybutu [Authorize] lub przy użyciu RequireAuthorization metody :
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();
Powyższy kod można napisać za pomocą RequireAuthorizationpolecenia :
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
W poniższym przykładzie użyto autoryzacji opartej na zasadach:
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();
Zezwalanie nieuwierzytelnionym użytkownikom na dostęp do punktu końcowego
Ustawienie [AllowAnonymous] umożliwia nieuwierzytelnionym użytkownikom dostęp do punktów końcowych:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Trasy mogą być włączone przez mechanizm CORS przy użyciu zasad MECHANIZMU CORS. Mechanizm CORS można zadeklarować za pomocą atrybutu [EnableCors] lub przy użyciu RequireCors metody . Następujące przykłady umożliwiają mechanizm 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();
Aby uzyskać więcej informacji, zobacz Włączanie żądań między źródłami (CORS) w usłudze ASP.NET Core
ValidateScopes i ValidateOnBuild
ValidateScopes i ValidateOnBuild są domyślnie włączone w środowisku deweloperów , ale wyłączone w innych środowiskach.
Gdy ValidateOnBuild parametr to true, kontener DI weryfikuje konfigurację usługi w czasie kompilacji. Jeśli konfiguracja usługi jest nieprawidłowa, kompilacja kończy się niepowodzeniem podczas uruchamiania aplikacji, a nie w czasie wykonywania żądania usługi.
Gdy ValidateScopes parametr to true, kontener DI sprawdza, czy usługa o określonym zakresie nie jest rozpoznawana z zakresu głównego. Rozwiązanie usługi o określonym zakresie z zakresu głównego może spowodować wyciek pamięci, ponieważ usługa jest przechowywana w pamięci dłużej niż zakres żądania.
ValidateScopes wartości i ValidateOnBuild są domyślnie fałszywe w trybach innych niż Programowanie ze względu na wydajność.
Poniższy kod pokazuje ValidateScopes , że jest domyślnie włączony w trybie programowania, ale wyłączony w trybie wydania:
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 { }
Poniższy kod pokazuje ValidateOnBuild , że jest domyślnie włączony w trybie programowania, ale wyłączony w trybie wydania:
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 { }
Następujący kod jest ValidateScopes wyłączany i ValidateOnBuild w pliku 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;
});
}
Zobacz także
- Krótkie informacje o minimalnych interfejsach API
- Generowanie dokumentów OpenAPI
- Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API
- Filtry w minimalnych aplikacjach interfejsu API
- Obsługa błędów w interfejsach API platformy ASP.NET Core
- Uwierzytelnianie i autoryzacja w minimalnych interfejsach API
- Testowanie minimalnych aplikacji interfejsu API
- Routing zwariowy
- Identity Punkty końcowe interfejsu API
- Obsługa kontenera wstrzykiwania zależności usługi kluczy
- Spojrzenie za kulisami minimalnych punktów końcowych interfejsu API
- Organizowanie minimalnych interfejsów API ASP.NET Core
- Płynna dyskusja dotycząca walidacji w usłudze GitHub
Ten dokument:
- Zawiera skróconą dokumentację dla minimalnych interfejsów API.
- Jest przeznaczony dla doświadczonych deweloperów. Aby zapoznać się z wprowadzeniem, zobacz Samouczek: tworzenie minimalnego interfejsu API przy użyciu platformy ASP.NET Core.
Minimalne interfejsy API składają się z następujących elementów:
WebApplication
Następujący kod jest generowany przez szablon ASP.NET Core:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Powyższy kod można utworzyć za pomocą wiersza dotnet new web polecenia lub wybrać pusty szablon sieci Web w programie Visual Studio.
Poniższy kod tworzy element WebApplication (app) bez jawnego utworzenia elementu WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create Inicjuje nowe wystąpienie WebApplication klasy ze wstępnie skonfigurowanymi wartościami domyślnymi.
WebApplication program automatycznie dodaje następujące oprogramowanie pośredniczące w aplikacjach interfejsu API w zależności od określonych warunków:
-
UseDeveloperExceptionPageelement jest dodawany jako pierwszy, gdy parametr ma wartośćHostingEnvironment"Development". -
UseRoutingJest dodawany drugi, jeśli kod użytkownika nie został jeszcze wywołanyUseRoutingi jeśli istnieją skonfigurowane punkty końcowe, na przykładapp.MapGet. -
UseEndpointsJest dodawany na końcu potoku oprogramowania pośredniczącego, jeśli są skonfigurowane jakiekolwiek punkty końcowe. -
UseAuthenticationjest dodawany natychmiast poUseRoutingtym, jak kod użytkownika nie został jeszcze wywołanyUseAuthenticationi czyIAuthenticationSchemeProvidermożna go wykryć u dostawcy usług.IAuthenticationSchemeProviderprogram jest domyślnie dodawany podczas korzystania z usługAddAuthentication, a usługa jest wykrywana przy użyciu poleceniaIServiceProviderIsService. -
UseAuthorizationZostanie dodany dalej, jeśli kod użytkownika nie został jeszcze wywołanyUseAuthorizationi czyIAuthorizationHandlerProvidermożna go wykryć u dostawcy usług.IAuthorizationHandlerProviderprogram jest domyślnie dodawany podczas korzystania z usługAddAuthorization, a usługa jest wykrywana przy użyciu poleceniaIServiceProviderIsService. - Oprogramowanie pośredniczące skonfigurowane przez użytkownika i punkty końcowe są dodawane między elementami
UseRoutingiUseEndpoints.
Poniższy kod jest w rzeczywistości tym, co tworzy automatyczne oprogramowanie pośredniczące dodawane do aplikacji:
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 => {});
W niektórych przypadkach domyślna konfiguracja oprogramowania pośredniczącego nie jest poprawna dla aplikacji i wymaga modyfikacji. Na przykład UseCors należy wywołać metodę przed UseAuthentication i UseAuthorization. Aplikacja musi wywołać metodę UseAuthentication , a UseAuthorization jeśli UseCors jest wywoływana:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Jeśli oprogramowanie pośredniczące powinno być uruchamiane przed rozpoczęciem dopasowywania tras, UseRouting należy wywołać metodę , a oprogramowanie pośredniczące powinno zostać umieszczone przed wywołaniem metody UseRouting.
UseEndpoints nie jest wymagany w tym przypadku, ponieważ jest automatycznie dodawany zgodnie z wcześniejszym opisem:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Podczas dodawania oprogramowania pośredniczącego terminalu:
- Oprogramowanie pośredniczące musi zostać dodane po .
UseEndpoints - Aplikacja musi wywołać metodę
UseRoutingiUseEndpointstak, aby oprogramowanie pośredniczące terminalu można było umieścić w odpowiedniej lokalizacji.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Oprogramowanie pośredniczące terminala to oprogramowanie pośredniczące uruchamiane, jeśli żaden punkt końcowy nie obsługuje żądania.
Praca z portami
Po utworzeniu aplikacji internetowej za pomocą programu Visual Studio lub dotnet newProperties/launchSettings.json zostanie utworzony plik, który określa porty, na które odpowiada aplikacja. W poniższych przykładach ustawień portów uruchomienie aplikacji z programu Visual Studio zwraca okno dialogowe Unable to connect to web server 'AppName'błędu . Program Visual Studio zwraca błąd, ponieważ oczekuje portu określonego w Properties/launchSettings.jsonelemecie , ale aplikacja używa portu określonego przez app.Run("http://localhost:3000"). Uruchom następujący port, zmieniając przykłady z wiersza polecenia.
W poniższych sekcjach ustawiono port, na który odpowiada aplikacja.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
W poprzednim kodzie aplikacja odpowiada na port 3000.
Wiele portów
W poniższym kodzie aplikacja odpowiada na port 3000 i 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Ustawianie portu z wiersza polecenia
Następujące polecenie powoduje, że aplikacja odpowiada na port 7777:
dotnet run --urls="https://localhost:7777"
Kestrel Jeśli punkt końcowy jest również skonfigurowany w appsettings.json pliku, appsettings.json używany jest określony adres URL. Aby uzyskać więcej informacji, zobacz Kestrel Konfiguracja punktu końcowego
Odczytywanie portu ze środowiska
Poniższy kod odczytuje port ze środowiska:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Preferowanym sposobem ustawienia portu ze środowiska jest użycie ASPNETCORE_URLS zmiennej środowiskowej, która jest pokazana w poniższej sekcji.
Ustawianie portów za pomocą zmiennej środowiskowej ASPNETCORE_URLS
Zmienna ASPNETCORE_URLS środowiskowa jest dostępna do ustawienia portu:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS obsługuje wiele adresów URL:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Nasłuchiwanie we wszystkich interfejsach
W poniższych przykładach pokazano nasłuchiwanie we wszystkich interfejsach
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();
Nasłuchiwanie we wszystkich interfejsach przy użyciu ASPNETCORE_URLS
Powyższe przykłady mogą być używane ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Nasłuchiwanie we wszystkich interfejsach przy użyciu ASPNETCORE_HTTPS_PORTS
Powyższe przykłady mogą używać elementów ASPNETCORE_HTTPS_PORTS i ASPNETCORE_HTTP_PORTS.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Aby uzyskać więcej informacji, zobacz Konfigurowanie punktów końcowych dla serwera internetowego platformy ASP.NET Core Kestrel
Określanie protokołu HTTPS przy użyciu certyfikatu programistycznego
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Aby uzyskać więcej informacji na temat certyfikatu programistycznego, zobacz Trust the ASP.NET Core HTTPS development certificate on Windows and macOS (Ufaj certyfikatowi programistycznemu ASP.NET Core HTTPS w systemach Windows i macOS).
Określanie protokołu HTTPS przy użyciu certyfikatu niestandardowego
W poniższych sekcjach pokazano, jak określić certyfikat niestandardowy przy użyciu appsettings.json pliku i za pośrednictwem konfiguracji.
Określanie certyfikatu niestandardowego za pomocą polecenia appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Określanie certyfikatu niestandardowego za pomocą konfiguracji
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();
Korzystanie z interfejsów API certyfikatów
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();
Odczytywanie środowiska
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();
Aby uzyskać więcej informacji na temat korzystania ze środowiska, zobacz ASP.NET Core runtime environments (Środowiska uruchomieniowe ASP.NET Core)
Configuration
Poniższy kod odczytuje z systemu konfiguracji:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Aby uzyskać więcej informacji, zobacz Configuration in ASP.NET Core (Konfiguracja w programie ASP.NET Core)
Logging
Poniższy kod zapisuje komunikat podczas uruchamiania aplikacji logowania:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Aby uzyskać więcej informacji, zobacz Rejestrowanie na platformie .NET i ASP.NET Core
Uzyskiwanie dostępu do kontenera wstrzykiwania zależności (DI)
Poniższy kod pokazuje, jak pobrać usługi z kontenera DI podczas uruchamiania aplikacji:
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();
Poniższy kod pokazuje, jak uzyskać dostęp do kluczy z kontenera DI przy użyciu atrybutu [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.";
}
Aby uzyskać więcej informacji na temat di, zobacz Wstrzykiwanie zależności w ASP.NET Core.
WebApplicationBuilder
Ta sekcja zawiera przykładowy kod przy użyciu polecenia WebApplicationBuilder.
Zmienianie katalogu głównego zawartości, nazwy aplikacji i środowiska
Poniższy kod ustawia katalog główny zawartości, nazwę aplikacji i środowisko:
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 inicjuje nowe wystąpienie klasy WebApplicationBuilder ze wstępnie skonfigurowanymi wartościami domyślnymi.
Aby uzyskać więcej informacji, zobacz omówienie podstaw platformy ASP.NET Core
Zmienianie katalogu głównego zawartości, nazwy aplikacji i środowiska przy użyciu zmiennych środowiskowych lub wiersza polecenia
W poniższej tabeli przedstawiono zmienną środowiskową i argument wiersza polecenia używany do zmiany katalogu głównego zawartości, nazwy aplikacji i środowiska:
| cecha | Zmienna środowiskowa | Argument wiersza polecenia |
|---|---|---|
| Nazwa aplikacji | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Nazwa środowiska | ASPNETCORE_ENVIRONMENT | --environment |
| Katalog główny zawartości | ASPNETCORE_CONTENTROOT | --contentRoot |
Dodawanie dostawców konfiguracji
Poniższy przykład dodaje dostawcę konfiguracji INI:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Aby uzyskać szczegółowe informacje, zobacz Dostawcy konfiguracji plików w konfiguracji w programie ASP.NET Core.
Konfiguracja odczytu
Domyślnie WebApplicationBuilder konfiguracja odczytu z wielu źródeł, w tym:
-
appSettings.jsoniappSettings.{environment}.json - Zmienne środowiskowe
- Wiersz polecenia
Aby uzyskać pełną listę źródeł konfiguracji, zobacz Konfiguracja domyślna w konfiguracji w programie ASP.NET Core.
Poniższy kod odczytuje HelloKey z konfiguracji i wyświetla wartość w punkcie / końcowym. Jeśli wartość konfiguracji ma wartość null, "Hello" zostanie przypisana do elementu message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Odczytywanie środowiska
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine($"Running in development.");
}
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Dodawanie dostawców rejestrowania
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();
Dodawanie usług
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();
Dostosowywanie elementu IHostBuilder
Dostęp do istniejących metod rozszerzeń IHostBuilder można uzyskać przy użyciu właściwości 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();
Dostosowywanie obiektu IWebHostBuilder
Dostęp do metod rozszerzeń IWebHostBuilder można uzyskać przy użyciu właściwości 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();
Zmienianie katalogu głównego sieci Web
Domyślnie katalog główny sieci Web jest powiązany z katalogem głównym zawartości w folderze wwwroot . Katalog główny sieci Web to miejsce, w którym oprogramowanie pośredniczące plików statycznych szuka plików statycznych. Katalog główny sieci Web można zmienić za pomocą WebHostOptionspolecenia , wiersza polecenia lub UseWebRoot metody :
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Niestandardowy kontener wstrzykiwania zależności (DI)
W poniższym przykładzie użyto funkcji 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();
Dodawanie oprogramowania pośredniczącego
W programie WebApplicationmożna skonfigurować dowolne istniejące oprogramowanie pośredniczące ASP.NET Core:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Aby uzyskać więcej informacji, zobacz ASP.NET Core Middleware
Strona wyjątku dla deweloperów
WebApplication.CreateBuilder Inicjuje nowe wystąpienie WebApplicationBuilder klasy ze wstępnie skonfigurowanymi wartościami domyślnymi. Strona wyjątku dewelopera jest włączona w wstępnie skonfigurowanych wartościach domyślnych. Po uruchomieniu następującego kodu w środowisku deweloperskim przejście do / strony renderuje przyjazną stronę, która pokazuje wyjątek.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () =>
{
throw new InvalidOperationException("Oops, the '/' route has thrown an exception.");
});
app.Run();
Oprogramowanie pośredniczące platformy ASP.NET Core
W poniższej tabeli wymieniono niektóre oprogramowanie pośredniczące często używane z minimalnymi interfejsami API.
| Middleware | Description | API |
|---|---|---|
| Authentication | Zapewnia obsługę uwierzytelniania. | UseAuthentication |
| Authorization | Zapewnia obsługę autoryzacji. | UseAuthorization |
| CORS | Konfiguruje współużytkowanie zasobów między źródłami. | UseCors |
| Procedura obsługi wyjątków | Globalnie obsługuje wyjątki zgłaszane przez potok oprogramowania pośredniczącego. | UseExceptionHandler |
| Przekierowane nagłówki | Przekazuje nagłówki przesłane przez serwer proxy do bieżącego żądania. | UseForwardedHeaders |
| Przekierowywanie HTTPS | Przekierowuje wszystkie żądania HTTP do protokołu HTTPS. | UseHttpsRedirection |
| Http Strict Transport Security (HSTS) | Oprogramowanie pośredniczące rozszerzenia zabezpieczeń, które dodaje specjalny nagłówek odpowiedzi. | UseHsts |
| Rejestrowanie żądań | Zapewnia obsługę rejestrowania żądań HTTP i odpowiedzi. | UseHttpLogging |
| Limity czasu żądania | Zapewnia obsługę konfigurowania limitów czasu żądań, wartości domyślnych globalnych i poszczególnych punktów końcowych. | UseRequestTimeouts |
| Rejestrowanie żądań W3C | Zapewnia obsługę rejestrowania żądań HTTP i odpowiedzi w formacie W3C. | UseW3CLogging |
| Buforowanie odpowiedzi | Zapewnia obsługę buforowania odpowiedzi. | UseResponseCaching |
| Kompresja odpowiedzi | Zapewnia obsługę kompresowania odpowiedzi. | UseResponseCompression |
| Session | Zapewnia obsługę zarządzania sesjami użytkowników. | UseSession |
| Pliki statyczne | Zapewnia obsługę plików statycznych i przeglądania katalogów. | UseStaticFiles, UseFileServer |
| WebSockets | Włącza protokoły WebSocket. | UseWebSockets |
W poniższych sekcjach omówiono obsługę żądań: routing, powiązanie parametrów i odpowiedzi.
Routing
Skonfigurowana WebApplication obsługuje Map{Verb} metodę MapMethods{Verb} HTTP typu camel-cased, npGet. , PostPut lub 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();
Argumenty Delegate przekazywane do tych metod są nazywane "procedurami obsługi tras".
Programy obsługi tras
Programy obsługi tras to metody, które są wykonywane, gdy trasa jest zgodna. Programy obsługi tras mogą być wyrażeniem lambda, funkcją lokalną, metodą wystąpienia lub metodą statyczną. Programy obsługi tras mogą być synchroniczne lub asynchroniczne.
Wyrażenie lambda
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();
Funkcja lokalna
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Metoda wystąpienia
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";
}
}
Metoda statyczna
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";
}
}
Punkt końcowy zdefiniowany poza Program.cs
Minimalne interfejsy API nie muszą znajdować się w lokalizacji 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" });
});
}
}
Zobacz również Grupy tras w dalszej części tego artykułu.
Nazwane punkty końcowe i generowanie linków
Punkty końcowe mogą mieć nazwy w celu wygenerowania adresów URL do punktu końcowego. Użycie nazwanego punktu końcowego pozwala uniknąć konieczności stosowania twardych ścieżek kodu w aplikacji:
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();
Powyższy kod jest wyświetlany The link to the hello route is /hello z punktu końcowego / .
UWAGA: W nazwach punktów końcowych jest rozróżniana wielkość liter.
Nazwy punktów końcowych:
- Musi ona być unikatowa w skali globalnej.
- Są używane jako identyfikator operacji interfejsu OpenAPI, gdy jest włączona obsługa interfejsu OpenAPI. Aby uzyskać więcej informacji, zobacz OpenAPI.
Parametry trasy
Parametry trasy można przechwycić w ramach definicji wzorca trasy:
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();
Powyższy kod zwraca The user id is 3 and book id is 7 z identyfikatora URI /users/3/books/7.
Procedura obsługi tras może zadeklarować parametry do przechwycenia. Po wysłaniu żądania do trasy z zadeklarowanymi parametrami do przechwycenia parametry są analizowane i przekazywane do programu obsługi. Ułatwia to przechwytywanie wartości w bezpieczny sposób typu. W poprzednim kodzie userId i bookId mają wartość int.
W poprzednim kodzie, jeśli nie można przekonwertować żadnej wartości trasy na intwartość , zgłaszany jest wyjątek. Żądanie /users/hello/books/3 GET zgłasza następujący wyjątek:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Symbol wieloznaczny i przechwyć wszystkie trasy
Następujące przechwycenie wszystkich tras zwracanych Routing to hello z punktu końcowego "/posts/hello":
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Ograniczenia trasy
Ograniczenia trasy ograniczają zgodne zachowanie trasy.
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();
W poniższej tabeli przedstawiono powyższe szablony tras i ich zachowanie:
| Szablon trasy | Przykładowy pasujący identyfikator URI |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Aby uzyskać więcej informacji, zobacz Route constraint reference in Routing in ASP.NET Core (Dokumentacja ograniczeń tras w usłudze Routing w usłudze ASP.NET Core).
Grupy tras
Metoda MapGroup rozszerzenia ułatwia organizowanie grup punktów końcowych za pomocą wspólnego prefiksu. Zmniejsza powtarzalny kod i umożliwia dostosowywanie całych grup punktów końcowych za pomocą jednego wywołania metod, takich jak RequireAuthorization i WithMetadata które dodają metadane punktu końcowego.
Na przykład poniższy kod tworzy dwie podobne grupy punktów końcowych:
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;
}
W tym scenariuszu możesz użyć względnego adresu nagłówka Location w 201 Created wyniku:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Pierwsza grupa punktów końcowych będzie pasowała tylko do żądań z prefiksem /public/todos i jest dostępna bez żadnego uwierzytelniania. Druga grupa punktów końcowych będzie pasowała tylko do żądań poprzedzonych prefiksem /private/todos i wymaga uwierzytelniania.
QueryPrivateTodos
Fabryka filtrów punktów końcowych to funkcja lokalna, która modyfikuje parametry programu obsługi TodoDb tras, aby umożliwić dostęp do prywatnych danych zadań do wykonania i przechowywanie ich.
Grupy tras obsługują również grupy zagnieżdżone i złożone wzorce prefiksów z parametrami i ograniczeniami trasy. W poniższym przykładzie program obsługi tras zamapowany na grupę user może przechwytywać {org} parametry trasy i {group} zdefiniowane w prefiksach grup zewnętrznych.
Prefiks może być również pusty. Może to być przydatne w przypadku dodawania metadanych lub filtrów punktu końcowego do grupy punktów końcowych bez zmiany wzorca trasy.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Dodanie filtrów lub metadanych do grupy działa tak samo jak dodawanie ich indywidualnie do każdego punktu końcowego przed dodaniem dodatkowych filtrów lub metadanych, które mogły zostać dodane do grupy wewnętrznej lub określonego punktu końcowego.
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);
});
W powyższym przykładzie zewnętrzny filtr będzie rejestrować żądanie przychodzące przed filtrem wewnętrznym, mimo że został dodany w sekundzie. Ponieważ filtry zostały zastosowane do różnych grup, kolejność, którą zostały dodane względem siebie, nie ma znaczenia. Filtry kolejności są dodawane niezależnie od tego, czy są stosowane do tej samej grupy lub określonego punktu końcowego.
Żądanie, aby zarejestrować /outer/inner/ następujące elementy:
/outer group filter
/inner group filter
MapGet filter
Powiązanie parametrów
Powiązanie parametrów to proces konwertowania danych żądania na silnie typizowane parametry, które są wyrażane przez programy obsługi tras. Źródło powiązania określa, skąd są powiązane parametry. Źródła powiązań mogą być jawne lub wnioskowane na podstawie metody HTTP i typu parametru.
Obsługiwane źródła powiązań:
- Wartości tras
- Ciąg zapytania
- Header
- Treść (jako kod JSON)
- Wartości formularza
- Usługi udostępniane przez wstrzykiwanie zależności
- Custom
Poniższa procedura obsługi tras GET używa niektórych z tych źródeł powiązań parametrów:
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 { }
Kluczowe funkcje powiązania parametrów
-
Powiązanie jawne: użyj atrybutów, takich jak
[FromRoute], ,[FromQuery][FromHeader][FromBody], ,[FromForm]i[FromServices], aby jawnie określić źródła powiązań. -
Powiązanie formularza: powiązanie wartości formularza za pomocą atrybutu
[FromForm], w tym wsparcie dlaIFormFileiIFormFileCollectionprzy przesyłaniu plików. - Typy złożone: Łącz z kolekcjami i złożonymi typami z formularzy, stringów zapytania i nagłówków.
-
Powiązanie niestandardowe: Zaimplementuj niestandardową logikę powiązania przy użyciu
TryParse,BindAsynclub interfejsuIBindableFromHttpContext<T>. - Parametry opcjonalne: obsługują typy dopuszczane do wartości null i wartości domyślne dla parametrów opcjonalnych.
- Wstrzykiwanie zależności: parametry są automatycznie powiązane z usługami zarejestrowanymi w kontenerze DI.
-
Typy specjalne: automatyczne powiązanie dla
HttpContext, ,HttpRequest,HttpResponseCancellationToken,ClaimsPrincipal,StreamiPipeReader.
Dowiedz się więcej: Aby uzyskać szczegółowe informacje na temat powiązania parametrów, w tym zaawansowanych scenariuszy, walidacji, pierwszeństwa powiązania i rozwiązywania problemów, zobacz Powiązanie parametrów w minimalnych aplikacjach interfejsu API.
Responses
Programy obsługi tras obsługują następujące typy zwracanych wartości:
-
IResultoparte — obejmujeTask<IResult>to iValueTask<IResult> -
string- Obejmuje toTask<string>iValueTask<string> -
T(Dowolny inny typ) — obejmujeTask<T>to iValueTask<T>
| Wartość zwracana | Behavior | Content-Type |
|---|---|---|
IResult |
Struktura wywołuje metodę IResult.ExecuteAsync | Decyzja o wdrożeniu IResult |
string |
Struktura zapisuje ciąg bezpośrednio w odpowiedzi | text/plain |
T (Dowolny inny typ) |
Struktura JSON serializuje odpowiedź | application/json |
Aby uzyskać bardziej szczegółowy przewodnik po zwracaniu wartości procedury obsługi tras, zobacz Tworzenie odpowiedzi w aplikacjach interfejsu API w minimalnej liczbie
Przykładowe wartości zwracane
ciąg zwracane wartości
app.MapGet("/hello", () => "Hello World");
Wartości zwracane w formacie JSON
app.MapGet("/hello", () => new { Message = "Hello World" });
Zwracane wartości TypedResults
Poniższy kod zwraca element TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Zwracanie jest preferowane TypedResults do zwracania Resultswartości . Aby uzyskać więcej informacji, zobacz TypedResults vs Results (TypdResults a wyniki).
Zwracane wartości IResult
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
W poniższym przykładzie użyto wbudowanych typów wyników, aby dostosować odpowiedź:
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" }));
Niestandardowy kod stanu
app.MapGet("/405", () => Results.StatusCode(405));
Tekst
app.MapGet("/text", () => Results.Text("This is some text"));
Stream
var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
Aby uzyskać więcej przykładów, zobacz Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Wbudowane wyniki
Typowe pomocniki wyników istnieją w Results klasach statycznych i .TypedResults Zwracanie jest preferowane TypedResults do zwracania Resultswartości . Aby uzyskać więcej informacji, zobacz TypedResults vs Results (TypdResults a wyniki).
Modyfikowanie nagłówków
Użyj obiektu HttpResponse, aby zmodyfikować nagłówki odpowiedzi:
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";
});
Dostosowywanie wyników
Aplikacje mogą kontrolować odpowiedzi, implementując typ niestandardowy IResult . Poniższy kod jest przykładem typu wyniku HTML:
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);
}
}
Zalecamy dodanie metody rozszerzenia w celu Microsoft.AspNetCore.Http.IResultExtensions zwiększenia możliwości odnajdywania tych niestandardowych wyników.
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();
Wpisane wyniki
Interfejs IResult może reprezentować wartości zwracane z minimalnych interfejsów API, które nie korzystają z niejawnej obsługi serializowania zwracanego obiektu do odpowiedzi HTTP.
Statyczna klasa Results służy do tworzenia różnych IResult obiektów reprezentujących różne typy odpowiedzi. Na przykład ustawienie kodu stanu odpowiedzi lub przekierowanie do innego adresu URL.
Implementowane IResult typy są publiczne, co umożliwia asercji typów podczas testowania. Przykład:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Możesz przyjrzeć się typom zwracanych odpowiednich metod w statycznej klasie TypedResults , aby znaleźć prawidłowy typ publiczny IResult do rzutowania.
Aby uzyskać więcej przykładów, zobacz Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API.
Filters
Aby uzyskać więcej informacji, zobacz Filtry w minimalnych aplikacjach interfejsu API.
Authorization
Trasy mogą być chronione przy użyciu zasad autoryzacji. Można je zadeklarować za pomocą atrybutu [Authorize] lub przy użyciu RequireAuthorization metody :
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();
Powyższy kod można napisać za pomocą RequireAuthorizationpolecenia :
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
W poniższym przykładzie użyto autoryzacji opartej na zasadach:
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();
Zezwalanie nieuwierzytelnionym użytkownikom na dostęp do punktu końcowego
Ustawienie [AllowAnonymous] umożliwia nieuwierzytelnionym użytkownikom dostęp do punktów końcowych:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Trasy mogą być włączone przez mechanizm CORS przy użyciu zasad MECHANIZMU CORS. Mechanizm CORS można zadeklarować za pomocą atrybutu [EnableCors] lub przy użyciu RequireCors metody . Następujące przykłady umożliwiają mechanizm 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();
Aby uzyskać więcej informacji, zobacz Włączanie żądań między źródłami (CORS) w usłudze ASP.NET Core
ValidateScopes i ValidateOnBuild
ValidateScopes i ValidateOnBuild są domyślnie włączone w środowisku deweloperów , ale wyłączone w innych środowiskach.
Gdy ValidateOnBuild parametr to true, kontener DI weryfikuje konfigurację usługi w czasie kompilacji. Jeśli konfiguracja usługi jest nieprawidłowa, kompilacja kończy się niepowodzeniem podczas uruchamiania aplikacji, a nie w czasie wykonywania żądania usługi.
Gdy ValidateScopes parametr to true, kontener DI sprawdza, czy usługa o określonym zakresie nie jest rozpoznawana z zakresu głównego. Rozwiązanie usługi o określonym zakresie z zakresu głównego może spowodować wyciek pamięci, ponieważ usługa jest przechowywana w pamięci dłużej niż zakres żądania.
ValidateScopes wartości i ValidateOnBuild są domyślnie fałszywe w trybach innych niż Programowanie ze względu na wydajność.
Poniższy kod pokazuje ValidateScopes , że jest domyślnie włączony w trybie programowania, ale wyłączony w trybie wydania:
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 { }
Poniższy kod pokazuje ValidateOnBuild , że jest domyślnie włączony w trybie programowania, ale wyłączony w trybie wydania:
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 { }
Następujący kod jest ValidateScopes wyłączany i ValidateOnBuild w pliku 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;
});
}
Zobacz także
- Krótkie informacje o minimalnych interfejsach API
- Generowanie dokumentów OpenAPI
- Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API
- Filtry w minimalnych aplikacjach interfejsu API
- Obsługa błędów w interfejsach API platformy ASP.NET Core
- Uwierzytelnianie i autoryzacja w minimalnych interfejsach API
- Testowanie minimalnych aplikacji interfejsu API
- Routing zwariowy
- Identity Punkty końcowe interfejsu API
- Obsługa kontenera wstrzykiwania zależności usługi kluczy
- Spojrzenie za kulisami minimalnych punktów końcowych interfejsu API
- Organizowanie minimalnych interfejsów API ASP.NET Core
- Płynna dyskusja dotycząca walidacji w usłudze GitHub
Ten dokument:
- Zawiera skróconą dokumentację dla minimalnych interfejsów API.
- Jest przeznaczony dla doświadczonych deweloperów. Aby zapoznać się z wprowadzeniem, zobacz Samouczek: tworzenie minimalnego interfejsu API przy użyciu platformy ASP.NET Core.
Minimalne interfejsy API składają się z następujących elementów:
WebApplication
Następujący kod jest generowany przez szablon ASP.NET Core:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Powyższy kod można utworzyć za pomocą wiersza dotnet new web polecenia lub wybrać pusty szablon sieci Web w programie Visual Studio.
Poniższy kod tworzy element WebApplication (app) bez jawnego utworzenia elementu WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create Inicjuje nowe wystąpienie WebApplication klasy ze wstępnie skonfigurowanymi wartościami domyślnymi.
WebApplication program automatycznie dodaje następujące oprogramowanie pośredniczące w aplikacjach interfejsu API w zależności od określonych warunków:
-
UseDeveloperExceptionPageelement jest dodawany jako pierwszy, gdy parametr ma wartośćHostingEnvironment"Development". -
UseRoutingJest dodawany drugi, jeśli kod użytkownika nie został jeszcze wywołanyUseRoutingi jeśli istnieją skonfigurowane punkty końcowe, na przykładapp.MapGet. -
UseEndpointsJest dodawany na końcu potoku oprogramowania pośredniczącego, jeśli są skonfigurowane jakiekolwiek punkty końcowe. -
UseAuthenticationjest dodawany natychmiast poUseRoutingtym, jak kod użytkownika nie został jeszcze wywołanyUseAuthenticationi czyIAuthenticationSchemeProvidermożna go wykryć u dostawcy usług.IAuthenticationSchemeProviderprogram jest domyślnie dodawany podczas korzystania z usługAddAuthentication, a usługa jest wykrywana przy użyciu poleceniaIServiceProviderIsService. -
UseAuthorizationZostanie dodany dalej, jeśli kod użytkownika nie został jeszcze wywołanyUseAuthorizationi czyIAuthorizationHandlerProvidermożna go wykryć u dostawcy usług.IAuthorizationHandlerProviderprogram jest domyślnie dodawany podczas korzystania z usługAddAuthorization, a usługa jest wykrywana przy użyciu poleceniaIServiceProviderIsService. - Oprogramowanie pośredniczące skonfigurowane przez użytkownika i punkty końcowe są dodawane między elementami
UseRoutingiUseEndpoints.
Poniższy kod jest w rzeczywistości tym, co tworzy automatyczne oprogramowanie pośredniczące dodawane do aplikacji:
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 => {});
W niektórych przypadkach domyślna konfiguracja oprogramowania pośredniczącego nie jest poprawna dla aplikacji i wymaga modyfikacji. Na przykład UseCors należy wywołać metodę przed UseAuthentication i UseAuthorization. Aplikacja musi wywołać metodę UseAuthentication , a UseAuthorization jeśli UseCors jest wywoływana:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Jeśli oprogramowanie pośredniczące powinno być uruchamiane przed rozpoczęciem dopasowywania tras, UseRouting należy wywołać metodę , a oprogramowanie pośredniczące powinno zostać umieszczone przed wywołaniem metody UseRouting.
UseEndpoints nie jest wymagany w tym przypadku, ponieważ jest automatycznie dodawany zgodnie z wcześniejszym opisem:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Podczas dodawania oprogramowania pośredniczącego terminalu:
- Oprogramowanie pośredniczące musi zostać dodane po .
UseEndpoints - Aplikacja musi wywołać metodę
UseRoutingiUseEndpointstak, aby oprogramowanie pośredniczące terminalu można było umieścić w odpowiedniej lokalizacji.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Oprogramowanie pośredniczące terminala to oprogramowanie pośredniczące uruchamiane, jeśli żaden punkt końcowy nie obsługuje żądania.
Praca z portami
Po utworzeniu aplikacji internetowej za pomocą programu Visual Studio lub dotnet newProperties/launchSettings.json zostanie utworzony plik, który określa porty, na które odpowiada aplikacja. W poniższych przykładach ustawień portów uruchomienie aplikacji z programu Visual Studio zwraca okno dialogowe Unable to connect to web server 'AppName'błędu . Program Visual Studio zwraca błąd, ponieważ oczekuje portu określonego w Properties/launchSettings.jsonelemecie , ale aplikacja używa portu określonego przez app.Run("http://localhost:3000"). Uruchom następujący port, zmieniając przykłady z wiersza polecenia.
W poniższych sekcjach ustawiono port, na który odpowiada aplikacja.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
W poprzednim kodzie aplikacja odpowiada na port 3000.
Wiele portów
W poniższym kodzie aplikacja odpowiada na port 3000 i 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Ustawianie portu z wiersza polecenia
Następujące polecenie powoduje, że aplikacja odpowiada na port 7777:
dotnet run --urls="https://localhost:7777"
Kestrel Jeśli punkt końcowy jest również skonfigurowany w appsettings.json pliku, appsettings.json używany jest określony adres URL. Aby uzyskać więcej informacji, zobacz Kestrel Konfiguracja punktu końcowego
Odczytywanie portu ze środowiska
Poniższy kod odczytuje port ze środowiska:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Preferowanym sposobem ustawienia portu ze środowiska jest użycie ASPNETCORE_URLS zmiennej środowiskowej, która jest pokazana w poniższej sekcji.
Ustawianie portów za pomocą zmiennej środowiskowej ASPNETCORE_URLS
Zmienna ASPNETCORE_URLS środowiskowa jest dostępna do ustawienia portu:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS obsługuje wiele adresów URL:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Nasłuchiwanie we wszystkich interfejsach
W poniższych przykładach pokazano nasłuchiwanie we wszystkich interfejsach
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();
Nasłuchiwanie we wszystkich interfejsach przy użyciu ASPNETCORE_URLS
Powyższe przykłady mogą być używane ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Nasłuchiwanie we wszystkich interfejsach przy użyciu ASPNETCORE_HTTPS_PORTS
Powyższe przykłady mogą używać elementów ASPNETCORE_HTTPS_PORTS i ASPNETCORE_HTTP_PORTS.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Aby uzyskać więcej informacji, zobacz Konfigurowanie punktów końcowych dla serwera internetowego platformy ASP.NET Core Kestrel
Określanie protokołu HTTPS przy użyciu certyfikatu programistycznego
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Aby uzyskać więcej informacji na temat certyfikatu programistycznego, zobacz Trust the ASP.NET Core HTTPS development certificate on Windows and macOS (Ufaj certyfikatowi programistycznemu ASP.NET Core HTTPS w systemach Windows i macOS).
Określanie protokołu HTTPS przy użyciu certyfikatu niestandardowego
W poniższych sekcjach pokazano, jak określić certyfikat niestandardowy przy użyciu appsettings.json pliku i za pośrednictwem konfiguracji.
Określanie certyfikatu niestandardowego za pomocą polecenia appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Określanie certyfikatu niestandardowego za pomocą konfiguracji
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();
Korzystanie z interfejsów API certyfikatów
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();
Odczytywanie środowiska
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();
Aby uzyskać więcej informacji na temat korzystania ze środowiska, zobacz ASP.NET Core runtime environments (Środowiska uruchomieniowe ASP.NET Core)
Configuration
Poniższy kod odczytuje z systemu konfiguracji:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Aby uzyskać więcej informacji, zobacz Configuration in ASP.NET Core (Konfiguracja w programie ASP.NET Core)
Logging
Poniższy kod zapisuje komunikat podczas uruchamiania aplikacji logowania:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Aby uzyskać więcej informacji, zobacz Rejestrowanie na platformie .NET i ASP.NET Core
Uzyskiwanie dostępu do kontenera wstrzykiwania zależności (DI)
Poniższy kod pokazuje, jak pobrać usługi z kontenera DI podczas uruchamiania aplikacji:
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();
Poniższy kod pokazuje, jak uzyskać dostęp do kluczy z kontenera DI przy użyciu atrybutu [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.";
}
Aby uzyskać więcej informacji na temat di, zobacz Wstrzykiwanie zależności w ASP.NET Core.
WebApplicationBuilder
Ta sekcja zawiera przykładowy kod przy użyciu polecenia WebApplicationBuilder.
Zmienianie katalogu głównego zawartości, nazwy aplikacji i środowiska
Poniższy kod ustawia katalog główny zawartości, nazwę aplikacji i środowisko:
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 inicjuje nowe wystąpienie klasy WebApplicationBuilder ze wstępnie skonfigurowanymi wartościami domyślnymi.
Aby uzyskać więcej informacji, zobacz omówienie podstaw platformy ASP.NET Core
Zmienianie katalogu głównego zawartości, nazwy aplikacji i środowiska przy użyciu zmiennych środowiskowych lub wiersza polecenia
W poniższej tabeli przedstawiono zmienną środowiskową i argument wiersza polecenia używany do zmiany katalogu głównego zawartości, nazwy aplikacji i środowiska:
| cecha | Zmienna środowiskowa | Argument wiersza polecenia |
|---|---|---|
| Nazwa aplikacji | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Nazwa środowiska | ASPNETCORE_ENVIRONMENT | --environment |
| Katalog główny zawartości | ASPNETCORE_CONTENTROOT | --contentRoot |
Dodawanie dostawców konfiguracji
Poniższy przykład dodaje dostawcę konfiguracji INI:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Aby uzyskać szczegółowe informacje, zobacz Dostawcy konfiguracji plików w konfiguracji w programie ASP.NET Core.
Konfiguracja odczytu
Domyślnie WebApplicationBuilder konfiguracja odczytu z wielu źródeł, w tym:
-
appSettings.jsoniappSettings.{environment}.json - Zmienne środowiskowe
- Wiersz polecenia
Aby uzyskać pełną listę źródeł konfiguracji, zobacz Konfiguracja domyślna w konfiguracji w programie ASP.NET Core.
Poniższy kod odczytuje HelloKey z konfiguracji i wyświetla wartość w punkcie / końcowym. Jeśli wartość konfiguracji ma wartość null, "Hello" zostanie przypisana do elementu message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Odczytywanie środowiska
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine($"Running in development.");
}
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Dodawanie dostawców rejestrowania
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();
Dodawanie usług
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();
Dostosowywanie elementu IHostBuilder
Dostęp do istniejących metod rozszerzeń IHostBuilder można uzyskać przy użyciu właściwości 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();
Dostosowywanie obiektu IWebHostBuilder
Dostęp do metod rozszerzeń IWebHostBuilder można uzyskać przy użyciu właściwości 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();
Zmienianie katalogu głównego sieci Web
Domyślnie katalog główny sieci Web jest powiązany z katalogem głównym zawartości w folderze wwwroot . Katalog główny sieci Web to miejsce, w którym oprogramowanie pośredniczące plików statycznych szuka plików statycznych. Katalog główny sieci Web można zmienić za pomocą WebHostOptionspolecenia , wiersza polecenia lub UseWebRoot metody :
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Niestandardowy kontener wstrzykiwania zależności (DI)
W poniższym przykładzie użyto funkcji 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();
Dodawanie oprogramowania pośredniczącego
W programie WebApplicationmożna skonfigurować dowolne istniejące oprogramowanie pośredniczące ASP.NET Core:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Aby uzyskać więcej informacji, zobacz ASP.NET Core Middleware
Strona wyjątku dla deweloperów
WebApplication.CreateBuilder Inicjuje nowe wystąpienie WebApplicationBuilder klasy ze wstępnie skonfigurowanymi wartościami domyślnymi. Strona wyjątku dewelopera jest włączona w wstępnie skonfigurowanych wartościach domyślnych. Po uruchomieniu następującego kodu w środowisku deweloperskim przejście do / strony renderuje przyjazną stronę, która pokazuje wyjątek.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () =>
{
throw new InvalidOperationException("Oops, the '/' route has thrown an exception.");
});
app.Run();
Oprogramowanie pośredniczące platformy ASP.NET Core
W poniższej tabeli wymieniono niektóre oprogramowanie pośredniczące często używane z minimalnymi interfejsami API.
| Middleware | Description | API |
|---|---|---|
| Authentication | Zapewnia obsługę uwierzytelniania. | UseAuthentication |
| Authorization | Zapewnia obsługę autoryzacji. | UseAuthorization |
| CORS | Konfiguruje współużytkowanie zasobów między źródłami. | UseCors |
| Procedura obsługi wyjątków | Globalnie obsługuje wyjątki zgłaszane przez potok oprogramowania pośredniczącego. | UseExceptionHandler |
| Przekierowane nagłówki | Przekazuje nagłówki przesłane przez serwer proxy do bieżącego żądania. | UseForwardedHeaders |
| Przekierowywanie HTTPS | Przekierowuje wszystkie żądania HTTP do protokołu HTTPS. | UseHttpsRedirection |
| Http Strict Transport Security (HSTS) | Oprogramowanie pośredniczące rozszerzenia zabezpieczeń, które dodaje specjalny nagłówek odpowiedzi. | UseHsts |
| Rejestrowanie żądań | Zapewnia obsługę rejestrowania żądań HTTP i odpowiedzi. | UseHttpLogging |
| Limity czasu żądania | Zapewnia obsługę konfigurowania limitów czasu żądań, wartości domyślnych globalnych i poszczególnych punktów końcowych. | UseRequestTimeouts |
| Rejestrowanie żądań W3C | Zapewnia obsługę rejestrowania żądań HTTP i odpowiedzi w formacie W3C. | UseW3CLogging |
| Buforowanie odpowiedzi | Zapewnia obsługę buforowania odpowiedzi. | UseResponseCaching |
| Kompresja odpowiedzi | Zapewnia obsługę kompresowania odpowiedzi. | UseResponseCompression |
| Session | Zapewnia obsługę zarządzania sesjami użytkowników. | UseSession |
| Pliki statyczne | Zapewnia obsługę plików statycznych i przeglądania katalogów. | UseStaticFiles, UseFileServer |
| WebSockets | Włącza protokoły WebSocket. | UseWebSockets |
W poniższych sekcjach omówiono obsługę żądań: routing, powiązanie parametrów i odpowiedzi.
Routing
Skonfigurowana WebApplication obsługuje Map{Verb} metodę MapMethods{Verb} HTTP typu camel-cased, npGet. , PostPut lub 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();
Argumenty Delegate przekazywane do tych metod są nazywane "procedurami obsługi tras".
Programy obsługi tras
Programy obsługi tras to metody, które są wykonywane, gdy trasa jest zgodna. Programy obsługi tras mogą być wyrażeniem lambda, funkcją lokalną, metodą wystąpienia lub metodą statyczną. Programy obsługi tras mogą być synchroniczne lub asynchroniczne.
Wyrażenie lambda
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();
Funkcja lokalna
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Metoda wystąpienia
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";
}
}
Metoda statyczna
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";
}
}
Punkt końcowy zdefiniowany poza Program.cs
Minimalne interfejsy API nie muszą znajdować się w lokalizacji 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" });
});
}
}
Zobacz również Grupy tras w dalszej części tego artykułu.
Nazwane punkty końcowe i generowanie linków
Punkty końcowe mogą mieć nazwy w celu wygenerowania adresów URL do punktu końcowego. Użycie nazwanego punktu końcowego pozwala uniknąć konieczności stosowania twardych ścieżek kodu w aplikacji:
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();
Powyższy kod jest wyświetlany The link to the hello route is /hello z punktu końcowego / .
UWAGA: W nazwach punktów końcowych jest rozróżniana wielkość liter.
Nazwy punktów końcowych:
- Musi ona być unikatowa w skali globalnej.
- Są używane jako identyfikator operacji interfejsu OpenAPI, gdy jest włączona obsługa interfejsu OpenAPI. Aby uzyskać więcej informacji, zobacz OpenAPI.
Parametry trasy
Parametry trasy można przechwycić w ramach definicji wzorca trasy:
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();
Powyższy kod zwraca The user id is 3 and book id is 7 z identyfikatora URI /users/3/books/7.
Procedura obsługi tras może zadeklarować parametry do przechwycenia. Po wysłaniu żądania do trasy z zadeklarowanymi parametrami do przechwycenia parametry są analizowane i przekazywane do programu obsługi. Ułatwia to przechwytywanie wartości w bezpieczny sposób typu. W poprzednim kodzie userId i bookId mają wartość int.
W poprzednim kodzie, jeśli nie można przekonwertować żadnej wartości trasy na intwartość , zgłaszany jest wyjątek. Żądanie /users/hello/books/3 GET zgłasza następujący wyjątek:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Symbol wieloznaczny i przechwyć wszystkie trasy
Następujące przechwycenie wszystkich tras zwracanych Routing to hello z punktu końcowego "/posts/hello":
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Ograniczenia trasy
Ograniczenia trasy ograniczają zgodne zachowanie trasy.
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();
W poniższej tabeli przedstawiono powyższe szablony tras i ich zachowanie:
| Szablon trasy | Przykładowy pasujący identyfikator URI |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Aby uzyskać więcej informacji, zobacz Route constraint reference in Routing in ASP.NET Core (Dokumentacja ograniczeń tras w usłudze Routing w usłudze ASP.NET Core).
Grupy tras
Metoda MapGroup rozszerzenia ułatwia organizowanie grup punktów końcowych za pomocą wspólnego prefiksu. Zmniejsza powtarzalny kod i umożliwia dostosowywanie całych grup punktów końcowych za pomocą jednego wywołania metod, takich jak RequireAuthorization i WithMetadata które dodają metadane punktu końcowego.
Na przykład poniższy kod tworzy dwie podobne grupy punktów końcowych:
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;
}
W tym scenariuszu możesz użyć względnego adresu nagłówka Location w 201 Created wyniku:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Pierwsza grupa punktów końcowych będzie pasowała tylko do żądań z prefiksem /public/todos i jest dostępna bez żadnego uwierzytelniania. Druga grupa punktów końcowych będzie pasowała tylko do żądań poprzedzonych prefiksem /private/todos i wymaga uwierzytelniania.
QueryPrivateTodos
Fabryka filtrów punktów końcowych to funkcja lokalna, która modyfikuje parametry programu obsługi TodoDb tras, aby umożliwić dostęp do prywatnych danych zadań do wykonania i przechowywanie ich.
Grupy tras obsługują również grupy zagnieżdżone i złożone wzorce prefiksów z parametrami i ograniczeniami trasy. W poniższym przykładzie program obsługi tras zamapowany na grupę user może przechwytywać {org} parametry trasy i {group} zdefiniowane w prefiksach grup zewnętrznych.
Prefiks może być również pusty. Może to być przydatne w przypadku dodawania metadanych lub filtrów punktu końcowego do grupy punktów końcowych bez zmiany wzorca trasy.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Dodanie filtrów lub metadanych do grupy działa tak samo jak dodawanie ich indywidualnie do każdego punktu końcowego przed dodaniem dodatkowych filtrów lub metadanych, które mogły zostać dodane do grupy wewnętrznej lub określonego punktu końcowego.
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);
});
W powyższym przykładzie zewnętrzny filtr będzie rejestrować żądanie przychodzące przed filtrem wewnętrznym, mimo że został dodany w sekundzie. Ponieważ filtry zostały zastosowane do różnych grup, kolejność, którą zostały dodane względem siebie, nie ma znaczenia. Filtry kolejności są dodawane niezależnie od tego, czy są stosowane do tej samej grupy lub określonego punktu końcowego.
Żądanie, aby zarejestrować /outer/inner/ następujące elementy:
/outer group filter
/inner group filter
MapGet filter
Powiązanie parametrów
Powiązanie parametrów to proces konwertowania danych żądania na silnie typizowane parametry, które są wyrażane przez programy obsługi tras. Źródło powiązania określa, skąd są powiązane parametry. Źródła powiązań mogą być jawne lub wnioskowane na podstawie metody HTTP i typu parametru.
Obsługiwane źródła powiązań:
- Wartości tras
- Ciąg zapytania
- Header
- Treść (jako kod JSON)
- Wartości formularza
- Usługi udostępniane przez wstrzykiwanie zależności
- Custom
Poniższa procedura obsługi tras GET używa niektórych z tych źródeł powiązań parametrów:
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 { }
Kluczowe funkcje powiązania parametrów
-
Powiązanie jawne: użyj atrybutów, takich jak
[FromRoute], ,[FromQuery][FromHeader][FromBody], ,[FromForm]i[FromServices], aby jawnie określić źródła powiązań. -
Powiązanie formularza: powiązanie wartości formularza za pomocą atrybutu
[FromForm], w tym wsparcie dlaIFormFileiIFormFileCollectionprzy przesyłaniu plików. - Typy złożone: Łącz z kolekcjami i złożonymi typami z formularzy, stringów zapytania i nagłówków.
-
Powiązanie niestandardowe: Zaimplementuj niestandardową logikę powiązania przy użyciu
TryParse,BindAsynclub interfejsuIBindableFromHttpContext<T>. - Parametry opcjonalne: obsługują typy dopuszczane do wartości null i wartości domyślne dla parametrów opcjonalnych.
- Wstrzykiwanie zależności: parametry są automatycznie powiązane z usługami zarejestrowanymi w kontenerze DI.
-
Typy specjalne: automatyczne powiązanie dla
HttpContext, ,HttpRequest,HttpResponseCancellationToken,ClaimsPrincipal,StreamiPipeReader.
Dowiedz się więcej: Aby uzyskać szczegółowe informacje na temat powiązania parametrów, w tym zaawansowanych scenariuszy, walidacji, pierwszeństwa powiązania i rozwiązywania problemów, zobacz Powiązanie parametrów w minimalnych aplikacjach interfejsu API.
Responses
Programy obsługi tras obsługują następujące typy zwracanych wartości:
-
IResultoparte — obejmujeTask<IResult>to iValueTask<IResult> -
string- Obejmuje toTask<string>iValueTask<string> -
T(Dowolny inny typ) — obejmujeTask<T>to iValueTask<T>
| Wartość zwracana | Behavior | Content-Type |
|---|---|---|
IResult |
Struktura wywołuje metodę IResult.ExecuteAsync | Decyzja o wdrożeniu IResult |
string |
Struktura zapisuje ciąg bezpośrednio w odpowiedzi | text/plain |
T (Dowolny inny typ) |
Struktura JSON serializuje odpowiedź | application/json |
Aby uzyskać bardziej szczegółowy przewodnik po zwracaniu wartości procedury obsługi tras, zobacz Tworzenie odpowiedzi w aplikacjach interfejsu API w minimalnej liczbie
Przykładowe wartości zwracane
ciąg zwracane wartości
app.MapGet("/hello", () => "Hello World");
Wartości zwracane w formacie JSON
app.MapGet("/hello", () => new { Message = "Hello World" });
Zwracane wartości TypedResults
Poniższy kod zwraca element TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Zwracanie jest preferowane TypedResults do zwracania Resultswartości . Aby uzyskać więcej informacji, zobacz TypedResults vs Results (TypdResults a wyniki).
Zwracane wartości IResult
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
W poniższym przykładzie użyto wbudowanych typów wyników, aby dostosować odpowiedź:
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" }));
Niestandardowy kod stanu
app.MapGet("/405", () => Results.StatusCode(405));
Tekst
app.MapGet("/text", () => Results.Text("This is some text"));
Stream
var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
Aby uzyskać więcej przykładów, zobacz Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Wbudowane wyniki
Typowe pomocniki wyników istnieją w Results klasach statycznych i .TypedResults Zwracanie jest preferowane TypedResults do zwracania Resultswartości . Aby uzyskać więcej informacji, zobacz TypedResults vs Results (TypdResults a wyniki).
Modyfikowanie nagłówków
Użyj obiektu HttpResponse, aby zmodyfikować nagłówki odpowiedzi:
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";
});
Dostosowywanie wyników
Aplikacje mogą kontrolować odpowiedzi, implementując typ niestandardowy IResult . Poniższy kod jest przykładem typu wyniku HTML:
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);
}
}
Zalecamy dodanie metody rozszerzenia w celu Microsoft.AspNetCore.Http.IResultExtensions zwiększenia możliwości odnajdywania tych niestandardowych wyników.
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();
Wpisane wyniki
Interfejs IResult może reprezentować wartości zwracane z minimalnych interfejsów API, które nie korzystają z niejawnej obsługi serializowania zwracanego obiektu do odpowiedzi HTTP.
Statyczna klasa Results służy do tworzenia różnych IResult obiektów reprezentujących różne typy odpowiedzi. Na przykład ustawienie kodu stanu odpowiedzi lub przekierowanie do innego adresu URL.
Implementowane IResult typy są publiczne, co umożliwia asercji typów podczas testowania. Przykład:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Możesz przyjrzeć się typom zwracanych odpowiednich metod w statycznej klasie TypedResults , aby znaleźć prawidłowy typ publiczny IResult do rzutowania.
Aby uzyskać więcej przykładów, zobacz Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API.
Filters
Aby uzyskać więcej informacji, zobacz Filtry w minimalnych aplikacjach interfejsu API.
Authorization
Trasy mogą być chronione przy użyciu zasad autoryzacji. Można je zadeklarować za pomocą atrybutu [Authorize] lub przy użyciu RequireAuthorization metody :
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();
Powyższy kod można napisać za pomocą RequireAuthorizationpolecenia :
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
W poniższym przykładzie użyto autoryzacji opartej na zasadach:
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();
Zezwalanie nieuwierzytelnionym użytkownikom na dostęp do punktu końcowego
Ustawienie [AllowAnonymous] umożliwia nieuwierzytelnionym użytkownikom dostęp do punktów końcowych:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Trasy mogą być włączone przez mechanizm CORS przy użyciu zasad MECHANIZMU CORS. Mechanizm CORS można zadeklarować za pomocą atrybutu [EnableCors] lub przy użyciu RequireCors metody . Następujące przykłady umożliwiają mechanizm 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();
Aby uzyskać więcej informacji, zobacz Włączanie żądań między źródłami (CORS) w usłudze ASP.NET Core
ValidateScopes i ValidateOnBuild
ValidateScopes i ValidateOnBuild są domyślnie włączone w środowisku deweloperów , ale wyłączone w innych środowiskach.
Gdy ValidateOnBuild parametr to true, kontener DI weryfikuje konfigurację usługi w czasie kompilacji. Jeśli konfiguracja usługi jest nieprawidłowa, kompilacja kończy się niepowodzeniem podczas uruchamiania aplikacji, a nie w czasie wykonywania żądania usługi.
Gdy ValidateScopes parametr to true, kontener DI sprawdza, czy usługa o określonym zakresie nie jest rozpoznawana z zakresu głównego. Rozwiązanie usługi o określonym zakresie z zakresu głównego może spowodować wyciek pamięci, ponieważ usługa jest przechowywana w pamięci dłużej niż zakres żądania.
ValidateScopes wartości i ValidateOnBuild są domyślnie fałszywe w trybach innych niż Programowanie ze względu na wydajność.
Poniższy kod pokazuje ValidateScopes , że jest domyślnie włączony w trybie programowania, ale wyłączony w trybie wydania:
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 { }
Poniższy kod pokazuje ValidateOnBuild , że jest domyślnie włączony w trybie programowania, ale wyłączony w trybie wydania:
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 { }
Następujący kod jest ValidateScopes wyłączany i ValidateOnBuild w pliku 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;
});
}
Zobacz także
- Krótkie informacje o minimalnych interfejsach API
- Generowanie dokumentów OpenAPI
- Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API
- Filtry w minimalnych aplikacjach interfejsu API
- Obsługa błędów w interfejsach API platformy ASP.NET Core
- Uwierzytelnianie i autoryzacja w minimalnych interfejsach API
- Testowanie minimalnych aplikacji interfejsu API
- Routing zwariowy
- Identity Punkty końcowe interfejsu API
- Obsługa kontenera wstrzykiwania zależności usługi kluczy
- Spojrzenie za kulisami minimalnych punktów końcowych interfejsu API
- Organizowanie minimalnych interfejsów API ASP.NET Core
- Płynna dyskusja dotycząca walidacji w usłudze GitHub
Ten dokument:
- Zawiera skróconą dokumentację dla minimalnych interfejsów API.
- Jest przeznaczony dla doświadczonych deweloperów. Aby zapoznać się z wprowadzeniem, zobacz Samouczek: tworzenie minimalnego interfejsu API przy użyciu platformy ASP.NET Core
Minimalne interfejsy API składają się z następujących elementów:
WebApplication
Następujący kod jest generowany przez szablon ASP.NET Core:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Powyższy kod można utworzyć za pomocą wiersza dotnet new web polecenia lub wybrać pusty szablon sieci Web w programie Visual Studio.
Poniższy kod tworzy element WebApplication (app) bez jawnego utworzenia elementu WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create Inicjuje nowe wystąpienie WebApplication klasy ze wstępnie skonfigurowanymi wartościami domyślnymi.
WebApplication program automatycznie dodaje następujące oprogramowanie pośredniczące w aplikacjach interfejsu API w zależności od określonych warunków:
-
UseDeveloperExceptionPageelement jest dodawany jako pierwszy, gdy parametr ma wartośćHostingEnvironment"Development". -
UseRoutingJest dodawany drugi, jeśli kod użytkownika nie został jeszcze wywołanyUseRoutingi jeśli istnieją skonfigurowane punkty końcowe, na przykładapp.MapGet. -
UseEndpointsJest dodawany na końcu potoku oprogramowania pośredniczącego, jeśli są skonfigurowane jakiekolwiek punkty końcowe. -
UseAuthenticationjest dodawany natychmiast poUseRoutingtym, jak kod użytkownika nie został jeszcze wywołanyUseAuthenticationi czyIAuthenticationSchemeProvidermożna go wykryć u dostawcy usług.IAuthenticationSchemeProviderprogram jest domyślnie dodawany podczas korzystania z usługAddAuthentication, a usługa jest wykrywana przy użyciu poleceniaIServiceProviderIsService. -
UseAuthorizationZostanie dodany dalej, jeśli kod użytkownika nie został jeszcze wywołanyUseAuthorizationi czyIAuthorizationHandlerProvidermożna go wykryć u dostawcy usług.IAuthorizationHandlerProviderprogram jest domyślnie dodawany podczas korzystania z usługAddAuthorization, a usługa jest wykrywana przy użyciu poleceniaIServiceProviderIsService. - Oprogramowanie pośredniczące skonfigurowane przez użytkownika i punkty końcowe są dodawane między elementami
UseRoutingiUseEndpoints.
Poniższy kod jest w rzeczywistości tym, co tworzy automatyczne oprogramowanie pośredniczące dodawane do aplikacji:
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 => {});
W niektórych przypadkach domyślna konfiguracja oprogramowania pośredniczącego nie jest poprawna dla aplikacji i wymaga modyfikacji. Na przykład UseCors należy wywołać metodę przed UseAuthentication i UseAuthorization. Aplikacja musi wywołać metodę UseAuthentication , a UseAuthorization jeśli UseCors jest wywoływana:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Jeśli oprogramowanie pośredniczące powinno być uruchamiane przed rozpoczęciem dopasowywania tras, UseRouting należy wywołać metodę , a oprogramowanie pośredniczące powinno zostać umieszczone przed wywołaniem metody UseRouting.
UseEndpoints nie jest wymagany w tym przypadku, ponieważ jest automatycznie dodawany zgodnie z wcześniejszym opisem:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Podczas dodawania oprogramowania pośredniczącego terminalu:
- Oprogramowanie pośredniczące musi zostać dodane po .
UseEndpoints - Aplikacja musi wywołać metodę
UseRoutingiUseEndpointstak, aby oprogramowanie pośredniczące terminalu można było umieścić w odpowiedniej lokalizacji.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Oprogramowanie pośredniczące terminala to oprogramowanie pośredniczące uruchamiane, jeśli żaden punkt końcowy nie obsługuje żądania.
Praca z portami
Po utworzeniu aplikacji internetowej za pomocą programu Visual Studio lub dotnet newProperties/launchSettings.json zostanie utworzony plik, który określa porty, na które odpowiada aplikacja. W poniższych przykładach ustawień portów uruchomienie aplikacji z programu Visual Studio zwraca okno dialogowe Unable to connect to web server 'AppName'błędu . Program Visual Studio zwraca błąd, ponieważ oczekuje portu określonego w Properties/launchSettings.jsonelemecie , ale aplikacja używa portu określonego przez app.Run("http://localhost:3000"). Uruchom następujący port, zmieniając przykłady z wiersza polecenia.
W poniższych sekcjach ustawiono port, na który odpowiada aplikacja.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
W poprzednim kodzie aplikacja odpowiada na port 3000.
Wiele portów
W poniższym kodzie aplikacja odpowiada na port 3000 i 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Ustawianie portu z wiersza polecenia
Następujące polecenie powoduje, że aplikacja odpowiada na port 7777:
dotnet run --urls="https://localhost:7777"
Kestrel Jeśli punkt końcowy jest również skonfigurowany w appsettings.json pliku, appsettings.json używany jest określony adres URL. Aby uzyskać więcej informacji, zobacz Kestrel Konfiguracja punktu końcowego
Odczytywanie portu ze środowiska
Poniższy kod odczytuje port ze środowiska:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Preferowanym sposobem ustawienia portu ze środowiska jest użycie ASPNETCORE_URLS zmiennej środowiskowej, która jest pokazana w poniższej sekcji.
Ustawianie portów za pomocą zmiennej środowiskowej ASPNETCORE_URLS
Zmienna ASPNETCORE_URLS środowiskowa jest dostępna do ustawienia portu:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS obsługuje wiele adresów URL:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Aby uzyskać więcej informacji na temat korzystania ze środowiska, zobacz ASP.NET Core runtime environments (Środowiska uruchomieniowe ASP.NET Core)
Nasłuchiwanie we wszystkich interfejsach
W poniższych przykładach pokazano nasłuchiwanie we wszystkich interfejsach
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();
Nasłuchiwanie we wszystkich interfejsach przy użyciu ASPNETCORE_URLS
Powyższe przykłady mogą być używane ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Określanie protokołu HTTPS przy użyciu certyfikatu programistycznego
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Aby uzyskać więcej informacji na temat certyfikatu programistycznego, zobacz Trust the ASP.NET Core HTTPS development certificate on Windows and macOS (Ufaj certyfikatowi programistycznemu ASP.NET Core HTTPS w systemach Windows i macOS).
Określanie protokołu HTTPS przy użyciu certyfikatu niestandardowego
W poniższych sekcjach pokazano, jak określić certyfikat niestandardowy przy użyciu appsettings.json pliku i za pośrednictwem konfiguracji.
Określanie certyfikatu niestandardowego za pomocą polecenia appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Określanie certyfikatu niestandardowego za pomocą konfiguracji
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();
Korzystanie z interfejsów API certyfikatów
using System.Security.Cryptography.X509Certificates;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
options.ConfigureHttpsDefaults(httpsOptions =>
{
var certPath = Path.Combine(builder.Environment.ContentRootPath, "cert.pem");
var keyPath = Path.Combine(builder.Environment.ContentRootPath, "key.pem");
httpsOptions.ServerCertificate = X509Certificate2.CreateFromPemFile(certPath,
keyPath);
});
});
var app = builder.Build();
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Configuration
Poniższy kod odczytuje z systemu konfiguracji:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Aby uzyskać więcej informacji, zobacz Configuration in ASP.NET Core (Konfiguracja w programie ASP.NET Core)
Logging
Poniższy kod zapisuje komunikat podczas uruchamiania aplikacji logowania:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Aby uzyskać więcej informacji, zobacz Rejestrowanie na platformie .NET i ASP.NET Core
Uzyskiwanie dostępu do kontenera wstrzykiwania zależności (DI)
Poniższy kod pokazuje, jak pobrać usługi z kontenera DI podczas uruchamiania aplikacji:
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();
Aby uzyskać więcej informacji, zobacz Wstrzykiwanie zależności na platformie ASP.NET Core.
WebApplicationBuilder
Ta sekcja zawiera przykładowy kod przy użyciu polecenia WebApplicationBuilder.
Zmienianie katalogu głównego zawartości, nazwy aplikacji i środowiska
Poniższy kod ustawia katalog główny zawartości, nazwę aplikacji i środowisko:
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 inicjuje nowe wystąpienie klasy WebApplicationBuilder ze wstępnie skonfigurowanymi wartościami domyślnymi.
Aby uzyskać więcej informacji, zobacz omówienie podstaw platformy ASP.NET Core
Zmienianie katalogu głównego zawartości, nazwy aplikacji i środowiska według zmiennych środowiskowych lub wiersza polecenia
W poniższej tabeli przedstawiono zmienną środowiskową i argument wiersza polecenia używany do zmiany katalogu głównego zawartości, nazwy aplikacji i środowiska:
| cecha | Zmienna środowiskowa | Argument wiersza polecenia |
|---|---|---|
| Nazwa aplikacji | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Nazwa środowiska | ASPNETCORE_ENVIRONMENT | --environment |
| Katalog główny zawartości | ASPNETCORE_CONTENTROOT | --contentRoot |
Dodawanie dostawców konfiguracji
Poniższy przykład dodaje dostawcę konfiguracji INI:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Aby uzyskać szczegółowe informacje, zobacz Dostawcy konfiguracji plików w konfiguracji w programie ASP.NET Core.
Konfiguracja odczytu
Domyślnie WebApplicationBuilder konfiguracja odczytu z wielu źródeł, w tym:
-
appSettings.jsoniappSettings.{environment}.json - Zmienne środowiskowe
- Wiersz polecenia
Poniższy kod odczytuje HelloKey z konfiguracji i wyświetla wartość w punkcie / końcowym. Jeśli wartość konfiguracji ma wartość null, "Hello" zostanie przypisana do elementu message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Aby uzyskać pełną listę źródeł konfiguracji, zobacz Konfiguracja domyślna w konfiguracji w programie ASP.NET Core
Dodawanie dostawców rejestrowania
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();
Dodawanie usług
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();
Dostosowywanie elementu IHostBuilder
Dostęp do istniejących metod rozszerzeń IHostBuilder można uzyskać przy użyciu właściwości 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();
Dostosowywanie obiektu IWebHostBuilder
Dostęp do metod rozszerzeń IWebHostBuilder można uzyskać przy użyciu właściwości 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();
Zmienianie katalogu głównego sieci Web
Domyślnie katalog główny sieci Web jest powiązany z katalogem głównym zawartości w folderze wwwroot . Katalog główny sieci Web to miejsce, w którym oprogramowanie pośredniczące plików statycznych szuka plików statycznych. Katalog główny sieci Web można zmienić za pomocą WebHostOptionspolecenia , wiersza polecenia lub UseWebRoot metody :
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Niestandardowy kontener wstrzykiwania zależności (DI)
W poniższym przykładzie użyto funkcji 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();
Dodawanie oprogramowania pośredniczącego
W programie WebApplicationmożna skonfigurować dowolne istniejące oprogramowanie pośredniczące ASP.NET Core:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Aby uzyskać więcej informacji, zobacz ASP.NET Core Middleware
Strona wyjątku dla deweloperów
WebApplication.CreateBuilder Inicjuje nowe wystąpienie WebApplicationBuilder klasy ze wstępnie skonfigurowanymi wartościami domyślnymi. Strona wyjątku dewelopera jest włączona w wstępnie skonfigurowanych wartościach domyślnych. Po uruchomieniu następującego kodu w środowisku deweloperskim przejście do / strony renderuje przyjazną stronę, która pokazuje wyjątek.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () =>
{
throw new InvalidOperationException("Oops, the '/' route has thrown an exception.");
});
app.Run();
Oprogramowanie pośredniczące platformy ASP.NET Core
W poniższej tabeli wymieniono niektóre oprogramowanie pośredniczące często używane z minimalnymi interfejsami API.
| Middleware | Description | API |
|---|---|---|
| Authentication | Zapewnia obsługę uwierzytelniania. | UseAuthentication |
| Authorization | Zapewnia obsługę autoryzacji. | UseAuthorization |
| CORS | Konfiguruje współużytkowanie zasobów między źródłami. | UseCors |
| Procedura obsługi wyjątków | Globalnie obsługuje wyjątki zgłaszane przez potok oprogramowania pośredniczącego. | UseExceptionHandler |
| Przekierowane nagłówki | Przekazuje nagłówki przesłane przez serwer proxy do bieżącego żądania. | UseForwardedHeaders |
| Przekierowywanie HTTPS | Przekierowuje wszystkie żądania HTTP do protokołu HTTPS. | UseHttpsRedirection |
| Http Strict Transport Security (HSTS) | Oprogramowanie pośredniczące rozszerzenia zabezpieczeń, które dodaje specjalny nagłówek odpowiedzi. | UseHsts |
| Rejestrowanie żądań | Zapewnia obsługę rejestrowania żądań HTTP i odpowiedzi. | UseHttpLogging |
| Limity czasu żądania | Zapewnia obsługę konfigurowania limitów czasu żądań, wartości domyślnych globalnych i poszczególnych punktów końcowych. | UseRequestTimeouts |
| Rejestrowanie żądań W3C | Zapewnia obsługę rejestrowania żądań HTTP i odpowiedzi w formacie W3C. | UseW3CLogging |
| Buforowanie odpowiedzi | Zapewnia obsługę buforowania odpowiedzi. | UseResponseCaching |
| Kompresja odpowiedzi | Zapewnia obsługę kompresowania odpowiedzi. | UseResponseCompression |
| Session | Zapewnia obsługę zarządzania sesjami użytkowników. | UseSession |
| Pliki statyczne | Zapewnia obsługę plików statycznych i przeglądania katalogów. | UseStaticFiles, UseFileServer |
| WebSockets | Włącza protokoły WebSocket. | UseWebSockets |
W poniższych sekcjach omówiono obsługę żądań: routing, powiązanie parametrów i odpowiedzi.
Routing
Skonfigurowana WebApplication obsługuje Map{Verb} metodę MapMethods{Verb} HTTP typu camel-cased, npGet. , PostPut lub 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();
Argumenty Delegate przekazywane do tych metod są nazywane "procedurami obsługi tras".
Programy obsługi tras
Programy obsługi tras to metody, które są wykonywane, gdy trasa jest zgodna. Programy obsługi tras mogą być wyrażeniem lambda, funkcją lokalną, metodą wystąpienia lub metodą statyczną. Programy obsługi tras mogą być synchroniczne lub asynchroniczne.
Wyrażenie lambda
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();
Funkcja lokalna
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Metoda wystąpienia
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";
}
}
Metoda statyczna
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";
}
}
Punkt końcowy zdefiniowany poza Program.cs
Minimalne interfejsy API nie muszą znajdować się w lokalizacji 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" });
});
}
}
Zobacz również Grupy tras w dalszej części tego artykułu.
Nazwane punkty końcowe i generowanie linków
Punkty końcowe mogą mieć nazwy w celu wygenerowania adresów URL do punktu końcowego. Użycie nazwanego punktu końcowego pozwala uniknąć konieczności stosowania twardych ścieżek kodu w aplikacji:
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();
Powyższy kod jest wyświetlany The link to the hello route is /hello z punktu końcowego / .
UWAGA: W nazwach punktów końcowych jest rozróżniana wielkość liter.
Nazwy punktów końcowych:
- Musi ona być unikatowa w skali globalnej.
- Są używane jako identyfikator operacji interfejsu OpenAPI, gdy jest włączona obsługa interfejsu OpenAPI. Aby uzyskać więcej informacji, zobacz OpenAPI.
Parametry trasy
Parametry trasy można przechwycić w ramach definicji wzorca trasy:
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();
Powyższy kod zwraca The user id is 3 and book id is 7 z identyfikatora URI /users/3/books/7.
Procedura obsługi tras może zadeklarować parametry do przechwycenia. Po wysłaniu żądania do trasy z zadeklarowanymi parametrami do przechwycenia parametry są analizowane i przekazywane do programu obsługi. Ułatwia to przechwytywanie wartości w bezpieczny sposób typu. W poprzednim kodzie userId i bookId mają wartość int.
W poprzednim kodzie, jeśli nie można przekonwertować żadnej wartości trasy na intwartość , zgłaszany jest wyjątek. Żądanie /users/hello/books/3 GET zgłasza następujący wyjątek:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Symbol wieloznaczny i przechwyć wszystkie trasy
Następujące przechwycenie wszystkich tras zwracanych Routing to hello z punktu końcowego "/posts/hello":
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Ograniczenia trasy
Ograniczenia trasy ograniczają zgodne zachowanie trasy.
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();
W poniższej tabeli przedstawiono powyższe szablony tras i ich zachowanie:
| Szablon trasy | Przykładowy pasujący identyfikator URI |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Aby uzyskać więcej informacji, zobacz Route constraint reference in Routing in ASP.NET Core (Dokumentacja ograniczeń tras w usłudze Routing w usłudze ASP.NET Core).
Grupy tras
Metoda MapGroup rozszerzenia ułatwia organizowanie grup punktów końcowych za pomocą wspólnego prefiksu. Zmniejsza powtarzalny kod i umożliwia dostosowywanie całych grup punktów końcowych za pomocą jednego wywołania metod, takich jak RequireAuthorization i WithMetadata które dodają metadane punktu końcowego.
Na przykład poniższy kod tworzy dwie podobne grupy punktów końcowych:
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;
}
W tym scenariuszu możesz użyć względnego adresu nagłówka Location w 201 Created wyniku:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Pierwsza grupa punktów końcowych będzie pasowała tylko do żądań z prefiksem /public/todos i jest dostępna bez żadnego uwierzytelniania. Druga grupa punktów końcowych będzie pasowała tylko do żądań poprzedzonych prefiksem /private/todos i wymaga uwierzytelniania.
QueryPrivateTodos
Fabryka filtrów punktów końcowych to funkcja lokalna, która modyfikuje parametry programu obsługi TodoDb tras, aby umożliwić dostęp do prywatnych danych zadań do wykonania i przechowywanie ich.
Grupy tras obsługują również grupy zagnieżdżone i złożone wzorce prefiksów z parametrami i ograniczeniami trasy. W poniższym przykładzie program obsługi tras zamapowany na grupę user może przechwytywać {org} parametry trasy i {group} zdefiniowane w prefiksach grup zewnętrznych.
Prefiks może być również pusty. Może to być przydatne w przypadku dodawania metadanych lub filtrów punktu końcowego do grupy punktów końcowych bez zmiany wzorca trasy.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Dodanie filtrów lub metadanych do grupy działa tak samo jak dodawanie ich indywidualnie do każdego punktu końcowego przed dodaniem dodatkowych filtrów lub metadanych, które mogły zostać dodane do grupy wewnętrznej lub określonego punktu końcowego.
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);
});
W powyższym przykładzie zewnętrzny filtr będzie rejestrować żądanie przychodzące przed filtrem wewnętrznym, mimo że został dodany w sekundzie. Ponieważ filtry zostały zastosowane do różnych grup, kolejność, którą zostały dodane względem siebie, nie ma znaczenia. Filtry kolejności są dodawane niezależnie od tego, czy są stosowane do tej samej grupy lub określonego punktu końcowego.
Żądanie, aby zarejestrować /outer/inner/ następujące elementy:
/outer group filter
/inner group filter
MapGet filter
Powiązanie parametrów
Powiązanie parametrów to proces konwertowania danych żądania na silnie typizowane parametry, które są wyrażane przez programy obsługi tras. Źródło powiązania określa, skąd są powiązane parametry. Źródła powiązań mogą być jawne lub wnioskowane na podstawie metody HTTP i typu parametru.
Obsługiwane źródła powiązań:
- Wartości tras
- Ciąg zapytania
- Header
- Treść (jako kod JSON)
- Usługi udostępniane przez wstrzykiwanie zależności
- Custom
Powiązanie z wartości formularza nie jest natywnie obsługiwane na platformie .NET 6 i 7.
GET Poniższa procedura obsługi tras używa niektórych z tych źródeł powiązań parametrów:
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 { }
W poniższej tabeli przedstawiono relację między parametrami używanymi w poprzednim przykładzie i skojarzonymi źródłami powiązań.
| Parameter | Źródło powiązania |
|---|---|
id |
wartość trasy |
page |
ciąg zapytania |
customHeader |
nagłówek |
service |
Udostępniane przez wstrzykiwanie zależności |
Metody GETHTTP , HEAD, OPTIONSi DELETE nie są niejawnie powiązane z treścią. Aby powiązać z treścią (jako kod JSON) dla tych metod HTTP, powiąż jawnie z elementem [FromBody] lub odczyt z pliku HttpRequest.
W poniższym przykładzie procedura obsługi tras POST używa powiązania źródła treści (jako kodu JSON) dla parametru person :
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/", (Person person) => { });
record Person(string Name, int Age);
Parametry w poprzednich przykładach są automatycznie powiązane z danymi żądania. Aby zademonstrować wygodę zapewnianą przez powiązanie parametrów, następujące programy obsługi tras pokazują, jak odczytywać dane żądania bezpośrednio z żądania:
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>();
// ...
});
Jawne powiązanie parametrów
Atrybuty mogą służyć do jawnego deklarowania, gdzie parametry są powiązane.
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 | Źródło powiązania |
|---|---|
id |
wartość trasy o nazwie id |
page |
ciąg zapytania o nazwie "p" |
service |
Udostępniane przez wstrzykiwanie zależności |
contentType |
nagłówek o nazwie "Content-Type" |
Note
Powiązanie z wartości formularza nie jest natywnie obsługiwane na platformie .NET 6 i 7.
Powiązanie parametrów z wstrzyknięciem zależności
Powiązanie parametrów dla minimalnych interfejsów API wiąże parametry za pośrednictwem wstrzykiwania zależności, gdy typ jest skonfigurowany jako usługa. Nie jest konieczne jawne zastosowanie atrybutu [FromServices] do parametru. W poniższym kodzie obie akcje zwracają czas:
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();
Parametry opcjonalne
Parametry zadeklarowane w programach obsługi tras są traktowane zgodnie z wymaganiami:
- Jeśli żądanie pasuje do trasy, procedura obsługi tras jest uruchamiana tylko wtedy, gdy wszystkie wymagane parametry są podane w żądaniu.
- Niepowodzenie podania wszystkich wymaganych parametrów powoduje wystąpienie błędu.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int pageNumber) => $"Requesting page {pageNumber}");
app.Run();
| URI | result |
|---|---|
/products?pageNumber=3 |
3 zwrócone |
/products |
BadHttpRequestException: Wymagany parametr "int pageNumber" nie został podany z ciągu zapytania. |
/products/1 |
Błąd HTTP 404, brak pasującej trasy |
Aby ustawić pageNumber wartość opcjonalną, zdefiniuj typ jako opcjonalny lub podaj wartość domyślną:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
string ListProducts(int pageNumber = 1) => $"Requesting page {pageNumber}";
app.MapGet("/products2", ListProducts);
app.Run();
| URI | result |
|---|---|
/products?pageNumber=3 |
3 zwrócone |
/products |
1 zwrócone |
/products2 |
1 zwrócone |
Poprzednia wartość dopuszczająca wartość null i domyślna ma zastosowanie do wszystkich źródeł:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/products", (Product? product) => { });
app.Run();
Powyższy kod wywołuje metodę z produktem o wartości null, jeśli nie zostanie wysłana żadna treść żądania.
UWAGA: Jeśli podano nieprawidłowe dane i parametr ma wartość null, procedura obsługi tras nie jest uruchamiana.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
app.Run();
| URI | result |
|---|---|
/products?pageNumber=3 |
3 Zwracane |
/products |
1 Zwracane |
/products?pageNumber=two |
BadHttpRequestException: Nie można powiązać parametru "Nullable<int> pageNumber" z "dwóch". |
/products/two |
Błąd HTTP 404, brak pasującej trasy |
Aby uzyskać więcej informacji, zobacz sekcję Błędy powiązań.
Typy specjalne
Następujące typy są powiązane bez jawnych atrybutów:
HttpContext: kontekst zawierający wszystkie informacje o bieżącym żądaniu HTTP lub odpowiedzi:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));HttpRequest i HttpResponse: Żądanie HTTP i odpowiedź HTTP:
app.MapGet("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));CancellationToken: token anulowania skojarzony z bieżącym żądaniem HTTP:
app.MapGet("/", async (CancellationToken cancellationToken) => await MakeLongRunningRequestAsync(cancellationToken));ClaimsPrincipal: użytkownik skojarzony z żądaniem powiązany z elementem HttpContext.User:
app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
Powiąż treść żądania jako element Stream lub PipeReader
Treść żądania może wiązać się ze scenariuszami Stream lub PipeReader , w których użytkownik musi przetwarzać dane i:
- Zapisz dane w magazynie obiektów blob lub zapisz dane w kolejce do dostawcy kolejki.
- Przetwarzanie przechowywanych danych za pomocą procesu roboczego lub funkcji w chmurze.
Na przykład dane mogą być w kolejce do usługi Azure Queue Storage lub przechowywane w usłudze Azure Blob Storage.
Poniższy kod implementuje kolejkę w tle:
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;
}
Poniższy kod wiąże treść żądania z elementem 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);
});
Poniższy kod przedstawia kompletny Program.cs plik:
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();
- Podczas odczytywania danych
Streamobiekt jest tym samym obiektem coHttpRequest.Body. - Treść żądania nie jest domyślnie buforowana. Po odczytaniu treści nie można jej przewijać. Strumień nie może być odczytywany wiele razy.
- Elementy
StreamiPipeReadernie mogą być używane poza minimalną procedurą obsługi akcji, ponieważ bazowe zostaną usunięte lub ponownie użyte.
Przekazywanie plików przy użyciu elementu IFormFile i IFormFileCollection
Poniższy kod używa instrukcji IFormFile i IFormFileCollection do przekazywania pliku:
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();
Uwierzytelnione żądania przekazywania plików są obsługiwane przy użyciu nagłówka autoryzacji, certyfikatu klienta lub nagłówka cookie .
Nie ma wbudowanej obsługi ochrony przed fałszerzami w programie ASP.NET Core na platformie .NET 7.
Antiforgery jest dostępny w programie ASP.NET Core na platformie .NET 8 lub nowszym. Można go jednak zaimplementować przy użyciu IAntiforgery usługi.
Wiązanie tablic i wartości ciągów z nagłówków i ciągów zapytania
Poniższy kod demonstruje powiązania ciągów zapytania z tablicą typów pierwotnych, tablic ciągów i 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]}");
Powiązanie ciągów zapytania lub wartości nagłówków z tablicą typów złożonych jest obsługiwane, gdy typ został TryParse zaimplementowany. Poniższy kod wiąże się z tablicą ciągów i zwraca wszystkie elementy z określonymi tagami:
// 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();
});
Poniższy kod przedstawia model i wymaganą TryParse implementację:
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;
}
}
Poniższy kod wiąże się z tablicą int :
// 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();
});
Aby przetestować poprzedni kod, dodaj następujący punkt końcowy, aby wypełnić bazę danych elementami Todo :
// POST /todoitems/batch
app.MapPost("/todoitems/batch", async (Todo[] todos, TodoDb db) =>
{
await db.Todos.AddRangeAsync(todos);
await db.SaveChangesAsync();
return Results.Ok(todos);
});
Użyj narzędzia do testowania interfejsu API, takiego jak HttpRepl przekazywanie następujących danych do poprzedniego punktu końcowego:
[
{
"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"
}
}
]
Poniższy kod wiąże się z kluczem X-Todo-Id nagłówka i zwraca Todo elementy z pasującymi Id wartościami:
// GET /todoitems/header-ids
// The keys of the headers should all be X-Todo-Id with different values
app.MapGet("/todoitems/header-ids", async ([FromHeader(Name = "X-Todo-Id")] int[] ids, TodoDb db) =>
{
return await db.Todos
.Where(t => ids.Contains(t.Id))
.ToListAsync();
});
Note
Podczas tworzenia powiązania string[] elementu z ciągu zapytania brak pasującej wartości ciągu zapytania spowoduje, że zamiast wartości null zostanie pusta tablica.
Powiązanie parametrów dla list argumentów za pomocą parametrów [AsParameters]
AsParametersAttribute Umożliwia proste powiązanie parametrów z typami, a nie złożonymi lub cyklicznych powiązań modelu.
Spójrzmy na poniższy 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.
Rozważ następujący GET punkt końcowy:
app.MapGet("/todoitems/{id}",
async (int Id, TodoDb Db) =>
await Db.Todos.FindAsync(Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
struct Następujące elementy mogą służyć do zastępowania poprzednich wyróżnionych parametrów:
struct TodoItemRequest
{
public int Id { get; set; }
public TodoDb Db { get; set; }
}
Refaktoryzowany GET punkt końcowy używa powyższego struct atrybutu 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());
Poniższy kod przedstawia dodatkowe punkty końcowe w aplikacji:
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();
});
Następujące klasy służą do refaktoryzacji list parametrów:
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!;
}
Poniższy kod przedstawia refaktoryzowane punkty końcowe przy użyciu poleceń AsParameters oraz poprzednie struct klasy i :
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();
});
Zastąp powyższe parametry następującymi record typami:
record TodoItemRequest(int Id, TodoDb Db);
record CreateTodoItemRequest(TodoItemDTO Dto, TodoDb Db);
record EditTodoItemRequest(int Id, TodoItemDTO Dto, TodoDb Db);
Użycie elementu z struct funkcją AsParameters może być bardziej wydajne niż użycie record typu.
Kompletny przykładowy kod w repozytorium AspNetCore.Docs.Samples .
Powiązanie niestandardowe
Istnieją trzy sposoby dostosowywania powiązania parametrów:
- W przypadku źródeł tras, zapytań i powiązań nagłówków powiąż typy niestandardowe, dodając metodę statyczną
TryParsedla typu. - Kontrolowanie procesu wiązania przez zaimplementowanie
BindAsyncmetody dla typu. - W przypadku zaawansowanych scenariuszy zaimplementuj interfejs IBindableFromHttpContext<TSelf> w celu zapewnienia niestandardowej logiki powiązania bezpośrednio z
HttpContext.
TryParse
TryParse ma dwa interfejsy API:
public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);
Poniższy kod jest wyświetlany Point: 12.3, 10.1 za pomocą identyfikatora 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 ma następujące interfejsy API:
public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);
Poniższy kod jest wyświetlany SortBy:xyz, SortDirection:Desc, CurrentPage:99 za pomocą identyfikatora 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
}
Powiązanie parametrów niestandardowych za pomocą IBindableFromHttpContext
ASP.NET Core zapewnia obsługę wiązania niestandardowych parametrów w Minimal APIs przy użyciu interfejsu IBindableFromHttpContext<TSelf>. Ten interfejs, wprowadzony przy użyciu statycznych elementów abstrakcyjnych języka C# 11, umożliwia tworzenie typów, które mogą być powiązane z kontekstu HTTP bezpośrednio w parametrach procedury obsługi tras.
public interface IBindableFromHttpContext<TSelf>
where TSelf : class, IBindableFromHttpContext<TSelf>
{
static abstract ValueTask<TSelf?> BindAsync(HttpContext context, ParameterInfo parameter);
}
Implementując IBindableFromHttpContext<TSelf> interfejs, można tworzyć typy niestandardowe obsługujące własną logikę powiązania z obiektu HttpContext. Gdy program obsługi tras zawiera parametr tego typu, platforma automatycznie wywołuje statyczną metodę BindAsync w celu utworzenia wystąpienia:
using CustomBindingExample;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseHttpsRedirection();
app.MapGet("/", () => "Hello, IBindableFromHttpContext example!");
app.MapGet("/custom-binding", (CustomBoundParameter param) =>
{
return $"Value from custom binding: {param.Value}";
});
app.MapGet("/combined/{id}", (int id, CustomBoundParameter param) =>
{
return $"ID: {id}, Custom Value: {param.Value}";
});
Poniżej przedstawiono przykładową implementację parametru niestandardowego, który wiąże się z nagłówkiem HTTP:
using System.Reflection;
namespace CustomBindingExample;
public class CustomBoundParameter : IBindableFromHttpContext<CustomBoundParameter>
{
public string Value { get; init; } = default!;
public static ValueTask<CustomBoundParameter?> BindAsync(HttpContext context, ParameterInfo parameter)
{
// Custom binding logic here
// This example reads from a custom header
var value = context.Request.Headers["X-Custom-Header"].ToString();
// If no header was provided, you could fall back to a query parameter
if (string.IsNullOrEmpty(value))
{
value = context.Request.Query["customValue"].ToString();
}
return ValueTask.FromResult<CustomBoundParameter?>(new CustomBoundParameter
{
Value = value
});
}
}
Walidację można również zaimplementować w ramach niestandardowej logiki powiązań:
app.MapGet("/validated", (ValidatedParameter param) =>
{
if (string.IsNullOrEmpty(param.Value))
{
return Results.BadRequest("Value cannot be empty");
}
return Results.Ok($"Validated value: {param.Value}");
});
Wyświetlanie lub pobieranie przykładowego kodu (jak pobrać)
Błędy powiązań
Gdy powiązanie zakończy się niepowodzeniem, platforma rejestruje komunikat debugowania i zwraca różne kody stanu do klienta w zależności od trybu awarii.
| Tryb awarii | Typ parametru dopuszczalnego do wartości null | Źródło powiązania | Kod stanu |
|---|---|---|---|
{ParameterType}.TryParse Zwraca false |
yes | route/query/header | 400 |
{ParameterType}.BindAsync Zwraca null |
yes | custom | 400 |
{ParameterType}.BindAsync Zgłasza |
nie ma znaczenia | custom | 500 |
| Nie można wykonać deserializacji treści JSON | nie ma znaczenia | body | 400 |
Nieprawidłowy typ zawartości (nie application/json) |
nie ma znaczenia | body | 415 |
Pierwszeństwo powiązania
Reguły określania źródła powiązania z parametru:
- Jawny atrybut zdefiniowany w parametrze (atrybuty From*) w następującej kolejności:
- Wartości tras:
[FromRoute] - Ciąg zapytania:
[FromQuery] - Nagłówek:
[FromHeader] - Ciało:
[FromBody] - Usługa:
[FromServices] - Wartości parametrów:
[AsParameters]
- Wartości tras:
- Typy specjalne
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)
- Typ parametru ma prawidłową metodę statyczną
BindAsync. - Typ parametru jest ciągiem lub ma prawidłową metodę statyczną
TryParse.- Jeśli nazwa parametru istnieje w szablonie trasy. W elemecie
app.Map("/todo/{id}", (int id) => {});idjest powiązana z trasą. - Powiązana z ciągu zapytania.
- Jeśli nazwa parametru istnieje w szablonie trasy. W elemecie
- Jeśli typ parametru jest usługą dostarczaną przez iniekcję zależności, używa tej usługi jako źródła.
- Parametr pochodzi z treści.
Konfigurowanie opcji deserializacji JSON dla powiązania treści
Źródło powiązania treści używa System.Text.Json do deserializacji. Nie można zmienić tej wartości domyślnej, ale można skonfigurować opcje serializacji i deserializacji JSON.
Globalne konfigurowanie opcji deserializacji JSON
Opcje stosowane globalnie dla aplikacji można skonfigurować, wywołując ConfigureHttpJsonOptionsmetodę . Poniższy przykład zawiera pola publiczne i formaty danych wyjściowych JSON.
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
// }
Ponieważ przykładowy kod konfiguruje zarówno serializacji, jak i deserializacji, może odczytywać NameField i uwzględniać NameField dane wyjściowe w formacie JSON.
Konfigurowanie opcji deserializacji JSON dla punktu końcowego
ReadFromJsonAsync ma przeciążenia, które akceptują JsonSerializerOptions obiekt. Poniższy przykład zawiera pola publiczne i formaty danych wyjściowych JSON.
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
// }
Ponieważ powyższy kod stosuje dostosowane opcje tylko do deserializacji, dane wyjściowe JSON wykluczają NameFieldwartość .
Odczytywanie treści żądania
Odczytywanie treści żądania bezpośrednio przy użyciu parametru HttpContext lub HttpRequest :
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/uploadstream", async (IConfiguration config, HttpRequest request) =>
{
var filePath = Path.Combine(config["StoredFilesPath"], Path.GetRandomFileName());
await using var writeStream = File.Create(filePath);
await request.BodyReader.CopyToAsync(writeStream);
});
app.Run();
Powyższy kod ma następujące działanie:
- Uzyskuje dostęp do treści żądania przy użyciu polecenia HttpRequest.BodyReader.
- Kopiuje treść żądania do pliku lokalnego.
Responses
Programy obsługi tras obsługują następujące typy zwracanych wartości:
-
IResultoparte — obejmujeTask<IResult>to iValueTask<IResult> -
string- Obejmuje toTask<string>iValueTask<string> -
T(Dowolny inny typ) — obejmujeTask<T>to iValueTask<T>
| Wartość zwracana | Behavior | Content-Type |
|---|---|---|
IResult |
Struktura wywołuje metodę IResult.ExecuteAsync | Decyzja o wdrożeniu IResult |
string |
Struktura zapisuje ciąg bezpośrednio w odpowiedzi | text/plain |
T (Dowolny inny typ) |
Struktura JSON serializuje odpowiedź | application/json |
Aby uzyskać bardziej szczegółowy przewodnik po zwracaniu wartości procedury obsługi tras, zobacz Tworzenie odpowiedzi w aplikacjach interfejsu API w minimalnej liczbie
Przykładowe wartości zwracane
ciąg zwracane wartości
app.MapGet("/hello", () => "Hello World");
Wartości zwracane w formacie JSON
app.MapGet("/hello", () => new { Message = "Hello World" });
Zwracane wartości TypedResults
Poniższy kod zwraca element TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Zwracanie jest preferowane TypedResults do zwracania Resultswartości . Aby uzyskać więcej informacji, zobacz TypedResults vs Results (TypdResults a wyniki).
Zwracane wartości IResult
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
W poniższym przykładzie użyto wbudowanych typów wyników, aby dostosować odpowiedź:
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" }));
Niestandardowy kod stanu
app.MapGet("/405", () => Results.StatusCode(405));
Tekst
app.MapGet("/text", () => Results.Text("This is some text"));
Stream
var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
Aby uzyskać więcej przykładów, zobacz Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Wbudowane wyniki
Typowe pomocniki wyników istnieją w Results klasach statycznych i .TypedResults Zwracanie jest preferowane TypedResults do zwracania Resultswartości . Aby uzyskać więcej informacji, zobacz TypedResults vs Results (TypdResults a wyniki).
Dostosowywanie wyników
Aplikacje mogą kontrolować odpowiedzi, implementując typ niestandardowy IResult . Poniższy kod jest przykładem typu wyniku HTML:
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);
}
}
Zalecamy dodanie metody rozszerzenia w celu Microsoft.AspNetCore.Http.IResultExtensions zwiększenia możliwości odnajdywania tych niestandardowych wyników.
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();
Wpisane wyniki
Interfejs IResult może reprezentować wartości zwracane z minimalnych interfejsów API, które nie korzystają z niejawnej obsługi serializowania zwracanego obiektu do odpowiedzi HTTP.
Statyczna klasa Results służy do tworzenia różnych IResult obiektów reprezentujących różne typy odpowiedzi. Na przykład ustawienie kodu stanu odpowiedzi lub przekierowanie do innego adresu URL.
Implementowane IResult typy są publiczne, co umożliwia asercji typów podczas testowania. Przykład:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Możesz przyjrzeć się typom zwracanych odpowiednich metod w statycznej klasie TypedResults , aby znaleźć prawidłowy typ publiczny IResult do rzutowania.
Aby uzyskać więcej przykładów, zobacz Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API.
Filters
Zobacz Filtry w minimalnych aplikacjach interfejsu API
Authorization
Trasy mogą być chronione przy użyciu zasad autoryzacji. Można je zadeklarować za pomocą atrybutu [Authorize] lub przy użyciu RequireAuthorization metody :
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();
Powyższy kod można napisać za pomocą RequireAuthorizationpolecenia :
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
W poniższym przykładzie użyto autoryzacji opartej na zasadach:
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();
Zezwalanie nieuwierzytelnionym użytkownikom na dostęp do punktu końcowego
Ustawienie [AllowAnonymous] umożliwia nieuwierzytelnionym użytkownikom dostęp do punktów końcowych:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Trasy mogą być włączone przez mechanizm CORS przy użyciu zasad MECHANIZMU CORS. Mechanizm CORS można zadeklarować za pomocą atrybutu [EnableCors] lub przy użyciu RequireCors metody . Następujące przykłady umożliwiają mechanizm 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();
Aby uzyskać więcej informacji, zobacz Włączanie żądań między źródłami (CORS) w usłudze ASP.NET Core
Zobacz także
- Generowanie dokumentów OpenAPI
- Tworzenie odpowiedzi w minimalnych aplikacjach interfejsu API
- Filtry w minimalnych aplikacjach interfejsu API
- Obsługa błędów w interfejsach API platformy ASP.NET Core
- Uwierzytelnianie i autoryzacja w minimalnych interfejsach API
- Testowanie minimalnych aplikacji interfejsu API
Ten dokument:
- Zawiera skróconą dokumentację dla minimalnych interfejsów API.
- Jest przeznaczony dla doświadczonych deweloperów. Aby zapoznać się z wprowadzeniem, zobacz Samouczek: tworzenie minimalnego interfejsu API przy użyciu platformy ASP.NET Core
Minimalne interfejsy API składają się z następujących elementów:
- WebApplication i WebApplicationBuilder
- Programy obsługi tras
WebApplication
Następujący kod jest generowany przez szablon ASP.NET Core:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Powyższy kod można utworzyć za pomocą wiersza dotnet new web polecenia lub wybrać pusty szablon sieci Web w programie Visual Studio.
Poniższy kod tworzy element WebApplication (app) bez jawnego utworzenia elementu WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create Inicjuje nowe wystąpienie WebApplication klasy ze wstępnie skonfigurowanymi wartościami domyślnymi.
Praca z portami
Po utworzeniu aplikacji internetowej za pomocą programu Visual Studio lub dotnet newProperties/launchSettings.json zostanie utworzony plik, który określa porty, na które odpowiada aplikacja. W poniższych przykładach ustawień portów uruchomienie aplikacji z programu Visual Studio zwraca okno dialogowe Unable to connect to web server 'AppName'błędu . Uruchom następujący port, zmieniając przykłady z wiersza polecenia.
W poniższych sekcjach ustawiono port, na który odpowiada aplikacja.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
W poprzednim kodzie aplikacja odpowiada na port 3000.
Wiele portów
W poniższym kodzie aplikacja odpowiada na port 3000 i 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Ustawianie portu z wiersza polecenia
Następujące polecenie powoduje, że aplikacja odpowiada na port 7777:
dotnet run --urls="https://localhost:7777"
Kestrel Jeśli punkt końcowy jest również skonfigurowany w appsettings.json pliku, appsettings.json używany jest określony adres URL. Aby uzyskać więcej informacji, zobacz Kestrel Konfiguracja punktu końcowego
Odczytywanie portu ze środowiska
Poniższy kod odczytuje port ze środowiska:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Preferowanym sposobem ustawienia portu ze środowiska jest użycie ASPNETCORE_URLS zmiennej środowiskowej, która jest pokazana w poniższej sekcji.
Ustawianie portów za pomocą zmiennej środowiskowej ASPNETCORE_URLS
Zmienna ASPNETCORE_URLS środowiskowa jest dostępna do ustawienia portu:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS obsługuje wiele adresów URL:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Nasłuchiwanie we wszystkich interfejsach
W poniższych przykładach pokazano nasłuchiwanie we wszystkich interfejsach
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();
Nasłuchiwanie we wszystkich interfejsach przy użyciu ASPNETCORE_URLS
Powyższe przykłady mogą być używane ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Określanie protokołu HTTPS przy użyciu certyfikatu programistycznego
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Aby uzyskać więcej informacji na temat certyfikatu programistycznego, zobacz Trust the ASP.NET Core HTTPS development certificate on Windows and macOS (Ufaj certyfikatowi programistycznemu ASP.NET Core HTTPS w systemach Windows i macOS).
Określanie protokołu HTTPS przy użyciu certyfikatu niestandardowego
W poniższych sekcjach pokazano, jak określić certyfikat niestandardowy przy użyciu appsettings.json pliku i za pośrednictwem konfiguracji.
Określanie certyfikatu niestandardowego za pomocą polecenia appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Określanie certyfikatu niestandardowego za pomocą konfiguracji
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();
Korzystanie z interfejsów API certyfikatów
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();
Odczytywanie środowiska
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();
Aby uzyskać więcej informacji na temat korzystania ze środowiska, zobacz ASP.NET Core runtime environments (Środowiska uruchomieniowe ASP.NET Core)
Configuration
Poniższy kod odczytuje z systemu konfiguracji:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Hello";
app.MapGet("/", () => message);
app.Run();
Aby uzyskać więcej informacji, zobacz Configuration in ASP.NET Core (Konfiguracja w programie ASP.NET Core)
Logging
Poniższy kod zapisuje komunikat podczas uruchamiania aplikacji logowania:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Aby uzyskać więcej informacji, zobacz Rejestrowanie na platformie .NET i ASP.NET Core
Uzyskiwanie dostępu do kontenera wstrzykiwania zależności (DI)
Poniższy kod pokazuje, jak pobrać usługi z kontenera DI podczas uruchamiania aplikacji:
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();
Aby uzyskać więcej informacji, zobacz Wstrzykiwanie zależności na platformie ASP.NET Core.
WebApplicationBuilder
Ta sekcja zawiera przykładowy kod przy użyciu polecenia WebApplicationBuilder.
Zmienianie katalogu głównego zawartości, nazwy aplikacji i środowiska
Poniższy kod ustawia katalog główny zawartości, nazwę aplikacji i środowisko:
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 inicjuje nowe wystąpienie klasy WebApplicationBuilder ze wstępnie skonfigurowanymi wartościami domyślnymi.
Aby uzyskać więcej informacji, zobacz omówienie podstaw platformy ASP.NET Core
Zmienianie katalogu głównego zawartości, nazwy aplikacji i środowiska według zmiennych środowiskowych lub wiersza polecenia
W poniższej tabeli przedstawiono zmienną środowiskową i argument wiersza polecenia używany do zmiany katalogu głównego zawartości, nazwy aplikacji i środowiska:
| cecha | Zmienna środowiskowa | Argument wiersza polecenia |
|---|---|---|
| Nazwa aplikacji | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Nazwa środowiska | ASPNETCORE_ENVIRONMENT | --environment |
| Katalog główny zawartości | ASPNETCORE_CONTENTROOT | --contentRoot |
Dodawanie dostawców konfiguracji
Poniższy przykład dodaje dostawcę konfiguracji INI:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Aby uzyskać szczegółowe informacje, zobacz Dostawcy konfiguracji plików w konfiguracji w programie ASP.NET Core.
Konfiguracja odczytu
Domyślnie WebApplicationBuilder konfiguracja odczytu z wielu źródeł, w tym:
-
appSettings.jsoniappSettings.{environment}.json - Zmienne środowiskowe
- Wiersz polecenia
Aby uzyskać pełną listę źródeł konfiguracji, zobacz Konfiguracja domyślna w konfiguracji w programie ASP.NET Core
Poniższy kod odczytuje HelloKey z konfiguracji i wyświetla wartość w punkcie / końcowym. Jeśli wartość konfiguracji ma wartość null, "Hello" zostanie przypisana do elementu message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Odczytywanie środowiska
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Dodawanie dostawców rejestrowania
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();
Dodawanie usług
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();
Dostosowywanie elementu IHostBuilder
Dostęp do istniejących metod rozszerzeń IHostBuilder można uzyskać przy użyciu właściwości 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();
Dostosowywanie obiektu IWebHostBuilder
Dostęp do metod rozszerzeń IWebHostBuilder można uzyskać przy użyciu właściwości 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();
Zmienianie katalogu głównego sieci Web
Domyślnie katalog główny sieci Web jest powiązany z katalogem głównym zawartości w folderze wwwroot . Katalog główny sieci Web to miejsce, w którym oprogramowanie pośredniczące plików statycznych szuka plików statycznych. Katalog główny sieci Web można zmienić za pomocą WebHostOptionspolecenia , wiersza polecenia lub UseWebRoot metody :
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Niestandardowy kontener wstrzykiwania zależności (DI)
W poniższym przykładzie użyto funkcji 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();
Dodawanie oprogramowania pośredniczącego
W programie WebApplicationmożna skonfigurować dowolne istniejące oprogramowanie pośredniczące ASP.NET Core:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Aby uzyskać więcej informacji, zobacz ASP.NET Core Middleware
Strona wyjątku dla deweloperów
WebApplication.CreateBuilder Inicjuje nowe wystąpienie WebApplicationBuilder klasy ze wstępnie skonfigurowanymi wartościami domyślnymi. Strona wyjątku dewelopera jest włączona w wstępnie skonfigurowanych wartościach domyślnych. Po uruchomieniu następującego kodu w środowisku deweloperskim przejście do / strony renderuje przyjazną stronę, która pokazuje wyjątek.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () =>
{
throw new InvalidOperationException("Oops, the '/' route has thrown an exception.");
});
app.Run();
Oprogramowanie pośredniczące platformy ASP.NET Core
W poniższej tabeli wymieniono niektóre oprogramowanie pośredniczące często używane z minimalnymi interfejsami API.
| Middleware | Description | API |
|---|---|---|
| Authentication | Zapewnia obsługę uwierzytelniania. | UseAuthentication |
| Authorization | Zapewnia obsługę autoryzacji. | UseAuthorization |
| CORS | Konfiguruje współużytkowanie zasobów między źródłami. | UseCors |
| Procedura obsługi wyjątków | Globalnie obsługuje wyjątki zgłaszane przez potok oprogramowania pośredniczącego. | UseExceptionHandler |
| Przekierowane nagłówki | Przekazuje nagłówki przesłane przez serwer proxy do bieżącego żądania. | UseForwardedHeaders |
| Przekierowywanie HTTPS | Przekierowuje wszystkie żądania HTTP do protokołu HTTPS. | UseHttpsRedirection |
| Http Strict Transport Security (HSTS) | Oprogramowanie pośredniczące rozszerzenia zabezpieczeń, które dodaje specjalny nagłówek odpowiedzi. | UseHsts |
| Rejestrowanie żądań | Zapewnia obsługę rejestrowania żądań HTTP i odpowiedzi. | UseHttpLogging |
| Rejestrowanie żądań W3C | Zapewnia obsługę rejestrowania żądań HTTP i odpowiedzi w formacie W3C. | UseW3CLogging |
| Buforowanie odpowiedzi | Zapewnia obsługę buforowania odpowiedzi. | UseResponseCaching |
| Kompresja odpowiedzi | Zapewnia obsługę kompresowania odpowiedzi. | UseResponseCompression |
| Session | Zapewnia obsługę zarządzania sesjami użytkowników. | UseSession |
| Pliki statyczne | Zapewnia obsługę plików statycznych i przeglądania katalogów. | UseStaticFiles, UseFileServer |
| WebSockets | Włącza protokoły WebSocket. | UseWebSockets |
Obsługa żądań
W poniższych sekcjach omówiono routing, powiązanie parametrów i odpowiedzi.
Routing
Skonfigurowana WebApplication obsługa i Map{Verb}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();
Programy obsługi tras
Programy obsługi tras to metody, które są wykonywane, gdy trasa jest zgodna. Programy obsługi tras mogą być funkcją dowolnego kształtu, w tym synchroniczną lub asynchroniczną. Programy obsługi tras mogą być wyrażeniem lambda, funkcją lokalną, metodą wystąpienia lub metodą statyczną.
Wyrażenie lambda
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();
Funkcja lokalna
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Metoda wystąpienia
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";
}
}
Metoda statyczna
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";
}
}
Nazwane punkty końcowe i generowanie linków
Punkty końcowe mogą mieć nazwy w celu wygenerowania adresów URL do punktu końcowego. Użycie nazwanego punktu końcowego pozwala uniknąć konieczności stosowania twardych ścieżek kodu w aplikacji:
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();
Powyższy kod jest wyświetlany The link to the hello endpoint is /hello z punktu końcowego / .
UWAGA: W nazwach punktów końcowych jest rozróżniana wielkość liter.
Nazwy punktów końcowych:
- Musi ona być unikatowa w skali globalnej.
- Są używane jako identyfikator operacji interfejsu OpenAPI, gdy jest włączona obsługa interfejsu OpenAPI. Aby uzyskać więcej informacji, zobacz OpenAPI.
Parametry trasy
Parametry trasy można przechwycić w ramach definicji wzorca trasy:
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();
Powyższy kod zwraca The user id is 3 and book id is 7 z identyfikatora URI /users/3/books/7.
Procedura obsługi tras może zadeklarować parametry do przechwycenia. Gdy żądanie jest kierowane z parametrami zadeklarowanymi do przechwycenia, parametry są analizowane i przekazywane do procedury obsługi. Ułatwia to przechwytywanie wartości w bezpieczny sposób typu. W poprzednim kodzie userId i bookId mają wartość int.
W poprzednim kodzie, jeśli nie można przekonwertować żadnej wartości trasy na intwartość , zgłaszany jest wyjątek. Żądanie /users/hello/books/3 GET zgłasza następujący wyjątek:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Symbol wieloznaczny i przechwyć wszystkie trasy
Następujące przechwycenie wszystkich tras zwracanych Routing to hello z punktu końcowego "/posts/hello":
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Ograniczenia trasy
Ograniczenia trasy ograniczają zgodne zachowanie trasy.
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();
W poniższej tabeli przedstawiono powyższe szablony tras i ich zachowanie:
| Szablon trasy | Przykładowy pasujący identyfikator URI |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Aby uzyskać więcej informacji, zobacz Route constraint reference in Routing in ASP.NET Core (Dokumentacja ograniczeń tras w usłudze Routing w usłudze ASP.NET Core).
Powiązanie parametrów
Powiązanie parametrów to proces konwertowania danych żądania na silnie typizowane parametry, które są wyrażane przez programy obsługi tras. Źródło powiązania określa, skąd są powiązane parametry. Źródła powiązań mogą być jawne lub wnioskowane na podstawie metody HTTP i typu parametru.
Obsługiwane źródła powiązań:
- Wartości tras
- Ciąg zapytania
- Header
- Treść (jako kod JSON)
- Usługi udostępniane przez wstrzykiwanie zależności
- Custom
Note
Powiązanie z wartości formularza nie jest natywnie obsługiwane na platformie .NET.
W poniższym przykładzie procedura obsługi tras GET używa niektórych z tych źródeł powiązań parametrów:
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 { }
W poniższej tabeli przedstawiono relację między parametrami używanymi w poprzednim przykładzie i skojarzonymi źródłami powiązań.
| Parameter | Źródło powiązania |
|---|---|
id |
wartość trasy |
page |
ciąg zapytania |
customHeader |
nagłówek |
service |
Udostępniane przez wstrzykiwanie zależności |
Metody GETHTTP , HEAD, OPTIONSi DELETE nie są niejawnie powiązane z treścią. Aby powiązać z treścią (jako kod JSON) dla tych metod HTTP, powiąż jawnie z elementem [FromBody] lub odczyt z pliku HttpRequest.
W poniższym przykładzie procedura obsługi tras POST używa powiązania źródła treści (jako kodu JSON) dla parametru person :
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/", (Person person) => { });
record Person(string Name, int Age);
Parametry w poprzednich przykładach są automatycznie powiązane z danymi żądania. Aby zademonstrować wygodę zapewnianą przez powiązanie parametrów, następujące przykładowe programy obsługi tras pokazują, jak odczytywać dane żądania bezpośrednio z żądania:
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>();
// ...
});
Jawne powiązanie parametrów
Atrybuty mogą służyć do jawnego deklarowania, gdzie parametry są powiązane.
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 | Źródło powiązania |
|---|---|
id |
wartość trasy o nazwie id |
page |
ciąg zapytania o nazwie "p" |
service |
Udostępniane przez wstrzykiwanie zależności |
contentType |
nagłówek o nazwie "Content-Type" |
Note
Powiązanie z wartości formularza nie jest natywnie obsługiwane na platformie .NET.
Powiązanie parametrów z di
Powiązanie parametrów dla minimalnych interfejsów API wiąże parametry za pośrednictwem wstrzykiwania zależności, gdy typ jest skonfigurowany jako usługa. Nie jest konieczne jawne zastosowanie atrybutu [FromServices] do parametru. W poniższym kodzie obie akcje zwracają czas:
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();
Parametry opcjonalne
Parametry zadeklarowane w programach obsługi tras są traktowane zgodnie z wymaganiami:
- Jeśli żądanie pasuje do trasy, procedura obsługi tras jest uruchamiana tylko wtedy, gdy wszystkie wymagane parametry są podane w żądaniu.
- Niepowodzenie podania wszystkich wymaganych parametrów powoduje wystąpienie błędu.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int pageNumber) => $"Requesting page {pageNumber}");
app.Run();
| URI | result |
|---|---|
/products?pageNumber=3 |
3 zwrócone |
/products |
BadHttpRequestException: Wymagany parametr "int pageNumber" nie został podany z ciągu zapytania. |
/products/1 |
Błąd HTTP 404, brak pasującej trasy |
Aby ustawić pageNumber wartość opcjonalną, zdefiniuj typ jako opcjonalny lub podaj wartość domyślną:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
string ListProducts(int pageNumber = 1) => $"Requesting page {pageNumber}";
app.MapGet("/products2", ListProducts);
app.Run();
| URI | result |
|---|---|
/products?pageNumber=3 |
3 zwrócone |
/products |
1 zwrócone |
/products2 |
1 zwrócone |
Poprzednia wartość dopuszczająca wartość null i domyślna ma zastosowanie do wszystkich źródeł:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/products", (Product? product) => { });
app.Run();
Powyższy kod wywołuje metodę z produktem o wartości null, jeśli nie zostanie wysłana żadna treść żądania.
UWAGA: Jeśli podano nieprawidłowe dane i parametr ma wartość null, procedura obsługi tras nie jest uruchamiana.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
app.Run();
| URI | result |
|---|---|
/products?pageNumber=3 |
3 Zwracane |
/products |
1 Zwracane |
/products?pageNumber=two |
BadHttpRequestException: Nie można powiązać parametru "Nullable<int> pageNumber" z "dwóch". |
/products/two |
Błąd HTTP 404, brak pasującej trasy |
Aby uzyskać więcej informacji, zobacz sekcję Błędy powiązań.
Typy specjalne
Następujące typy są powiązane bez jawnych atrybutów:
HttpContext: kontekst zawierający wszystkie informacje o bieżącym żądaniu HTTP lub odpowiedzi:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));HttpRequest i HttpResponse: Żądanie HTTP i odpowiedź HTTP:
app.MapGet("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));CancellationToken: token anulowania skojarzony z bieżącym żądaniem HTTP:
app.MapGet("/", async (CancellationToken cancellationToken) => await MakeLongRunningRequestAsync(cancellationToken));ClaimsPrincipal: użytkownik skojarzony z żądaniem powiązany z elementem HttpContext.User:
app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
Powiązanie niestandardowe
Istnieją dwa sposoby dostosowywania powiązania parametrów:
- W przypadku źródeł tras, zapytań i powiązań nagłówków powiąż typy niestandardowe, dodając metodę statyczną
TryParsedla typu. - Kontrolowanie procesu wiązania przez zaimplementowanie
BindAsyncmetody dla typu.
TryParse
TryParse ma dwa interfejsy API:
public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);
Poniższy kod jest wyświetlany Point: 12.3, 10.1 za pomocą identyfikatora 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 ma następujące interfejsy API:
public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);
Poniższy kod jest wyświetlany SortBy:xyz, SortDirection:Desc, CurrentPage:99 za pomocą identyfikatora 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
}
Błędy powiązań
Gdy powiązanie zakończy się niepowodzeniem, platforma rejestruje komunikat debugowania i zwraca różne kody stanu do klienta w zależności od trybu awarii.
| Tryb awarii | Typ parametru dopuszczalnego do wartości null | Źródło powiązania | Kod stanu |
|---|---|---|---|
{ParameterType}.TryParse Zwraca false |
yes | route/query/header | 400 |
{ParameterType}.BindAsync Zwraca null |
yes | custom | 400 |
{ParameterType}.BindAsync Zgłasza |
nie ma znaczenia | custom | 500 |
| Nie można wykonać deserializacji treści JSON | nie ma znaczenia | body | 400 |
Nieprawidłowy typ zawartości (nie application/json) |
nie ma znaczenia | body | 415 |
Pierwszeństwo powiązania
Reguły określania źródła powiązania z parametru:
- Jawny atrybut zdefiniowany w parametrze (atrybuty From*) w następującej kolejności:
- Wartości tras:
[FromRoute] - Ciąg zapytania:
[FromQuery] - Nagłówek:
[FromHeader] - Ciało:
[FromBody] - Usługa:
[FromServices]
- Wartości tras:
- Typy specjalne
- Typ parametru ma prawidłową
BindAsyncmetodę. - Typ parametru jest ciągiem lub ma prawidłową
TryParsemetodę.- Jeśli nazwa parametru istnieje w szablonie trasy. W elemecie
app.Map("/todo/{id}", (int id) => {});idjest powiązana z trasą. - Powiązana z ciągu zapytania.
- Jeśli nazwa parametru istnieje w szablonie trasy. W elemecie
- Jeśli typ parametru jest usługą dostarczaną przez iniekcję zależności, używa tej usługi jako źródła.
- Parametr pochodzi z treści.
Dostosowywanie powiązania JSON
Źródło powiązania treści używa System.Text.Json do dese serializacji. Nie można zmienić tego ustawienia domyślnego, ale powiązanie można dostosować przy użyciu innych technik opisanych wcześniej. Aby dostosować opcje serializatora JSON, użyj kodu podobnego do następującego:
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;
}
Powyższy kod ma następujące działanie:
- Konfiguruje domyślne opcje JSON danych wejściowych i wyjściowych.
- Zwraca następujący kod JSON
Podczas publikowania{ "id": 1, "name": "Joe Smith" }{ "Id": 1, "Name": "Joe Smith" }
Odczytywanie treści żądania
Odczytywanie treści żądania bezpośrednio przy użyciu parametru HttpContext lub HttpRequest :
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/uploadstream", async (IConfiguration config, HttpRequest request) =>
{
var filePath = Path.Combine(config["StoredFilesPath"], Path.GetRandomFileName());
await using var writeStream = File.Create(filePath);
await request.BodyReader.CopyToAsync(writeStream);
});
app.Run();
Powyższy kod ma następujące działanie:
- Uzyskuje dostęp do treści żądania przy użyciu polecenia HttpRequest.BodyReader.
- Kopiuje treść żądania do pliku lokalnego.
Responses
Programy obsługi tras obsługują następujące typy zwracanych wartości:
-
IResultoparte — obejmujeTask<IResult>to iValueTask<IResult> -
string- Obejmuje toTask<string>iValueTask<string> -
T(Dowolny inny typ) — obejmujeTask<T>to iValueTask<T>
| Wartość zwracana | Behavior | Content-Type |
|---|---|---|
IResult |
Struktura wywołuje metodę IResult.ExecuteAsync | Decyzja o wdrożeniu IResult |
string |
Struktura zapisuje ciąg bezpośrednio w odpowiedzi | text/plain |
T (Dowolny inny typ) |
Struktura serializuje odpowiedź w formacie JSON | application/json |
Przykładowe wartości zwracane
ciąg zwracane wartości
app.MapGet("/hello", () => "Hello World");
Wartości zwracane w formacie JSON
app.MapGet("/hello", () => new { Message = "Hello World" });
Zwracane wartości IResult
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
W poniższym przykładzie użyto wbudowanych typów wyników, aby dostosować odpowiedź:
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" }));
Niestandardowy kod stanu
app.MapGet("/405", () => Results.StatusCode(405));
Tekst
app.MapGet("/text", () => Results.Text("This is some text"));
Stream
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
app.Run();
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Wbudowane wyniki
Typowe pomocniki wyników istnieją w klasie statycznej Microsoft.AspNetCore.Http.Results .
| Description | Typ odpowiedzi | Kod stanu | API |
|---|---|---|---|
| Pisanie odpowiedzi JSON przy użyciu opcji zaawansowanych | application/json | 200 | Results.Json |
| Pisanie odpowiedzi JSON | application/json | 200 | Results.Ok |
| Pisanie odpowiedzi tekstowej | tekst/zwykły (domyślny), konfigurowalny | 200 | Results.Text |
| Zapisywanie odpowiedzi jako bajtów | application/octet-stream (ustawienie domyślne), konfigurowalne | 200 | Results.Bytes |
| Zapisywanie strumienia bajtów w odpowiedzi | application/octet-stream (ustawienie domyślne), konfigurowalne | 200 | Results.Stream |
| Przesyłanie strumieniowe pliku do odpowiedzi do pobrania za pomocą nagłówka content-disposition | application/octet-stream (ustawienie domyślne), konfigurowalne | 200 | Results.File |
| Ustaw kod stanu na 404 z opcjonalną odpowiedzią JSON | N/A | 404 | Results.NotFound |
| Ustaw kod stanu na 204 | N/A | 204 | Results.NoContent |
| Ustaw kod stanu na 422 z opcjonalną odpowiedzią JSON | N/A | 422 | Results.UnprocessableEntity |
| Ustaw kod stanu na 400 z opcjonalną odpowiedzią JSON | N/A | 400 | Results.BadRequest |
| Ustaw kod stanu na 409 z opcjonalną odpowiedzią JSON | N/A | 409 | Results.Conflict |
| Pisanie obiektu JSON ze szczegółami problemu w odpowiedzi | N/A | 500 (ustawienie domyślne), konfigurowalne | Results.Problem |
| Pisanie obiektu JSON ze szczegółami problemu w odpowiedzi z błędami walidacji | N/A | Nie dotyczy, można skonfigurować | Results.ValidationProblem |
Dostosowywanie wyników
Aplikacje mogą kontrolować odpowiedzi, implementując typ niestandardowy IResult . Poniższy kod jest przykładem typu wyniku HTML:
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);
}
}
Zalecamy dodanie metody rozszerzenia w celu Microsoft.AspNetCore.Http.IResultExtensions zwiększenia możliwości odnajdywania tych niestandardowych wyników.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/html", () => Results.Extensions.Html(@$"<!doctype html>
<html>
<head><title>miniHTML</title></head>
<body>
<h1>Hello World</h1>
<p>The time on the server is {DateTime.Now:O}</p>
</body>
</html>"));
app.Run();
Authorization
Trasy mogą być chronione przy użyciu zasad autoryzacji. Można je zadeklarować za pomocą atrybutu [Authorize] lub przy użyciu RequireAuthorization metody :
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();
Powyższy kod można napisać za pomocą RequireAuthorizationpolecenia :
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
W poniższym przykładzie użyto autoryzacji opartej na zasadach:
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();
Zezwalanie nieuwierzytelnionym użytkownikom na dostęp do punktu końcowego
Ustawienie [AllowAnonymous] umożliwia nieuwierzytelnionym użytkownikom dostęp do punktów końcowych:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Trasy mogą być włączone przez mechanizm CORS przy użyciu zasad MECHANIZMU CORS. Mechanizm CORS można zadeklarować za pomocą atrybutu [EnableCors] lub przy użyciu RequireCors metody . Następujące przykłady umożliwiają mechanizm 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();
Aby uzyskać więcej informacji, zobacz Włączanie żądań między źródłami (CORS) w usłudze ASP.NET Core