Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Note
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 10 dari artikel ini.
Warning
Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.
Dokumen ini:
- Menyediakan referensi cepat untuk API Minimal.
- Ditujukan untuk pengembang berpengalaman. Untuk pengenalan, lihat Tutorial: Membuat API minimal dengan ASP.NET Core.
API Minimal terdiri dari:
WebApplication
Kode berikut dihasilkan oleh templat ASP.NET Core:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Kode sebelumnya dapat dibuat melalui dotnet new web pada baris perintah atau memilih templat Web Kosong di Visual Studio.
Kode berikut membuat WebApplication (app) tanpa secara eksplisit membuat WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create menginisialisasi instans WebApplication baru kelas dengan default yang telah dikonfigurasi sebelumnya.
WebApplication secara otomatis menambahkan middleware berikut dalam aplikasi API Minimal tergantung pada kondisi tertentu:
-
UseDeveloperExceptionPageditambahkan terlebih dahulu ketikaHostingEnvironmentadalah"Development". -
UseRoutingditambahkan kedua jika kode pengguna belum memanggilUseRoutingdan jika ada titik akhir yang dikonfigurasi, misalnyaapp.MapGet. -
UseEndpointsditambahkan di akhir alur middleware jika ada titik akhir yang dikonfigurasi. -
UseAuthenticationditambahkan segera setelahUseRoutingjika kode pengguna belum memanggilUseAuthenticationdan apakahIAuthenticationSchemeProviderdapat terdeteksi di penyedia layanan.IAuthenticationSchemeProviderditambahkan secara default saat menggunakanAddAuthentication, dan layanan terdeteksi menggunakanIServiceProviderIsService. -
UseAuthorizationditambahkan berikutnya jika kode pengguna belum memanggilUseAuthorizationdan apakahIAuthorizationHandlerProviderdapat terdeteksi di penyedia layanan.IAuthorizationHandlerProviderditambahkan secara default saat menggunakanAddAuthorization, dan layanan terdeteksi menggunakanIServiceProviderIsService. - Middleware dan titik akhir yang dikonfigurasi pengguna ditambahkan antara
UseRoutingdanUseEndpoints.
Kode berikut secara efektif adalah apa yang ditambahkan middleware otomatis ke aplikasi:
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 => {});
Dalam beberapa kasus, konfigurasi middleware default tidak benar untuk aplikasi dan memerlukan modifikasi. Misalnya, UseCors harus dipanggil sebelum UseAuthentication dan UseAuthorization. Aplikasi perlu memanggil UseAuthentication dan UseAuthorization jika UseCors dipanggil:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Jika middleware harus dijalankan sebelum pencocokan rute terjadi, UseRouting harus dipanggil dan middleware harus ditempatkan sebelum panggilan ke UseRouting.
UseEndpoints tidak diperlukan dalam kasus ini karena ditambahkan secara otomatis seperti yang dijelaskan sebelumnya:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Saat menambahkan middleware terminal:
- Middleware harus ditambahkan setelah
UseEndpoints. - Aplikasi perlu memanggil
UseRoutingdanUseEndpointssehingga middleware terminal dapat ditempatkan di lokasi yang benar.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Middleware terminal adalah middleware yang berjalan jika tidak ada titik akhir yang menangani permintaan.
Bekerja dengan port
Saat aplikasi web dibuat dengan Visual Studio atau dotnet new, Properties/launchSettings.json file dibuat yang menentukan port yang ditanggapi aplikasi. Di sampel pengaturan port berikut, menjalankan aplikasi dari Visual Studio mengembalikan dialog Unable to connect to web server 'AppName'kesalahan . Visual Studio mengembalikan kesalahan karena mengharapkan port yang ditentukan dalam Properties/launchSettings.json, tetapi aplikasi menggunakan port yang ditentukan oleh app.Run("http://localhost:3000"). Jalankan sampel perubahan port berikut dari baris perintah.
Bagian berikut mengatur port yang direspons aplikasi.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
Dalam kode sebelumnya, aplikasi merespons port 3000.
Beberapa port
Dalam kode berikut, aplikasi merespons port 3000 dan 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Mengatur port dari baris perintah
Perintah berikut membuat aplikasi merespons port 7777:
dotnet run --urls="https://localhost:7777"
Kestrel Jika titik akhir juga dikonfigurasi dalam appsettings.json file, appsettings.json URL yang ditentukan file akan digunakan. Untuk informasi selengkapnya, lihat Kestrel konfigurasi titik akhir
Membaca port dari lingkungan
Kode berikut membaca port dari lingkungan:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Cara yang disukai untuk mengatur port dari lingkungan adalah dengan menggunakan ASPNETCORE_URLS variabel lingkungan, yang ditunjukkan di bagian berikut.
Mengatur port melalui variabel lingkungan ASPNETCORE_URLS
Variabel ASPNETCORE_URLS lingkungan tersedia untuk mengatur port:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS mendukung beberapa URL:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Dengarkan semua antarmuka
Sampel berikut menunjukkan mendengarkan di semua antarmuka
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();
Dengarkan semua antarmuka menggunakan ASPNETCORE_URLS
Sampel sebelumnya dapat menggunakan ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Dengarkan semua antarmuka menggunakan ASPNETCORE_HTTPS_PORTS
Sampel sebelumnya dapat menggunakan ASPNETCORE_HTTPS_PORTS dan ASPNETCORE_HTTP_PORTS.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Untuk informasi selengkapnya, lihat Mengonfigurasi titik akhir untuk server web ASP.NET Core Kestrel
Tentukan HTTPS dengan sertifikat pengembangan
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Untuk informasi selengkapnya tentang sertifikat pengembangan, lihat Mempercayai sertifikat pengembangan ASP.NET Core HTTPS di Windows dan macOS.
Tentukan HTTPS menggunakan sertifikat kustom
Bagian berikut menunjukkan cara menentukan sertifikat kustom menggunakan appsettings.json file dan melalui konfigurasi.
Tentukan sertifikat kustom dengan appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Tentukan sertifikat kustom melalui konfigurasi
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();
Menggunakan API sertifikat
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();
Membaca lingkungan
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();
Untuk informasi lebih lanjut tentang penggunaan lingkungan runtime, lihat lingkungan runtime ASP.NET Core.
Configuration
Kode berikut membaca dari sistem konfigurasi:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Untuk informasi selengkapnya, lihat Konfigurasi di ASP.NET Core
Logging
Kode berikut menulis pesan ke startup aplikasi masuk:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Untuk informasi selengkapnya, lihat Logging di .NET dan ASP.NET Core
Mengakses kontainer Injeksi Dependensi (DI)
Kode berikut menunjukkan cara mendapatkan layanan dari kontainer DI selama pengaktifan aplikasi:
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();
Kode berikut menunjukkan cara mengakses kunci dari kontainer DI menggunakan [FromKeyedServices] atribut :
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.";
}
Untuk informasi selengkapnya tentang DI, lihat Injeksi dependensi di ASP.NET Core.
WebApplicationBuilder
Bagian ini berisi kode sampel menggunakan WebApplicationBuilder.
Mengubah akar konten, nama aplikasi, dan lingkungan
Kode berikut mengatur akar konten, nama aplikasi, dan lingkungan:
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 menginisialisasi instans baru kelas WebApplicationBuilder dengan default yang telah dikonfigurasi sebelumnya.
Untuk informasi selengkapnya, lihat gambaran umum dasar-dasar ASP.NET Core
Mengubah akar konten, nama aplikasi, dan lingkungan dengan menggunakan variabel lingkungan atau baris perintah
Tabel berikut ini memperlihatkan variabel lingkungan dan argumen baris perintah yang digunakan untuk mengubah akar konten, nama aplikasi, dan lingkungan:
| fitur | Variabel lingkungan | Argumen baris perintah |
|---|---|---|
| Nama aplikasi | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Nama lingkungan | ASPNETCORE_ENVIRONMENT | --environment |
| Akar konten | ASPNETCORE_CONTENTROOT | --contentRoot |
Menambahkan penyedia konfigurasi
Contoh berikut menambahkan penyedia konfigurasi INI:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Untuk informasi terperinci, lihat Penyedia konfigurasi file di Konfigurasi di ASP.NET Core.
Membaca konfigurasi
Secara default WebApplicationBuilder , konfigurasi baca dari beberapa sumber, termasuk:
-
appSettings.jsondanappSettings.{environment}.json - Variabel lingkungan
- Baris perintah
Untuk daftar lengkap sumber konfigurasi yang dibaca, lihat Konfigurasi default di Konfigurasi di ASP.NET Core.
Kode berikut membaca HelloKey dari konfigurasi dan menampilkan nilai di / titik akhir. Jika nilai konfigurasi null, "Halo" ditetapkan ke message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Membaca lingkungan
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine($"Running in development.");
}
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Menambahkan penyedia pengelogan
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();
Menambahkan layanan
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();
Menyesuaikan IHostBuilder
Metode ekstensi yang ada di IHostBuilder dapat diakses menggunakan properti 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();
Menyesuaikan IWebHostBuilder
Metode ekstensi pada IWebHostBuilder dapat diakses menggunakan properti 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();
Mengubah akar web
Secara default, akar web relatif terhadap akar konten di wwwroot folder. Direktori root web adalah tempat Middleware File Statis mencari file statis. Akar web dapat diubah dengan WebHostOptions, baris perintah, atau dengan UseWebRoot metode :
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Kontainer injeksi dependensi kustom (DI)
Contoh berikut menggunakan 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();
Tambahkan Middleware
Middleware ASP.NET Core yang ada dapat dikonfigurasi pada WebApplication:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Untuk informasi selengkapnya, lihat ASP.NET Core Middleware
Halaman pengecualian pengembang
WebApplication.CreateBuilder menginisialisasi instans WebApplicationBuilder baru kelas dengan default yang telah dikonfigurasi sebelumnya. Halaman pengecualian pengembang diaktifkan dalam default yang telah dikonfigurasi sebelumnya. Saat kode berikut dijalankan di lingkungan pengembangan, navigasi untuk / merender halaman ramah yang menunjukkan pengecualian.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () =>
{
throw new InvalidOperationException("Oops, the '/' route has thrown an exception.");
});
app.Run();
ASP.NET Core Middleware
Tabel berikut mencantumkan beberapa middleware yang sering digunakan dengan API Minimal.
| Middleware | Description | API |
|---|---|---|
| Authentication | Menyediakan dukungan autentikasi. | UseAuthentication |
| Authorization | Menyediakan dukungan otorisasi. | UseAuthorization |
| CORS | Mengonfigurasi Cross-Origin Resource Sharing. | UseCors |
| Handler Pengecualian | Secara global menangani pengecualian yang dilemparkan oleh alur middleware. | UseExceptionHandler |
| Header yang Diteruskan | Meneruskan header yang diproksi ke permintaan saat ini. | UseForwardedHeaders |
| Pengalihan HTTPS | Mengalihkan semua permintaan HTTP ke HTTPS. | UseHttpsRedirection |
| Http Strict Transport Security (HSTS) | Middleware peningkatan keamanan yang menambahkan header respons khusus. | UseHsts |
| Pengelogan Permintaan | Menyediakan dukungan untuk mencatat permintaan dan respons HTTP. | UseHttpLogging |
| Batas Waktu Permintaan | Menyediakan dukungan untuk mengonfigurasi batas waktu permintaan, default global, dan per titik akhir. | UseRequestTimeouts |
| Pengelogan Permintaan W3C | Menyediakan dukungan untuk mencatat permintaan dan respons HTTP dalam format W3C. | UseW3CLogging |
| Penembolokan Respons | Menyediakan dukungan untuk respons penembolokan. | UseResponseCaching |
| Kompresi Respons | Memberikan dukungan untuk memadatkan respons. | UseResponseCompression |
| Session | Menyediakan dukungan untuk mengelola sesi pengguna. | UseSession |
| File Statis | Menyediakan dukungan untuk menyajikan file statis dan penjelajahan direktori. | UseStaticFiles, UseFileServer |
| WebSockets | Mengaktifkan protokol WebSocket. | UseWebSockets |
Bagian berikut mencakup penanganan permintaan: perutean, pengikatan parameter, dan respons.
Routing
Dukungan yang dikonfigurasi WebApplication dan Map{Verb} di mana MapMethods adalah metode HTTP camel-cased seperti {Verb}, , Get, Postatau Put: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();
Argumen Delegate yang diteruskan ke metode ini disebut "penangan rute".
Penangan Rute
Penangan rute adalah metode yang dijalankan saat rute cocok. Penangan rute dapat berupa ekspresi lambda, fungsi lokal, metode instans, atau metode statis. Penangan rute bisa sinkron atau asinkron.
Ekspresi 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();
Fungsi lokal
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Metode instans
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";
}
}
Metode statis
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";
}
}
Titik akhir yang ditentukan di luar Program.cs
API minimal tidak harus berada di 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" });
});
}
}
Lihat juga Merutekan grup nanti di artikel ini.
Titik akhir dan pembuatan tautan bernama
Titik akhir dapat diberikan nama untuk menghasilkan URL ke titik akhir. Menggunakan titik akhir bernama menghindari harus melakukan jalur kode keras di aplikasi:
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();
Kode sebelumnya ditampilkan The link to the hello route is /hello dari / titik akhir.
CATATAN: Nama titik akhir peka huruf besar/kecil.
Nama titik akhir:
- Harus unik secara global.
- Digunakan sebagai id operasi OpenAPI saat dukungan OpenAPI diaktifkan. Untuk informasi selengkapnya, lihat OpenAPI.
Parameter Rute
Parameter rute dapat ditangkap sebagai bagian dari definisi pola rute:
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();
Kode sebelumnya dikembalikan The user id is 3 and book id is 7 dari URI /users/3/books/7.
Handler rute dapat mendeklarasikan parameter yang akan diambil. Ketika permintaan dibuat ke rute dengan parameter yang dinyatakan untuk diambil, parameter diurai dan diteruskan ke handler. Ini memudahkan untuk menangkap nilai dengan cara yang aman. Dalam kode sebelumnya, userId dan bookId keduanya int.
Dalam kode sebelumnya, jika salah satu nilai rute tidak dapat dikonversi ke int, pengecualian akan dilemparkan. Permintaan /users/hello/books/3 GET melemparkan pengecualian berikut:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Kartubebas dan tangkap semua rute
Berikut ini menangkap semua rute yang Routing to hello dikembalikan dari titik akhir '/posts/hello':
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Batasan rute
Batasan rute membatasi perilaku pencocokan rute.
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();
Tabel berikut menunjukkan templat rute sebelumnya dan perilakunya:
| Templat Rute | Contoh URI yang Cocok |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Untuk informasi selengkapnya, lihat Referensi batasan rute dalam Perutean di ASP.NET Core.
Grup rute
Metode MapGroup ekstensi membantu mengatur grup titik akhir dengan awalan umum. Ini mengurangi kode berulang dan memungkinkan untuk menyesuaikan seluruh grup titik akhir dengan satu panggilan ke metode seperti RequireAuthorization dan WithMetadata yang menambahkan metadata titik akhir.
Misalnya, kode berikut membuat dua grup titik akhir serupa:
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;
}
Dalam skenario ini, Anda dapat menggunakan alamat relatif untuk Location header dalam hasil 201 Created :
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Grup titik akhir pertama hanya akan cocok dengan permintaan yang diawali dan /public/todos dapat diakses tanpa autentikasi apa pun. Grup titik akhir kedua hanya akan cocok dengan permintaan yang diawali dan /private/todos memerlukan autentikasi.
Pabrik QueryPrivateTodos filter titik akhir adalah fungsi lokal yang memodifikasi parameter handler TodoDb rute untuk memungkinkan mengakses dan menyimpan data todo privat.
Grup rute juga mendukung grup berlapis dan pola awalan kompleks dengan parameter rute dan batasan. Dalam contoh berikut, dan handler rute yang dipetakan user ke grup dapat menangkap {org} parameter rute dan {group} yang ditentukan dalam awalan grup luar.
Awalan juga dapat kosong. Ini dapat berguna untuk menambahkan metadata titik akhir atau filter ke sekelompok titik akhir tanpa mengubah pola rute.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Menambahkan filter atau metadata ke grup berulah dengan cara yang sama seperti menambahkannya satu per satu ke setiap titik akhir sebelum menambahkan filter atau metadata tambahan yang mungkin telah ditambahkan ke grup dalam atau titik akhir tertentu.
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);
});
Dalam contoh di atas, filter luar akan mencatat permintaan masuk sebelum filter dalam meskipun ditambahkan kedua. Karena filter diterapkan ke grup yang berbeda, urutan yang ditambahkan relatif satu sama lain tidak masalah. Filter pesanan ditambahkan tidak masalah jika diterapkan ke grup yang sama atau titik akhir tertentu.
Permintaan untuk /outer/inner/ mencatat hal berikut:
/outer group filter
/inner group filter
MapGet filter
Pengikatan parameter
Pengikatan parameter adalah proses mengonversi data permintaan menjadi parameter yang diekspresikan dengan kuat yang dinyatakan oleh penangan rute. Sumber pengikatan menentukan dari mana parameter terikat. Sumber pengikatan dapat eksplisit atau disimpulkan berdasarkan metode HTTP dan jenis parameter.
Sumber pengikatan yang didukung:
- Nilai rute
- Untai kueri
- Header
- Isi (sebagai JSON)
- Nilai formulir
- Layanan yang disediakan oleh injeksi dependensi
- Custom
Handler rute GET berikut menggunakan beberapa sumber pengikatan parameter ini:
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 { }
Fitur pengikatan parameter penting
-
Pengikatan eksplisit: Gunakan atribut seperti
[FromRoute], ,[FromQuery],[FromHeader][FromBody],[FromForm], dan[FromServices]untuk secara eksplisit menentukan sumber pengikatan. -
Pengikatan formulir: Mengikat nilai formulir menggunakan
[FromForm]atribut, termasuk dukungan untukIFormFiledanIFormFileCollectionuntuk unggahan file. - Jenis kompleks: Ikat ke koleksi dan jenis kompleks dari formulir, string kueri, dan header.
-
Pengikatan kustom: Terapkan logika pengikatan kustom menggunakan
TryParse,BindAsync, atauIBindableFromHttpContext<T>antarmuka. - Parameter opsional: Mendukung jenis nullable dan nilai default untuk parameter opsional.
- Injeksi dependensi: Parameter secara otomatis dihubungkan dari layanan yang terdaftar dalam kontainer DI tersebut.
-
Jenis khusus: Pengikatan otomatis untuk
HttpContext, ,HttpRequest,HttpResponseCancellationToken,ClaimsPrincipal,Stream, danPipeReader.
Pelajari lebih lanjut: Untuk informasi terperinci tentang pengikatan parameter termasuk skenario lanjutan, validasi, prioritas pengikatan, dan pemecahan masalah, lihat Pengikatan parameter dalam aplikasi API Minimal.
Deserialisasi Json+PipeReader dalam API minimal
Mulai dari .NET 10, area fungsional berikut dari ASP.NET Core menggunakan kelebihan beban JsonSerializer.DeserializeAsync berdasarkan PipeReader alih-alih Stream:
- Minimal API (pengikatan parameter, membaca isi permintaan)
- MVC (pemformat input, model)
- Metode HttpRequestJsonExtensions Ekstensi untuk membaca isi permintaan sebagai JSON.
Untuk sebagian besar aplikasi, transisi dari Stream ke PipeReader memberikan performa yang lebih baik tanpa memerlukan perubahan dalam kode aplikasi. Tetapi jika aplikasi Anda memiliki pengonversi kustom, pengonversi mungkin tidak menangani Utf8JsonReader.HasValueSequence dengan benar. Jika tidak, hasilnya bisa berupa kesalahan seperti ArgumentOutOfRangeException atau data yang hilang saat deserialisasi. Anda memiliki opsi berikut untuk membuat pengonversi Anda berfungsi tanpa kesalahan terkait PipeReader.
Opsi 1: Solusi sementara
Solusi cepatnya adalah kembali menggunakan Stream tanpa dukungan PipeReader. Untuk menerapkan opsi ini, atur sakelar AppContext "Microsoft.AspNetCore.UseStreamBasedJsonParsing" ke "true". Kami menyarankan agar Anda melakukan ini hanya sebagai solusi sementara, dan perbarui pengonversi Anda untuk mendukung HasValueSequence sesegera mungkin. Sakelar mungkin dihapus di .NET 11. Satu-satunya tujuannya adalah untuk memberi pengembang waktu untuk memperbarui konverter mereka.
Opsi 2: Perbaikan cepat untuk JsonConverter implementasi
Untuk perbaikan ini, Anda mengalokasikan array dari ReadOnlySequence. Contoh ini menunjukkan seperti apa kode tersebut:
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
// previous code
}
Opsi 3: Perbaikan yang lebih rumit tetapi berkinerja lebih baik
Perbaikan ini melibatkan pengaturan jalur kode terpisah untuk ReadOnlySequence penanganan:
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
}
}
Untuk mengetahui informasi selengkapnya, lihat
Dukungan validasi dalam API Minimal
Mengaktifkan validasi memungkinkan runtime ASP.NET Core untuk melakukan validasi yang ditentukan pada:
- Query
- Header
- Isi dari permintaan
Validasi didefinisikan menggunakan atribut di DataAnnotations namespace.
Saat parameter ke titik akhir API Minimal adalah kelas atau jenis rekaman, atribut validasi akan diterapkan secara otomatis. Contohnya:
public record Product(
[Required] string Name,
[Range(1, 1000)] int Quantity);
Pengembang menyesuaikan perilaku sistem validasi dengan:
- Membuat implementasi atribut kustom
[Validation]. - Menerapkan
IValidatableObjectantarmuka untuk logika validasi yang kompleks.
Jika validasi gagal, runtime mengembalikan respons 400 - Permintaan Buruk dengan detail kesalahan validasi.
Mengaktifkan dukungan validasi bawaan untuk API Minimal
Aktifkan dukungan validasi bawaan untuk API Minimal dengan memanggil AddValidation metode ekstensi untuk mendaftarkan layanan yang diperlukan dalam kontainer layanan untuk aplikasi Anda:
builder.Services.AddValidation();
Implementasi secara otomatis menemukan jenis yang didefinisikan dalam handler API Minimal atau sebagai jenis dasar jenis yang ditentukan dalam handler API Minimal. Filter titik akhir melakukan validasi pada jenis ini dan ditambahkan untuk setiap titik akhir.
Validasi dapat dinonaktifkan untuk titik akhir tertentu dengan menggunakan DisableValidation metode ekstensi, seperti dalam contoh berikut:
app.MapPost("/products",
([EvenNumber(ErrorMessage = "Product ID must be even")] int productId, [Required] string name)
=> TypedResults.Ok(productId))
.DisableValidation();
Menyesuaikan respons kesalahan validasi menggunakan IProblemDetailsService
Sesuaikan respons kesalahan dari logika validasi API Minimal dengan IProblemDetailsService implementasi. Daftarkan layanan ini dalam kumpulan layanan aplikasi Anda untuk mengaktifkan respons kesalahan yang lebih konsisten dan spesifik pengguna. Dukungan untuk validasi API Minimal diperkenalkan di ASP.NET Core di .NET 10.
Untuk menerapkan respons kesalahan validasi kustom:
- Menerapkan IProblemDetailsService atau menggunakan implementasi default
- Mendaftarkan layanan dalam kontainer DI
- Sistem validasi secara otomatis menggunakan layanan terdaftar untuk memformat respons kesalahan validasi
Untuk informasi selengkapnya tentang menyesuaikan respons kesalahan validasi dengan IProblemDetailsService, lihat Membuat respons di aplikasi API Minimal.
Responses
Handler rute mendukung jenis nilai pengembalian berikut:
-
IResultberbasis - Ini termasukTask<IResult>danValueTask<IResult> -
string- Ini termasukTask<string>danValueTask<string> -
T(Jenis lainnya) - Ini termasukTask<T>danValueTask<T>
| Mengembalikan nilai | Behavior | Content-Type |
|---|---|---|
IResult |
Kerangka kerja memanggil IResult.ExecuteAsync | Diputuskan IResult oleh implementasi |
string |
Kerangka kerja menulis string langsung ke respons | text/plain |
T (Jenis lainnya) |
Kerangka kerja JSON-menserialisasikan respons | application/json |
Untuk panduan yang lebih mendalam untuk merutekan nilai pengembalian handler, lihat Membuat respons di aplikasi API Minimal
Contoh nilai pengembalian
nilai pengembalian string
app.MapGet("/hello", () => "Hello World");
JSON mengembalikan nilai
app.MapGet("/hello", () => new { Message = "Hello World" });
Mengembalikan TypedResults
Kode berikut mengembalikan TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
TypedResults Mengembalikan lebih disukai untuk mengembalikan Results. Untuk informasi selengkapnya, lihat TypedResults vs Results.
Nilai pengembalian IResult
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
Contoh berikut menggunakan jenis hasil bawaan untuk mengkustomisasi respons:
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" }));
Kode Status Kustom
app.MapGet("/405", () => Results.StatusCode(405));
Text
app.MapGet("/text", () => Results.Text("This is some text"));
Stream
var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
Lihat Membuat respons di aplikasi API Minimal untuk contoh selengkapnya.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Hasil bawaan
Pembantu hasil umum ada di Results kelas statis dan TypedResults .
TypedResults Mengembalikan lebih disukai untuk mengembalikan Results. Untuk informasi selengkapnya, lihat TypedResults vs Results.
Mengubah Header
Gunakan objek HttpResponse untuk mengubah header respons:
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";
});
Menyesuaikan hasil
Aplikasi dapat mengontrol respons dengan menerapkan jenis kustom IResult . Kode berikut adalah contoh jenis hasil 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);
}
}
Sebaiknya tambahkan metode ekstensi untuk Microsoft.AspNetCore.Http.IResultExtensions membuat hasil kustom ini lebih dapat ditemukan.
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();
Hasil yang ditik
Antarmuka IResult dapat mewakili nilai yang dikembalikan dari API Minimal yang tidak menggunakan dukungan implisit untuk JSON yang menserialisasikan objek yang dikembalikan ke respons HTTP. Kelas Hasil statis digunakan untuk membuat berbagai IResult objek yang mewakili berbagai jenis respons. Misalnya, mengatur kode status respons atau mengalihkan ke URL lain.
Jenis yang IResult diterapkan bersifat publik, memungkinkan pernyataan jenis saat pengujian. Contohnya:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Anda dapat melihat jenis pengembalian metode yang sesuai pada kelas TypedResults statis untuk menemukan jenis publik IResult yang benar untuk ditransmisikan.
Lihat Membuat respons di aplikasi API Minimal untuk contoh selengkapnya.
Filters
Untuk informasi selengkapnya, lihat Filter di aplikasi API Minimal.
Authorization
Rute dapat dilindungi menggunakan kebijakan otorisasi. Ini dapat dideklarasikan melalui [Authorize] atribut atau dengan menggunakan RequireAuthorization metode :
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();
Kode sebelumnya dapat ditulis dengan RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
Sampel berikut menggunakan otorisasi berbasis kebijakan:
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();
Perbolehkan pengguna yang tidak diaauthenticated untuk mengakses titik akhir
memungkinkan [AllowAnonymous] pengguna yang tidak diaauthenticated untuk mengakses titik akhir:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Rute dapat diaktifkan CORS menggunakan kebijakan CORS. CORS dapat dideklarasikan melalui [EnableCors] atribut atau dengan menggunakan RequireCors metode . Sampel berikut mengaktifkan 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();
Untuk informasi selengkapnya, lihat Mengaktifkan Permintaan Lintas Asal (CORS) di ASP.NET Core
ValidateScopes dan ValidateOnBuild
ValidateScopes dan ValidateOnBuild diaktifkan secara default di lingkungan Pengembangan tetapi dinonaktifkan di lingkungan lain.
Ketika ValidateOnBuild adalah true, kontainer DI memvalidasi konfigurasi layanan pada waktu build. Jika konfigurasi layanan tidak valid, build gagal pada startup aplikasi, bukan pada runtime saat layanan diminta.
Ketika ValidateScopes adalah true, kontainer DI memvalidasi bahwa layanan tercakup tidak diselesaikan dari cakupan akar. Menyelesaikan layanan tercakup dari cakupan akar dapat mengakibatkan kebocoran memori karena layanan dipertahankan dalam memori lebih lama dari cakupan permintaan.
ValidateScopes dan ValidateOnBuild salah secara default dalam mode non-Pengembangan karena alasan performa.
Kode berikut menunjukkan ValidateScopes diaktifkan secara default dalam mode pengembangan tetapi dinonaktifkan dalam mode rilis:
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 { }
Kode berikut menunjukkan ValidateOnBuild diaktifkan secara default dalam mode pengembangan tetapi dinonaktifkan dalam mode rilis:
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 { }
Kode berikut menonaktifkan ValidateScopes dan ValidateOnBuild dalam 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;
});
}
Lihat juga
- Referensi cepat API minimal
- Hasilkan dokumen OpenAPI
- Membuat respons dalam aplikasi API Minimal
- Filter di aplikasi API Minimal
- Menangani kesalahan di API inti ASP.NET
- Autentikasi dan otorisasi dalam API minimal
- Menguji aplikasi API Minimal
- Perutean sirkuit pendek
- Identity Titik akhir API
- Dukungan kontainer injeksi dependensi layanan yang dikunci
- Melihat di balik layar titik akhir API Minimal
- Mengatur API Minimal Inti ASP.NET
- Diskusi validasi yang lancar di GitHub
Dokumen ini:
- Menyediakan referensi cepat untuk API minimal.
- Ditujukan untuk pengembang berpengalaman. Untuk pengenalan, lihat Tutorial: Membuat API minimal dengan ASP.NET Core.
API minimal terdiri dari:
WebApplication
Kode berikut dihasilkan oleh templat ASP.NET Core:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Kode sebelumnya dapat dibuat melalui dotnet new web pada baris perintah atau memilih templat Web Kosong di Visual Studio.
Kode berikut membuat WebApplication (app) tanpa secara eksplisit membuat WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create menginisialisasi instans WebApplication baru kelas dengan default yang telah dikonfigurasi sebelumnya.
WebApplication secara otomatis menambahkan middleware berikut dalam aplikasi API Minimal tergantung pada kondisi tertentu:
-
UseDeveloperExceptionPageditambahkan terlebih dahulu ketikaHostingEnvironmentadalah"Development". -
UseRoutingditambahkan kedua jika kode pengguna belum memanggilUseRoutingdan jika ada titik akhir yang dikonfigurasi, misalnyaapp.MapGet. -
UseEndpointsditambahkan di akhir alur middleware jika ada titik akhir yang dikonfigurasi. -
UseAuthenticationditambahkan segera setelahUseRoutingjika kode pengguna belum memanggilUseAuthenticationdan apakahIAuthenticationSchemeProviderdapat terdeteksi di penyedia layanan.IAuthenticationSchemeProviderditambahkan secara default saat menggunakanAddAuthentication, dan layanan terdeteksi menggunakanIServiceProviderIsService. -
UseAuthorizationditambahkan berikutnya jika kode pengguna belum memanggilUseAuthorizationdan apakahIAuthorizationHandlerProviderdapat terdeteksi di penyedia layanan.IAuthorizationHandlerProviderditambahkan secara default saat menggunakanAddAuthorization, dan layanan terdeteksi menggunakanIServiceProviderIsService. - Middleware dan titik akhir yang dikonfigurasi pengguna ditambahkan antara
UseRoutingdanUseEndpoints.
Kode berikut secara efektif adalah apa yang ditambahkan middleware otomatis ke aplikasi:
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 => {});
Dalam beberapa kasus, konfigurasi middleware default tidak benar untuk aplikasi dan memerlukan modifikasi. Misalnya, UseCors harus dipanggil sebelum UseAuthentication dan UseAuthorization. Aplikasi perlu memanggil UseAuthentication dan UseAuthorization jika UseCors dipanggil:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Jika middleware harus dijalankan sebelum pencocokan rute terjadi, UseRouting harus dipanggil dan middleware harus ditempatkan sebelum panggilan ke UseRouting.
UseEndpoints tidak diperlukan dalam kasus ini karena ditambahkan secara otomatis seperti yang dijelaskan sebelumnya:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Saat menambahkan middleware terminal:
- Middleware harus ditambahkan setelah
UseEndpoints. - Aplikasi perlu memanggil
UseRoutingdanUseEndpointssehingga middleware terminal dapat ditempatkan di lokasi yang benar.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Middleware terminal adalah middleware yang berjalan jika tidak ada titik akhir yang menangani permintaan.
Bekerja dengan port
Saat aplikasi web dibuat dengan Visual Studio atau dotnet new, Properties/launchSettings.json file dibuat yang menentukan port yang ditanggapi aplikasi. Di sampel pengaturan port berikut, menjalankan aplikasi dari Visual Studio mengembalikan dialog Unable to connect to web server 'AppName'kesalahan . Visual Studio mengembalikan kesalahan karena mengharapkan port yang ditentukan dalam Properties/launchSettings.json, tetapi aplikasi menggunakan port yang ditentukan oleh app.Run("http://localhost:3000"). Jalankan sampel perubahan port berikut dari baris perintah.
Bagian berikut mengatur port yang direspons aplikasi.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
Dalam kode sebelumnya, aplikasi merespons port 3000.
Beberapa port
Dalam kode berikut, aplikasi merespons port 3000 dan 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Mengatur port dari baris perintah
Perintah berikut membuat aplikasi merespons port 7777:
dotnet run --urls="https://localhost:7777"
Kestrel Jika titik akhir juga dikonfigurasi dalam appsettings.json file, appsettings.json URL yang ditentukan file akan digunakan. Untuk informasi selengkapnya, lihat Kestrel konfigurasi titik akhir
Membaca port dari lingkungan
Kode berikut membaca port dari lingkungan:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Cara yang disukai untuk mengatur port dari lingkungan adalah dengan menggunakan ASPNETCORE_URLS variabel lingkungan, yang ditunjukkan di bagian berikut.
Mengatur port melalui variabel lingkungan ASPNETCORE_URLS
Variabel ASPNETCORE_URLS lingkungan tersedia untuk mengatur port:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS mendukung beberapa URL:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Dengarkan semua antarmuka
Sampel berikut menunjukkan mendengarkan di semua antarmuka
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();
Dengarkan semua antarmuka menggunakan ASPNETCORE_URLS
Sampel sebelumnya dapat menggunakan ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Dengarkan semua antarmuka menggunakan ASPNETCORE_HTTPS_PORTS
Sampel sebelumnya dapat menggunakan ASPNETCORE_HTTPS_PORTS dan ASPNETCORE_HTTP_PORTS.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Untuk informasi selengkapnya, lihat Mengonfigurasi titik akhir untuk server web ASP.NET Core Kestrel
Tentukan HTTPS dengan sertifikat pengembangan
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Untuk informasi selengkapnya tentang sertifikat pengembangan, lihat Mempercayai sertifikat pengembangan ASP.NET Core HTTPS di Windows dan macOS.
Tentukan HTTPS menggunakan sertifikat kustom
Bagian berikut menunjukkan cara menentukan sertifikat kustom menggunakan appsettings.json file dan melalui konfigurasi.
Tentukan sertifikat kustom dengan appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Tentukan sertifikat kustom melalui konfigurasi
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();
Menggunakan API sertifikat
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();
Membaca lingkungan
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();
Untuk informasi lebih lanjut tentang penggunaan lingkungan runtime, lihat lingkungan runtime ASP.NET Core.
Configuration
Kode berikut membaca dari sistem konfigurasi:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Untuk informasi selengkapnya, lihat Konfigurasi di ASP.NET Core
Logging
Kode berikut menulis pesan ke startup aplikasi masuk:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Untuk informasi selengkapnya, lihat Logging di .NET dan ASP.NET Core
Mengakses kontainer Injeksi Dependensi (DI)
Kode berikut menunjukkan cara mendapatkan layanan dari kontainer DI selama pengaktifan aplikasi:
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();
Kode berikut menunjukkan cara mengakses kunci dari kontainer DI menggunakan [FromKeyedServices] atribut :
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.";
}
Untuk informasi selengkapnya tentang DI, lihat Injeksi dependensi di ASP.NET Core.
WebApplicationBuilder
Bagian ini berisi kode sampel menggunakan WebApplicationBuilder.
Mengubah akar konten, nama aplikasi, dan lingkungan
Kode berikut mengatur akar konten, nama aplikasi, dan lingkungan:
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 menginisialisasi instans baru kelas WebApplicationBuilder dengan default yang telah dikonfigurasi sebelumnya.
Untuk informasi selengkapnya, lihat gambaran umum dasar-dasar ASP.NET Core
Mengubah akar konten, nama aplikasi, dan lingkungan dengan menggunakan variabel lingkungan atau baris perintah
Tabel berikut ini memperlihatkan variabel lingkungan dan argumen baris perintah yang digunakan untuk mengubah akar konten, nama aplikasi, dan lingkungan:
| fitur | Variabel lingkungan | Argumen baris perintah |
|---|---|---|
| Nama aplikasi | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Nama lingkungan | ASPNETCORE_ENVIRONMENT | --environment |
| Akar konten | ASPNETCORE_CONTENTROOT | --contentRoot |
Menambahkan penyedia konfigurasi
Contoh berikut menambahkan penyedia konfigurasi INI:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Untuk informasi terperinci, lihat Penyedia konfigurasi file di Konfigurasi di ASP.NET Core.
Membaca konfigurasi
Secara default WebApplicationBuilder , konfigurasi baca dari beberapa sumber, termasuk:
-
appSettings.jsondanappSettings.{environment}.json - Variabel lingkungan
- Baris perintah
Untuk daftar lengkap sumber konfigurasi yang dibaca, lihat Konfigurasi default di Konfigurasi di ASP.NET Core.
Kode berikut membaca HelloKey dari konfigurasi dan menampilkan nilai di / titik akhir. Jika nilai konfigurasi null, "Halo" ditetapkan ke message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Membaca lingkungan
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine($"Running in development.");
}
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Menambahkan penyedia pengelogan
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();
Menambahkan layanan
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();
Menyesuaikan IHostBuilder
Metode ekstensi yang ada di IHostBuilder dapat diakses menggunakan properti 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();
Menyesuaikan IWebHostBuilder
Metode ekstensi pada IWebHostBuilder dapat diakses menggunakan properti 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();
Mengubah akar web
Secara default, akar web relatif terhadap akar konten di wwwroot folder. Direktori root web adalah tempat Middleware File Statis mencari file statis. Akar web dapat diubah dengan WebHostOptions, baris perintah, atau dengan UseWebRoot metode :
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Kontainer injeksi dependensi kustom (DI)
Contoh berikut menggunakan 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();
Tambahkan Middleware
Middleware ASP.NET Core yang ada dapat dikonfigurasi pada WebApplication:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Untuk informasi selengkapnya, lihat ASP.NET Core Middleware
Halaman pengecualian pengembang
WebApplication.CreateBuilder menginisialisasi instans WebApplicationBuilder baru kelas dengan default yang telah dikonfigurasi sebelumnya. Halaman pengecualian pengembang diaktifkan dalam default yang telah dikonfigurasi sebelumnya. Saat kode berikut dijalankan di lingkungan pengembangan, navigasi untuk / merender halaman ramah yang menunjukkan pengecualian.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () =>
{
throw new InvalidOperationException("Oops, the '/' route has thrown an exception.");
});
app.Run();
ASP.NET Core Middleware
Tabel berikut mencantumkan beberapa middleware yang sering digunakan dengan API minimal.
| Middleware | Description | API |
|---|---|---|
| Authentication | Menyediakan dukungan autentikasi. | UseAuthentication |
| Authorization | Menyediakan dukungan otorisasi. | UseAuthorization |
| CORS | Mengonfigurasi Cross-Origin Resource Sharing. | UseCors |
| Handler Pengecualian | Secara global menangani pengecualian yang dilemparkan oleh alur middleware. | UseExceptionHandler |
| Header yang Diteruskan | Meneruskan header yang diproksi ke permintaan saat ini. | UseForwardedHeaders |
| Pengalihan HTTPS | Mengalihkan semua permintaan HTTP ke HTTPS. | UseHttpsRedirection |
| Http Strict Transport Security (HSTS) | Middleware peningkatan keamanan yang menambahkan header respons khusus. | UseHsts |
| Pengelogan Permintaan | Menyediakan dukungan untuk mencatat permintaan dan respons HTTP. | UseHttpLogging |
| Batas Waktu Permintaan | Menyediakan dukungan untuk mengonfigurasi batas waktu permintaan, default global, dan per titik akhir. | UseRequestTimeouts |
| Pengelogan Permintaan W3C | Menyediakan dukungan untuk mencatat permintaan dan respons HTTP dalam format W3C. | UseW3CLogging |
| Penembolokan Respons | Menyediakan dukungan untuk respons penembolokan. | UseResponseCaching |
| Kompresi Respons | Memberikan dukungan untuk memadatkan respons. | UseResponseCompression |
| Session | Menyediakan dukungan untuk mengelola sesi pengguna. | UseSession |
| File Statis | Menyediakan dukungan untuk menyajikan file statis dan penjelajahan direktori. | UseStaticFiles, UseFileServer |
| WebSockets | Mengaktifkan protokol WebSocket. | UseWebSockets |
Bagian berikut mencakup penanganan permintaan: perutean, pengikatan parameter, dan respons.
Routing
Dukungan yang dikonfigurasi WebApplicationMap{Verb} dan MapMethods di mana {Verb} adalah metode HTTP camel-cased seperti Get, , PostPut atau 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();
Argumen Delegate yang diteruskan ke metode ini disebut "penangan rute".
Penangan Rute
Penangan rute adalah metode yang dijalankan saat rute cocok. Penangan rute dapat berupa ekspresi lambda, fungsi lokal, metode instans, atau metode statis. Penangan rute bisa sinkron atau asinkron.
Ekspresi 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();
Fungsi lokal
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Metode instans
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";
}
}
Metode statis
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";
}
}
Titik akhir yang ditentukan di luar Program.cs
API minimal tidak harus berada di 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" });
});
}
}
Lihat juga Merutekan grup nanti di artikel ini.
Titik akhir dan pembuatan tautan bernama
Titik akhir dapat diberikan nama untuk menghasilkan URL ke titik akhir. Menggunakan titik akhir bernama menghindari harus melakukan jalur kode keras di aplikasi:
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();
Kode sebelumnya ditampilkan The link to the hello route is /hello dari / titik akhir.
CATATAN: Nama titik akhir peka huruf besar/kecil.
Nama titik akhir:
- Harus unik secara global.
- Digunakan sebagai id operasi OpenAPI saat dukungan OpenAPI diaktifkan. Untuk informasi selengkapnya, lihat OpenAPI.
Parameter Rute
Parameter rute dapat ditangkap sebagai bagian dari definisi pola rute:
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();
Kode sebelumnya dikembalikan The user id is 3 and book id is 7 dari URI /users/3/books/7.
Handler rute dapat mendeklarasikan parameter yang akan diambil. Ketika permintaan dibuat ke rute dengan parameter yang dinyatakan untuk diambil, parameter diurai dan diteruskan ke handler. Ini memudahkan untuk menangkap nilai dengan cara yang aman. Dalam kode sebelumnya, userId dan bookId keduanya int.
Dalam kode sebelumnya, jika salah satu nilai rute tidak dapat dikonversi ke int, pengecualian akan dilemparkan. Permintaan /users/hello/books/3 GET melemparkan pengecualian berikut:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Kartubebas dan tangkap semua rute
Berikut ini menangkap semua rute yang Routing to hello dikembalikan dari titik akhir '/posts/hello':
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Batasan rute
Batasan rute membatasi perilaku pencocokan rute.
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();
Tabel berikut menunjukkan templat rute sebelumnya dan perilakunya:
| Templat Rute | Contoh URI yang Cocok |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Untuk informasi selengkapnya, lihat Referensi batasan rute dalam Perutean di ASP.NET Core.
Grup rute
Metode MapGroup ekstensi membantu mengatur grup titik akhir dengan awalan umum. Ini mengurangi kode berulang dan memungkinkan untuk menyesuaikan seluruh grup titik akhir dengan satu panggilan ke metode seperti RequireAuthorization dan WithMetadata yang menambahkan metadata titik akhir.
Misalnya, kode berikut membuat dua grup titik akhir serupa:
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;
}
Dalam skenario ini, Anda dapat menggunakan alamat relatif untuk Location header dalam hasil 201 Created :
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Grup titik akhir pertama hanya akan cocok dengan permintaan yang diawali dan /public/todos dapat diakses tanpa autentikasi apa pun. Grup titik akhir kedua hanya akan cocok dengan permintaan yang diawali dan /private/todos memerlukan autentikasi.
Pabrik QueryPrivateTodos filter titik akhir adalah fungsi lokal yang memodifikasi parameter handler TodoDb rute untuk memungkinkan mengakses dan menyimpan data todo privat.
Grup rute juga mendukung grup berlapis dan pola awalan kompleks dengan parameter rute dan batasan. Dalam contoh berikut, dan handler rute yang dipetakan user ke grup dapat menangkap {org} parameter rute dan {group} yang ditentukan dalam awalan grup luar.
Awalan juga dapat kosong. Ini dapat berguna untuk menambahkan metadata titik akhir atau filter ke sekelompok titik akhir tanpa mengubah pola rute.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Menambahkan filter atau metadata ke grup berulah dengan cara yang sama seperti menambahkannya satu per satu ke setiap titik akhir sebelum menambahkan filter atau metadata tambahan yang mungkin telah ditambahkan ke grup dalam atau titik akhir tertentu.
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);
});
Dalam contoh di atas, filter luar akan mencatat permintaan masuk sebelum filter dalam meskipun ditambahkan kedua. Karena filter diterapkan ke grup yang berbeda, urutan yang ditambahkan relatif satu sama lain tidak masalah. Filter pesanan ditambahkan tidak masalah jika diterapkan ke grup yang sama atau titik akhir tertentu.
Permintaan untuk /outer/inner/ mencatat hal berikut:
/outer group filter
/inner group filter
MapGet filter
Pengikatan parameter
Pengikatan parameter adalah proses mengonversi data permintaan menjadi parameter yang diekspresikan dengan kuat yang dinyatakan oleh penangan rute. Sumber pengikatan menentukan dari mana parameter terikat. Sumber pengikatan dapat eksplisit atau disimpulkan berdasarkan metode HTTP dan jenis parameter.
Sumber pengikatan yang didukung:
- Nilai rute
- Untai kueri
- Header
- Isi (sebagai JSON)
- Nilai formulir
- Layanan yang disediakan oleh injeksi dependensi
- Custom
Handler rute GET berikut menggunakan beberapa sumber pengikatan parameter ini:
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 { }
Fitur pengikatan parameter penting
-
Pengikatan eksplisit: Gunakan atribut seperti
[FromRoute], ,[FromQuery],[FromHeader][FromBody],[FromForm], dan[FromServices]untuk secara eksplisit menentukan sumber pengikatan. -
Pengikatan formulir: Mengikat nilai formulir menggunakan
[FromForm]atribut, termasuk dukungan untukIFormFiledanIFormFileCollectionuntuk unggahan file. - Jenis kompleks: Ikat ke koleksi dan jenis kompleks dari formulir, string kueri, dan header.
-
Pengikatan kustom: Terapkan logika pengikatan kustom menggunakan
TryParse,BindAsync, atauIBindableFromHttpContext<T>antarmuka. - Parameter opsional: Mendukung jenis nullable dan nilai default untuk parameter opsional.
- Injeksi dependensi: Parameter secara otomatis dihubungkan dari layanan yang terdaftar dalam kontainer DI tersebut.
-
Jenis khusus: Pengikatan otomatis untuk
HttpContext, ,HttpRequest,HttpResponseCancellationToken,ClaimsPrincipal,Stream, danPipeReader.
Pelajari lebih lanjut: Untuk informasi terperinci tentang pengikatan parameter termasuk skenario lanjutan, validasi, prioritas pengikatan, dan pemecahan masalah, lihat Pengikatan parameter dalam aplikasi API Minimal.
Responses
Handler rute mendukung jenis nilai pengembalian berikut:
-
IResultberbasis - Ini termasukTask<IResult>danValueTask<IResult> -
string- Ini termasukTask<string>danValueTask<string> -
T(Jenis lainnya) - Ini termasukTask<T>danValueTask<T>
| Mengembalikan nilai | Behavior | Content-Type |
|---|---|---|
IResult |
Kerangka kerja memanggil IResult.ExecuteAsync | Diputuskan IResult oleh implementasi |
string |
Kerangka kerja menulis string langsung ke respons | text/plain |
T (Jenis lainnya) |
Kerangka kerja JSON-menserialisasikan respons | application/json |
Untuk panduan yang lebih mendalam untuk merutekan nilai pengembalian handler, lihat Membuat respons di aplikasi API Minimal
Contoh nilai pengembalian
nilai pengembalian string
app.MapGet("/hello", () => "Hello World");
JSON mengembalikan nilai
app.MapGet("/hello", () => new { Message = "Hello World" });
Mengembalikan TypedResults
Kode berikut mengembalikan TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
TypedResults Mengembalikan lebih disukai untuk mengembalikan Results. Untuk informasi selengkapnya, lihat TypedResults vs Results.
Nilai pengembalian IResult
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
Contoh berikut menggunakan jenis hasil bawaan untuk mengkustomisasi respons:
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" }));
Kode Status Kustom
app.MapGet("/405", () => Results.StatusCode(405));
Text
app.MapGet("/text", () => Results.Text("This is some text"));
Stream
var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
Lihat Membuat respons di aplikasi API Minimal untuk contoh selengkapnya.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Hasil bawaan
Pembantu hasil umum ada di Results kelas statis dan TypedResults .
TypedResults Mengembalikan lebih disukai untuk mengembalikan Results. Untuk informasi selengkapnya, lihat TypedResults vs Results.
Mengubah Header
Gunakan objek HttpResponse untuk mengubah header respons:
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";
});
Menyesuaikan hasil
Aplikasi dapat mengontrol respons dengan menerapkan jenis kustom IResult . Kode berikut adalah contoh jenis hasil 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);
}
}
Sebaiknya tambahkan metode ekstensi untuk Microsoft.AspNetCore.Http.IResultExtensions membuat hasil kustom ini lebih dapat ditemukan.
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();
Hasil yang ditik
Antarmuka IResult dapat mewakili nilai yang dikembalikan dari API minimal yang tidak menggunakan dukungan implisit untuk JSON yang menserialisasikan objek yang dikembalikan ke respons HTTP. Kelas Hasil statis digunakan untuk membuat berbagai IResult objek yang mewakili berbagai jenis respons. Misalnya, mengatur kode status respons atau mengalihkan ke URL lain.
Jenis yang IResult diterapkan bersifat publik, memungkinkan pernyataan jenis saat pengujian. Contohnya:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Anda dapat melihat jenis pengembalian metode yang sesuai pada kelas TypedResults statis untuk menemukan jenis publik IResult yang benar untuk ditransmisikan.
Lihat Membuat respons di aplikasi API Minimal untuk contoh selengkapnya.
Filters
Untuk informasi selengkapnya, lihat Filter di aplikasi API Minimal.
Authorization
Rute dapat dilindungi menggunakan kebijakan otorisasi. Ini dapat dideklarasikan melalui [Authorize] atribut atau dengan menggunakan RequireAuthorization metode :
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();
Kode sebelumnya dapat ditulis dengan RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
Sampel berikut menggunakan otorisasi berbasis kebijakan:
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();
Perbolehkan pengguna yang tidak diaauthenticated untuk mengakses titik akhir
memungkinkan [AllowAnonymous] pengguna yang tidak diaauthenticated untuk mengakses titik akhir:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Rute dapat diaktifkan CORS menggunakan kebijakan CORS. CORS dapat dideklarasikan melalui [EnableCors] atribut atau dengan menggunakan RequireCors metode . Sampel berikut mengaktifkan 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();
Untuk informasi selengkapnya, lihat Mengaktifkan Permintaan Lintas Asal (CORS) di ASP.NET Core
ValidateScopes dan ValidateOnBuild
ValidateScopes dan ValidateOnBuild diaktifkan secara default di lingkungan Pengembangan tetapi dinonaktifkan di lingkungan lain.
Ketika ValidateOnBuild adalah true, kontainer DI memvalidasi konfigurasi layanan pada waktu build. Jika konfigurasi layanan tidak valid, build gagal pada startup aplikasi, bukan pada runtime saat layanan diminta.
Ketika ValidateScopes adalah true, kontainer DI memvalidasi bahwa layanan tercakup tidak diselesaikan dari cakupan akar. Menyelesaikan layanan tercakup dari cakupan akar dapat mengakibatkan kebocoran memori karena layanan dipertahankan dalam memori lebih lama dari cakupan permintaan.
ValidateScopes dan ValidateOnBuild salah secara default dalam mode non-Pengembangan karena alasan performa.
Kode berikut menunjukkan ValidateScopes diaktifkan secara default dalam mode pengembangan tetapi dinonaktifkan dalam mode rilis:
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 { }
Kode berikut menunjukkan ValidateOnBuild diaktifkan secara default dalam mode pengembangan tetapi dinonaktifkan dalam mode rilis:
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 { }
Kode berikut menonaktifkan ValidateScopes dan ValidateOnBuild dalam 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;
});
}
Lihat juga
- Referensi cepat API minimal
- Hasilkan dokumen OpenAPI
- Membuat respons dalam aplikasi API Minimal
- Filter di aplikasi API Minimal
- Menangani kesalahan di API inti ASP.NET
- Autentikasi dan otorisasi dalam API minimal
- Menguji aplikasi API Minimal
- Perutean sirkuit pendek
- Identity Titik akhir API
- Dukungan kontainer injeksi dependensi layanan yang dikunci
- Melihat ke belakang layar titik akhir API minimal
- Mengatur API Minimal Inti ASP.NET
- Diskusi validasi yang lancar di GitHub
Dokumen ini:
- Menyediakan referensi cepat untuk API minimal.
- Ditujukan untuk pengembang berpengalaman. Untuk pengenalan, lihat Tutorial: Membuat API minimal dengan ASP.NET Core.
API minimal terdiri dari:
WebApplication
Kode berikut dihasilkan oleh templat ASP.NET Core:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Kode sebelumnya dapat dibuat melalui dotnet new web pada baris perintah atau memilih templat Web Kosong di Visual Studio.
Kode berikut membuat WebApplication (app) tanpa secara eksplisit membuat WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create menginisialisasi instans WebApplication baru kelas dengan default yang telah dikonfigurasi sebelumnya.
WebApplication secara otomatis menambahkan middleware berikut dalam aplikasi API Minimal tergantung pada kondisi tertentu:
-
UseDeveloperExceptionPageditambahkan terlebih dahulu ketikaHostingEnvironmentadalah"Development". -
UseRoutingditambahkan kedua jika kode pengguna belum memanggilUseRoutingdan jika ada titik akhir yang dikonfigurasi, misalnyaapp.MapGet. -
UseEndpointsditambahkan di akhir alur middleware jika ada titik akhir yang dikonfigurasi. -
UseAuthenticationditambahkan segera setelahUseRoutingjika kode pengguna belum memanggilUseAuthenticationdan apakahIAuthenticationSchemeProviderdapat terdeteksi di penyedia layanan.IAuthenticationSchemeProviderditambahkan secara default saat menggunakanAddAuthentication, dan layanan terdeteksi menggunakanIServiceProviderIsService. -
UseAuthorizationditambahkan berikutnya jika kode pengguna belum memanggilUseAuthorizationdan apakahIAuthorizationHandlerProviderdapat terdeteksi di penyedia layanan.IAuthorizationHandlerProviderditambahkan secara default saat menggunakanAddAuthorization, dan layanan terdeteksi menggunakanIServiceProviderIsService. - Middleware dan titik akhir yang dikonfigurasi pengguna ditambahkan antara
UseRoutingdanUseEndpoints.
Kode berikut secara efektif adalah apa yang ditambahkan middleware otomatis ke aplikasi:
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 => {});
Dalam beberapa kasus, konfigurasi middleware default tidak benar untuk aplikasi dan memerlukan modifikasi. Misalnya, UseCors harus dipanggil sebelum UseAuthentication dan UseAuthorization. Aplikasi perlu memanggil UseAuthentication dan UseAuthorization jika UseCors dipanggil:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Jika middleware harus dijalankan sebelum pencocokan rute terjadi, UseRouting harus dipanggil dan middleware harus ditempatkan sebelum panggilan ke UseRouting.
UseEndpoints tidak diperlukan dalam kasus ini karena ditambahkan secara otomatis seperti yang dijelaskan sebelumnya:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Saat menambahkan middleware terminal:
- Middleware harus ditambahkan setelah
UseEndpoints. - Aplikasi perlu memanggil
UseRoutingdanUseEndpointssehingga middleware terminal dapat ditempatkan di lokasi yang benar.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Middleware terminal adalah middleware yang berjalan jika tidak ada titik akhir yang menangani permintaan.
Bekerja dengan port
Saat aplikasi web dibuat dengan Visual Studio atau dotnet new, Properties/launchSettings.json file dibuat yang menentukan port yang ditanggapi aplikasi. Di sampel pengaturan port berikut, menjalankan aplikasi dari Visual Studio mengembalikan dialog Unable to connect to web server 'AppName'kesalahan . Visual Studio mengembalikan kesalahan karena mengharapkan port yang ditentukan dalam Properties/launchSettings.json, tetapi aplikasi menggunakan port yang ditentukan oleh app.Run("http://localhost:3000"). Jalankan sampel perubahan port berikut dari baris perintah.
Bagian berikut mengatur port yang direspons aplikasi.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
Dalam kode sebelumnya, aplikasi merespons port 3000.
Beberapa port
Dalam kode berikut, aplikasi merespons port 3000 dan 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Mengatur port dari baris perintah
Perintah berikut membuat aplikasi merespons port 7777:
dotnet run --urls="https://localhost:7777"
Kestrel Jika titik akhir juga dikonfigurasi dalam appsettings.json file, appsettings.json URL yang ditentukan file akan digunakan. Untuk informasi selengkapnya, lihat Kestrel konfigurasi titik akhir
Membaca port dari lingkungan
Kode berikut membaca port dari lingkungan:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Cara yang disukai untuk mengatur port dari lingkungan adalah dengan menggunakan ASPNETCORE_URLS variabel lingkungan, yang ditunjukkan di bagian berikut.
Mengatur port melalui variabel lingkungan ASPNETCORE_URLS
Variabel ASPNETCORE_URLS lingkungan tersedia untuk mengatur port:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS mendukung beberapa URL:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Dengarkan semua antarmuka
Sampel berikut menunjukkan mendengarkan di semua antarmuka
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();
Dengarkan semua antarmuka menggunakan ASPNETCORE_URLS
Sampel sebelumnya dapat menggunakan ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Dengarkan semua antarmuka menggunakan ASPNETCORE_HTTPS_PORTS
Sampel sebelumnya dapat menggunakan ASPNETCORE_HTTPS_PORTS dan ASPNETCORE_HTTP_PORTS.
ASPNETCORE_HTTP_PORTS=3000;5005
ASPNETCORE_HTTPS_PORTS=5000
Untuk informasi selengkapnya, lihat Mengonfigurasi titik akhir untuk server web ASP.NET Core Kestrel
Tentukan HTTPS dengan sertifikat pengembangan
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Untuk informasi selengkapnya tentang sertifikat pengembangan, lihat Mempercayai sertifikat pengembangan ASP.NET Core HTTPS di Windows dan macOS.
Tentukan HTTPS menggunakan sertifikat kustom
Bagian berikut menunjukkan cara menentukan sertifikat kustom menggunakan appsettings.json file dan melalui konfigurasi.
Tentukan sertifikat kustom dengan appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Tentukan sertifikat kustom melalui konfigurasi
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();
Menggunakan API sertifikat
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();
Membaca lingkungan
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();
Untuk informasi lebih lanjut tentang penggunaan lingkungan runtime, lihat lingkungan runtime ASP.NET Core.
Configuration
Kode berikut membaca dari sistem konfigurasi:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Untuk informasi selengkapnya, lihat Konfigurasi di ASP.NET Core
Logging
Kode berikut menulis pesan ke startup aplikasi masuk:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Untuk informasi selengkapnya, lihat Logging di .NET dan ASP.NET Core
Mengakses kontainer Injeksi Dependensi (DI)
Kode berikut menunjukkan cara mendapatkan layanan dari kontainer DI selama pengaktifan aplikasi:
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();
Kode berikut menunjukkan cara mengakses kunci dari kontainer DI menggunakan [FromKeyedServices] atribut :
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.";
}
Untuk informasi selengkapnya tentang DI, lihat Injeksi dependensi di ASP.NET Core.
WebApplicationBuilder
Bagian ini berisi kode sampel menggunakan WebApplicationBuilder.
Mengubah akar konten, nama aplikasi, dan lingkungan
Kode berikut mengatur akar konten, nama aplikasi, dan lingkungan:
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 menginisialisasi instans baru kelas WebApplicationBuilder dengan default yang telah dikonfigurasi sebelumnya.
Untuk informasi selengkapnya, lihat gambaran umum dasar-dasar ASP.NET Core
Mengubah akar konten, nama aplikasi, dan lingkungan dengan menggunakan variabel lingkungan atau baris perintah
Tabel berikut ini memperlihatkan variabel lingkungan dan argumen baris perintah yang digunakan untuk mengubah akar konten, nama aplikasi, dan lingkungan:
| fitur | Variabel lingkungan | Argumen baris perintah |
|---|---|---|
| Nama aplikasi | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Nama lingkungan | ASPNETCORE_ENVIRONMENT | --environment |
| Akar konten | ASPNETCORE_CONTENTROOT | --contentRoot |
Menambahkan penyedia konfigurasi
Contoh berikut menambahkan penyedia konfigurasi INI:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Untuk informasi terperinci, lihat Penyedia konfigurasi file di Konfigurasi di ASP.NET Core.
Membaca konfigurasi
Secara default WebApplicationBuilder , konfigurasi baca dari beberapa sumber, termasuk:
-
appSettings.jsondanappSettings.{environment}.json - Variabel lingkungan
- Baris perintah
Untuk daftar lengkap sumber konfigurasi yang dibaca, lihat Konfigurasi default di Konfigurasi di ASP.NET Core.
Kode berikut membaca HelloKey dari konfigurasi dan menampilkan nilai di / titik akhir. Jika nilai konfigurasi null, "Halo" ditetapkan ke message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Membaca lingkungan
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
Console.WriteLine($"Running in development.");
}
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Menambahkan penyedia pengelogan
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();
Menambahkan layanan
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();
Menyesuaikan IHostBuilder
Metode ekstensi yang ada di IHostBuilder dapat diakses menggunakan properti 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();
Menyesuaikan IWebHostBuilder
Metode ekstensi pada IWebHostBuilder dapat diakses menggunakan properti 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();
Mengubah akar web
Secara default, akar web relatif terhadap akar konten di wwwroot folder. Direktori root web adalah tempat Middleware File Statis mencari file statis. Akar web dapat diubah dengan WebHostOptions, baris perintah, atau dengan UseWebRoot metode :
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Kontainer injeksi dependensi kustom (DI)
Contoh berikut menggunakan 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();
Tambahkan Middleware
Middleware ASP.NET Core yang ada dapat dikonfigurasi pada WebApplication:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Untuk informasi selengkapnya, lihat ASP.NET Core Middleware
Halaman pengecualian pengembang
WebApplication.CreateBuilder menginisialisasi instans WebApplicationBuilder baru kelas dengan default yang telah dikonfigurasi sebelumnya. Halaman pengecualian pengembang diaktifkan dalam default yang telah dikonfigurasi sebelumnya. Saat kode berikut dijalankan di lingkungan pengembangan, navigasi untuk / merender halaman ramah yang menunjukkan pengecualian.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () =>
{
throw new InvalidOperationException("Oops, the '/' route has thrown an exception.");
});
app.Run();
ASP.NET Core Middleware
Tabel berikut mencantumkan beberapa middleware yang sering digunakan dengan API minimal.
| Middleware | Description | API |
|---|---|---|
| Authentication | Menyediakan dukungan autentikasi. | UseAuthentication |
| Authorization | Menyediakan dukungan otorisasi. | UseAuthorization |
| CORS | Mengonfigurasi Cross-Origin Resource Sharing. | UseCors |
| Handler Pengecualian | Secara global menangani pengecualian yang dilemparkan oleh alur middleware. | UseExceptionHandler |
| Header yang Diteruskan | Meneruskan header yang diproksi ke permintaan saat ini. | UseForwardedHeaders |
| Pengalihan HTTPS | Mengalihkan semua permintaan HTTP ke HTTPS. | UseHttpsRedirection |
| Http Strict Transport Security (HSTS) | Middleware peningkatan keamanan yang menambahkan header respons khusus. | UseHsts |
| Pengelogan Permintaan | Menyediakan dukungan untuk mencatat permintaan dan respons HTTP. | UseHttpLogging |
| Batas Waktu Permintaan | Menyediakan dukungan untuk mengonfigurasi batas waktu permintaan, default global, dan per titik akhir. | UseRequestTimeouts |
| Pengelogan Permintaan W3C | Menyediakan dukungan untuk mencatat permintaan dan respons HTTP dalam format W3C. | UseW3CLogging |
| Penembolokan Respons | Menyediakan dukungan untuk respons penembolokan. | UseResponseCaching |
| Kompresi Respons | Memberikan dukungan untuk memadatkan respons. | UseResponseCompression |
| Session | Menyediakan dukungan untuk mengelola sesi pengguna. | UseSession |
| File Statis | Menyediakan dukungan untuk menyajikan file statis dan penjelajahan direktori. | UseStaticFiles, UseFileServer |
| WebSockets | Mengaktifkan protokol WebSocket. | UseWebSockets |
Bagian berikut mencakup penanganan permintaan: perutean, pengikatan parameter, dan respons.
Routing
Dukungan yang dikonfigurasi WebApplicationMap{Verb} dan MapMethods di mana {Verb} adalah metode HTTP camel-cased seperti Get, , PostPut atau 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();
Argumen Delegate yang diteruskan ke metode ini disebut "penangan rute".
Penangan Rute
Penangan rute adalah metode yang dijalankan saat rute cocok. Penangan rute dapat berupa ekspresi lambda, fungsi lokal, metode instans, atau metode statis. Penangan rute bisa sinkron atau asinkron.
Ekspresi 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();
Fungsi lokal
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Metode instans
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";
}
}
Metode statis
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";
}
}
Titik akhir yang ditentukan di luar Program.cs
API minimal tidak harus berada di 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" });
});
}
}
Lihat juga Merutekan grup nanti di artikel ini.
Titik akhir dan pembuatan tautan bernama
Titik akhir dapat diberikan nama untuk menghasilkan URL ke titik akhir. Menggunakan titik akhir bernama menghindari harus melakukan jalur kode keras di aplikasi:
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();
Kode sebelumnya ditampilkan The link to the hello route is /hello dari / titik akhir.
CATATAN: Nama titik akhir peka huruf besar/kecil.
Nama titik akhir:
- Harus unik secara global.
- Digunakan sebagai id operasi OpenAPI saat dukungan OpenAPI diaktifkan. Untuk informasi selengkapnya, lihat OpenAPI.
Parameter Rute
Parameter rute dapat ditangkap sebagai bagian dari definisi pola rute:
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();
Kode sebelumnya dikembalikan The user id is 3 and book id is 7 dari URI /users/3/books/7.
Handler rute dapat mendeklarasikan parameter yang akan diambil. Ketika permintaan dibuat ke rute dengan parameter yang dinyatakan untuk diambil, parameter diurai dan diteruskan ke handler. Ini memudahkan untuk menangkap nilai dengan cara yang aman. Dalam kode sebelumnya, userId dan bookId keduanya int.
Dalam kode sebelumnya, jika salah satu nilai rute tidak dapat dikonversi ke int, pengecualian akan dilemparkan. Permintaan /users/hello/books/3 GET melemparkan pengecualian berikut:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Kartubebas dan tangkap semua rute
Berikut ini menangkap semua rute yang Routing to hello dikembalikan dari titik akhir '/posts/hello':
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Batasan rute
Batasan rute membatasi perilaku pencocokan rute.
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();
Tabel berikut menunjukkan templat rute sebelumnya dan perilakunya:
| Templat Rute | Contoh URI yang Cocok |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Untuk informasi selengkapnya, lihat Referensi batasan rute dalam Perutean di ASP.NET Core.
Grup rute
Metode MapGroup ekstensi membantu mengatur grup titik akhir dengan awalan umum. Ini mengurangi kode berulang dan memungkinkan untuk menyesuaikan seluruh grup titik akhir dengan satu panggilan ke metode seperti RequireAuthorization dan WithMetadata yang menambahkan metadata titik akhir.
Misalnya, kode berikut membuat dua grup titik akhir serupa:
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;
}
Dalam skenario ini, Anda dapat menggunakan alamat relatif untuk Location header dalam hasil 201 Created :
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Grup titik akhir pertama hanya akan cocok dengan permintaan yang diawali dan /public/todos dapat diakses tanpa autentikasi apa pun. Grup titik akhir kedua hanya akan cocok dengan permintaan yang diawali dan /private/todos memerlukan autentikasi.
Pabrik QueryPrivateTodos filter titik akhir adalah fungsi lokal yang memodifikasi parameter handler TodoDb rute untuk memungkinkan mengakses dan menyimpan data todo privat.
Grup rute juga mendukung grup berlapis dan pola awalan kompleks dengan parameter rute dan batasan. Dalam contoh berikut, dan handler rute yang dipetakan user ke grup dapat menangkap {org} parameter rute dan {group} yang ditentukan dalam awalan grup luar.
Awalan juga dapat kosong. Ini dapat berguna untuk menambahkan metadata titik akhir atau filter ke sekelompok titik akhir tanpa mengubah pola rute.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Menambahkan filter atau metadata ke grup berulah dengan cara yang sama seperti menambahkannya satu per satu ke setiap titik akhir sebelum menambahkan filter atau metadata tambahan yang mungkin telah ditambahkan ke grup dalam atau titik akhir tertentu.
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);
});
Dalam contoh di atas, filter luar akan mencatat permintaan masuk sebelum filter dalam meskipun ditambahkan kedua. Karena filter diterapkan ke grup yang berbeda, urutan yang ditambahkan relatif satu sama lain tidak masalah. Filter pesanan ditambahkan tidak masalah jika diterapkan ke grup yang sama atau titik akhir tertentu.
Permintaan untuk /outer/inner/ mencatat hal berikut:
/outer group filter
/inner group filter
MapGet filter
Pengikatan parameter
Pengikatan parameter adalah proses mengonversi data permintaan menjadi parameter yang diekspresikan dengan kuat yang dinyatakan oleh penangan rute. Sumber pengikatan menentukan dari mana parameter terikat. Sumber pengikatan dapat eksplisit atau disimpulkan berdasarkan metode HTTP dan jenis parameter.
Sumber pengikatan yang didukung:
- Nilai rute
- Untai kueri
- Header
- Isi (sebagai JSON)
- Nilai formulir
- Layanan yang disediakan oleh injeksi dependensi
- Custom
Handler rute GET berikut menggunakan beberapa sumber pengikatan parameter ini:
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 { }
Fitur pengikatan parameter penting
-
Pengikatan eksplisit: Gunakan atribut seperti
[FromRoute], ,[FromQuery],[FromHeader][FromBody],[FromForm], dan[FromServices]untuk secara eksplisit menentukan sumber pengikatan. -
Pengikatan formulir: Mengikat nilai formulir menggunakan
[FromForm]atribut, termasuk dukungan untukIFormFiledanIFormFileCollectionuntuk unggahan file. - Jenis kompleks: Ikat ke koleksi dan jenis kompleks dari formulir, string kueri, dan header.
-
Pengikatan kustom: Terapkan logika pengikatan kustom menggunakan
TryParse,BindAsync, atauIBindableFromHttpContext<T>antarmuka. - Parameter opsional: Mendukung jenis nullable dan nilai default untuk parameter opsional.
- Injeksi dependensi: Parameter secara otomatis dihubungkan dari layanan yang terdaftar dalam kontainer DI tersebut.
-
Jenis khusus: Pengikatan otomatis untuk
HttpContext, ,HttpRequest,HttpResponseCancellationToken,ClaimsPrincipal,Stream, danPipeReader.
Pelajari lebih lanjut: Untuk informasi terperinci tentang pengikatan parameter termasuk skenario lanjutan, validasi, prioritas pengikatan, dan pemecahan masalah, lihat Pengikatan parameter dalam aplikasi API Minimal.
Responses
Handler rute mendukung jenis nilai pengembalian berikut:
-
IResultberbasis - Ini termasukTask<IResult>danValueTask<IResult> -
string- Ini termasukTask<string>danValueTask<string> -
T(Jenis lainnya) - Ini termasukTask<T>danValueTask<T>
| Mengembalikan nilai | Behavior | Content-Type |
|---|---|---|
IResult |
Kerangka kerja memanggil IResult.ExecuteAsync | Diputuskan IResult oleh implementasi |
string |
Kerangka kerja menulis string langsung ke respons | text/plain |
T (Jenis lainnya) |
Kerangka kerja JSON-menserialisasikan respons | application/json |
Untuk panduan yang lebih mendalam untuk merutekan nilai pengembalian handler, lihat Membuat respons di aplikasi API Minimal
Contoh nilai pengembalian
nilai pengembalian string
app.MapGet("/hello", () => "Hello World");
JSON mengembalikan nilai
app.MapGet("/hello", () => new { Message = "Hello World" });
Mengembalikan TypedResults
Kode berikut mengembalikan TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
TypedResults Mengembalikan lebih disukai untuk mengembalikan Results. Untuk informasi selengkapnya, lihat TypedResults vs Results.
Nilai pengembalian IResult
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
Contoh berikut menggunakan jenis hasil bawaan untuk mengkustomisasi respons:
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" }));
Kode Status Kustom
app.MapGet("/405", () => Results.StatusCode(405));
Text
app.MapGet("/text", () => Results.Text("This is some text"));
Stream
var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
Lihat Membuat respons di aplikasi API Minimal untuk contoh selengkapnya.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Hasil bawaan
Pembantu hasil umum ada di Results kelas statis dan TypedResults .
TypedResults Mengembalikan lebih disukai untuk mengembalikan Results. Untuk informasi selengkapnya, lihat TypedResults vs Results.
Mengubah Header
Gunakan objek HttpResponse untuk mengubah header respons:
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";
});
Menyesuaikan hasil
Aplikasi dapat mengontrol respons dengan menerapkan jenis kustom IResult . Kode berikut adalah contoh jenis hasil 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);
}
}
Sebaiknya tambahkan metode ekstensi untuk Microsoft.AspNetCore.Http.IResultExtensions membuat hasil kustom ini lebih dapat ditemukan.
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();
Hasil yang ditik
Antarmuka IResult dapat mewakili nilai yang dikembalikan dari API minimal yang tidak menggunakan dukungan implisit untuk JSON yang menserialisasikan objek yang dikembalikan ke respons HTTP. Kelas Hasil statis digunakan untuk membuat berbagai IResult objek yang mewakili berbagai jenis respons. Misalnya, mengatur kode status respons atau mengalihkan ke URL lain.
Jenis yang IResult diterapkan bersifat publik, memungkinkan pernyataan jenis saat pengujian. Contohnya:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Anda dapat melihat jenis pengembalian metode yang sesuai pada kelas TypedResults statis untuk menemukan jenis publik IResult yang benar untuk ditransmisikan.
Lihat Membuat respons di aplikasi API Minimal untuk contoh selengkapnya.
Filters
Untuk informasi selengkapnya, lihat Filter di aplikasi API Minimal.
Authorization
Rute dapat dilindungi menggunakan kebijakan otorisasi. Ini dapat dideklarasikan melalui [Authorize] atribut atau dengan menggunakan RequireAuthorization metode :
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();
Kode sebelumnya dapat ditulis dengan RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
Sampel berikut menggunakan otorisasi berbasis kebijakan:
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();
Perbolehkan pengguna yang tidak diaauthenticated untuk mengakses titik akhir
memungkinkan [AllowAnonymous] pengguna yang tidak diaauthenticated untuk mengakses titik akhir:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Rute dapat diaktifkan CORS menggunakan kebijakan CORS. CORS dapat dideklarasikan melalui [EnableCors] atribut atau dengan menggunakan RequireCors metode . Sampel berikut mengaktifkan 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();
Untuk informasi selengkapnya, lihat Mengaktifkan Permintaan Lintas Asal (CORS) di ASP.NET Core
ValidateScopes dan ValidateOnBuild
ValidateScopes dan ValidateOnBuild diaktifkan secara default di lingkungan Pengembangan tetapi dinonaktifkan di lingkungan lain.
Ketika ValidateOnBuild adalah true, kontainer DI memvalidasi konfigurasi layanan pada waktu build. Jika konfigurasi layanan tidak valid, build gagal pada startup aplikasi, bukan pada runtime saat layanan diminta.
Ketika ValidateScopes adalah true, kontainer DI memvalidasi bahwa layanan tercakup tidak diselesaikan dari cakupan akar. Menyelesaikan layanan tercakup dari cakupan akar dapat mengakibatkan kebocoran memori karena layanan dipertahankan dalam memori lebih lama dari cakupan permintaan.
ValidateScopes dan ValidateOnBuild salah secara default dalam mode non-Pengembangan karena alasan performa.
Kode berikut menunjukkan ValidateScopes diaktifkan secara default dalam mode pengembangan tetapi dinonaktifkan dalam mode rilis:
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 { }
Kode berikut menunjukkan ValidateOnBuild diaktifkan secara default dalam mode pengembangan tetapi dinonaktifkan dalam mode rilis:
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 { }
Kode berikut menonaktifkan ValidateScopes dan ValidateOnBuild dalam 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;
});
}
Lihat juga
- Referensi cepat API minimal
- Hasilkan dokumen OpenAPI
- Membuat respons dalam aplikasi API Minimal
- Filter di aplikasi API Minimal
- Menangani kesalahan di API inti ASP.NET
- Autentikasi dan otorisasi dalam API minimal
- Menguji aplikasi API Minimal
- Perutean sirkuit pendek
- Identity Titik akhir API
- Dukungan kontainer injeksi dependensi layanan yang dikunci
- Melihat ke belakang layar titik akhir API minimal
- Mengatur API Minimal Inti ASP.NET
- Diskusi validasi yang lancar di GitHub
Dokumen ini:
- Menyediakan referensi cepat untuk API minimal.
- Ditujukan untuk pengembang berpengalaman. Untuk pengenalan, lihat Tutorial: Membuat API minimal dengan ASP.NET Core
API minimal terdiri dari:
WebApplication
Kode berikut dihasilkan oleh templat ASP.NET Core:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Kode sebelumnya dapat dibuat melalui dotnet new web pada baris perintah atau memilih templat Web Kosong di Visual Studio.
Kode berikut membuat WebApplication (app) tanpa secara eksplisit membuat WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create menginisialisasi instans WebApplication baru kelas dengan default yang telah dikonfigurasi sebelumnya.
WebApplication secara otomatis menambahkan middleware berikut dalam aplikasi API Minimal tergantung pada kondisi tertentu:
-
UseDeveloperExceptionPageditambahkan terlebih dahulu ketikaHostingEnvironmentadalah"Development". -
UseRoutingditambahkan kedua jika kode pengguna belum memanggilUseRoutingdan jika ada titik akhir yang dikonfigurasi, misalnyaapp.MapGet. -
UseEndpointsditambahkan di akhir alur middleware jika ada titik akhir yang dikonfigurasi. -
UseAuthenticationditambahkan segera setelahUseRoutingjika kode pengguna belum memanggilUseAuthenticationdan apakahIAuthenticationSchemeProviderdapat terdeteksi di penyedia layanan.IAuthenticationSchemeProviderditambahkan secara default saat menggunakanAddAuthentication, dan layanan terdeteksi menggunakanIServiceProviderIsService. -
UseAuthorizationditambahkan berikutnya jika kode pengguna belum memanggilUseAuthorizationdan apakahIAuthorizationHandlerProviderdapat terdeteksi di penyedia layanan.IAuthorizationHandlerProviderditambahkan secara default saat menggunakanAddAuthorization, dan layanan terdeteksi menggunakanIServiceProviderIsService. - Middleware dan titik akhir yang dikonfigurasi pengguna ditambahkan antara
UseRoutingdanUseEndpoints.
Kode berikut secara efektif adalah apa yang ditambahkan middleware otomatis ke aplikasi:
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 => {});
Dalam beberapa kasus, konfigurasi middleware default tidak benar untuk aplikasi dan memerlukan modifikasi. Misalnya, UseCors harus dipanggil sebelum UseAuthentication dan UseAuthorization. Aplikasi perlu memanggil UseAuthentication dan UseAuthorization jika UseCors dipanggil:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Jika middleware harus dijalankan sebelum pencocokan rute terjadi, UseRouting harus dipanggil dan middleware harus ditempatkan sebelum panggilan ke UseRouting.
UseEndpoints tidak diperlukan dalam kasus ini karena ditambahkan secara otomatis seperti yang dijelaskan sebelumnya:
app.Use((context, next) =>
{
return next(context);
});
app.UseRouting();
// other middleware and endpoints
Saat menambahkan middleware terminal:
- Middleware harus ditambahkan setelah
UseEndpoints. - Aplikasi perlu memanggil
UseRoutingdanUseEndpointssehingga middleware terminal dapat ditempatkan di lokasi yang benar.
app.UseRouting();
app.MapGet("/", () => "hello world");
app.UseEndpoints(e => {});
app.Run(context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
Middleware terminal adalah middleware yang berjalan jika tidak ada titik akhir yang menangani permintaan.
Bekerja dengan port
Saat aplikasi web dibuat dengan Visual Studio atau dotnet new, Properties/launchSettings.json file dibuat yang menentukan port yang ditanggapi aplikasi. Di sampel pengaturan port berikut, menjalankan aplikasi dari Visual Studio mengembalikan dialog Unable to connect to web server 'AppName'kesalahan . Visual Studio mengembalikan kesalahan karena mengharapkan port yang ditentukan dalam Properties/launchSettings.json, tetapi aplikasi menggunakan port yang ditentukan oleh app.Run("http://localhost:3000"). Jalankan sampel perubahan port berikut dari baris perintah.
Bagian berikut mengatur port yang direspons aplikasi.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
Dalam kode sebelumnya, aplikasi merespons port 3000.
Beberapa port
Dalam kode berikut, aplikasi merespons port 3000 dan 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Mengatur port dari baris perintah
Perintah berikut membuat aplikasi merespons port 7777:
dotnet run --urls="https://localhost:7777"
Kestrel Jika titik akhir juga dikonfigurasi dalam appsettings.json file, appsettings.json URL yang ditentukan file akan digunakan. Untuk informasi selengkapnya, lihat Kestrel konfigurasi titik akhir
Membaca port dari lingkungan
Kode berikut membaca port dari lingkungan:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Cara yang disukai untuk mengatur port dari lingkungan adalah dengan menggunakan ASPNETCORE_URLS variabel lingkungan, yang ditunjukkan di bagian berikut.
Mengatur port melalui variabel lingkungan ASPNETCORE_URLS
Variabel ASPNETCORE_URLS lingkungan tersedia untuk mengatur port:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS mendukung beberapa URL:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Untuk informasi lebih lanjut tentang penggunaan lingkungan runtime, lihat lingkungan runtime ASP.NET Core.
Dengarkan semua antarmuka
Sampel berikut menunjukkan mendengarkan di semua antarmuka
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();
Dengarkan semua antarmuka menggunakan ASPNETCORE_URLS
Sampel sebelumnya dapat menggunakan ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Tentukan HTTPS dengan sertifikat pengembangan
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Untuk informasi selengkapnya tentang sertifikat pengembangan, lihat Mempercayai sertifikat pengembangan ASP.NET Core HTTPS di Windows dan macOS.
Tentukan HTTPS menggunakan sertifikat kustom
Bagian berikut menunjukkan cara menentukan sertifikat kustom menggunakan appsettings.json file dan melalui konfigurasi.
Tentukan sertifikat kustom dengan appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Tentukan sertifikat kustom melalui konfigurasi
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();
Menggunakan API sertifikat
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
Kode berikut membaca dari sistem konfigurasi:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Config failed!";
app.MapGet("/", () => message);
app.Run();
Untuk informasi selengkapnya, lihat Konfigurasi di ASP.NET Core
Logging
Kode berikut menulis pesan ke startup aplikasi masuk:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Untuk informasi selengkapnya, lihat Logging di .NET dan ASP.NET Core
Mengakses kontainer Injeksi Dependensi (DI)
Kode berikut menunjukkan cara mendapatkan layanan dari kontainer DI selama pengaktifan aplikasi:
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();
Untuk informasi lebih lanjut, lihat Injeksi dependensi di ASP.NET Core.
WebApplicationBuilder
Bagian ini berisi kode sampel menggunakan WebApplicationBuilder.
Mengubah akar konten, nama aplikasi, dan lingkungan
Kode berikut mengatur akar konten, nama aplikasi, dan lingkungan:
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 menginisialisasi instans baru kelas WebApplicationBuilder dengan default yang telah dikonfigurasi sebelumnya.
Untuk informasi selengkapnya, lihat gambaran umum dasar-dasar ASP.NET Core
Mengubah akar konten, nama aplikasi, dan lingkungan menurut variabel lingkungan atau baris perintah
Tabel berikut ini memperlihatkan variabel lingkungan dan argumen baris perintah yang digunakan untuk mengubah akar konten, nama aplikasi, dan lingkungan:
| fitur | Variabel lingkungan | Argumen baris perintah |
|---|---|---|
| Nama aplikasi | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Nama lingkungan | ASPNETCORE_ENVIRONMENT | --environment |
| Akar konten | ASPNETCORE_CONTENTROOT | --contentRoot |
Menambahkan penyedia konfigurasi
Contoh berikut menambahkan penyedia konfigurasi INI:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Untuk informasi terperinci, lihat Penyedia konfigurasi file di Konfigurasi di ASP.NET Core.
Membaca konfigurasi
Secara default WebApplicationBuilder , konfigurasi baca dari beberapa sumber, termasuk:
-
appSettings.jsondanappSettings.{environment}.json - Variabel lingkungan
- Baris perintah
Kode berikut membaca HelloKey dari konfigurasi dan menampilkan nilai di / titik akhir. Jika nilai konfigurasi null, "Halo" ditetapkan ke message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Untuk daftar lengkap sumber konfigurasi yang dibaca, lihat Konfigurasi default dalam Konfigurasi di ASP.NET Core
Menambahkan penyedia pengelogan
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();
Menambahkan layanan
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();
Menyesuaikan IHostBuilder
Metode ekstensi yang ada di IHostBuilder dapat diakses menggunakan properti 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();
Menyesuaikan IWebHostBuilder
Metode ekstensi pada IWebHostBuilder dapat diakses menggunakan properti 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();
Mengubah akar web
Secara default, akar web relatif terhadap akar konten di wwwroot folder. Direktori root web adalah tempat Middleware File Statis mencari file statis. Akar web dapat diubah dengan WebHostOptions, baris perintah, atau dengan UseWebRoot metode :
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Kontainer injeksi dependensi kustom (DI)
Contoh berikut menggunakan 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();
Tambahkan Middleware
Middleware ASP.NET Core yang ada dapat dikonfigurasi pada WebApplication:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Untuk informasi selengkapnya, lihat ASP.NET Core Middleware
Halaman pengecualian pengembang
WebApplication.CreateBuilder menginisialisasi instans WebApplicationBuilder baru kelas dengan default yang telah dikonfigurasi sebelumnya. Halaman pengecualian pengembang diaktifkan dalam default yang telah dikonfigurasi sebelumnya. Saat kode berikut dijalankan di lingkungan pengembangan, navigasi untuk / merender halaman ramah yang menunjukkan pengecualian.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () =>
{
throw new InvalidOperationException("Oops, the '/' route has thrown an exception.");
});
app.Run();
ASP.NET Core Middleware
Tabel berikut mencantumkan beberapa middleware yang sering digunakan dengan API minimal.
| Middleware | Description | API |
|---|---|---|
| Authentication | Menyediakan dukungan autentikasi. | UseAuthentication |
| Authorization | Menyediakan dukungan otorisasi. | UseAuthorization |
| CORS | Mengonfigurasi Cross-Origin Resource Sharing. | UseCors |
| Handler Pengecualian | Secara global menangani pengecualian yang dilemparkan oleh alur middleware. | UseExceptionHandler |
| Header yang Diteruskan | Meneruskan header yang diproksi ke permintaan saat ini. | UseForwardedHeaders |
| Pengalihan HTTPS | Mengalihkan semua permintaan HTTP ke HTTPS. | UseHttpsRedirection |
| Http Strict Transport Security (HSTS) | Middleware peningkatan keamanan yang menambahkan header respons khusus. | UseHsts |
| Pengelogan Permintaan | Menyediakan dukungan untuk mencatat permintaan dan respons HTTP. | UseHttpLogging |
| Batas Waktu Permintaan | Menyediakan dukungan untuk mengonfigurasi batas waktu permintaan, default global, dan per titik akhir. | UseRequestTimeouts |
| Pengelogan Permintaan W3C | Menyediakan dukungan untuk mencatat permintaan dan respons HTTP dalam format W3C. | UseW3CLogging |
| Penembolokan Respons | Menyediakan dukungan untuk respons penembolokan. | UseResponseCaching |
| Kompresi Respons | Memberikan dukungan untuk memadatkan respons. | UseResponseCompression |
| Session | Menyediakan dukungan untuk mengelola sesi pengguna. | UseSession |
| File Statis | Menyediakan dukungan untuk menyajikan file statis dan penjelajahan direktori. | UseStaticFiles, UseFileServer |
| WebSockets | Mengaktifkan protokol WebSocket. | UseWebSockets |
Bagian berikut mencakup penanganan permintaan: perutean, pengikatan parameter, dan respons.
Routing
Dukungan yang dikonfigurasi WebApplicationMap{Verb} dan MapMethods di mana {Verb} adalah metode HTTP camel-cased seperti Get, , PostPut atau 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();
Argumen Delegate yang diteruskan ke metode ini disebut "penangan rute".
Penangan Rute
Penangan rute adalah metode yang dijalankan saat rute cocok. Penangan rute dapat berupa ekspresi lambda, fungsi lokal, metode instans, atau metode statis. Penangan rute bisa sinkron atau asinkron.
Ekspresi 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();
Fungsi lokal
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Metode instans
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";
}
}
Metode statis
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";
}
}
Titik akhir yang ditentukan di luar Program.cs
API minimal tidak harus berada di 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" });
});
}
}
Lihat juga Merutekan grup nanti di artikel ini.
Titik akhir dan pembuatan tautan bernama
Titik akhir dapat diberikan nama untuk menghasilkan URL ke titik akhir. Menggunakan titik akhir bernama menghindari harus melakukan jalur kode keras di aplikasi:
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();
Kode sebelumnya ditampilkan The link to the hello route is /hello dari / titik akhir.
CATATAN: Nama titik akhir peka huruf besar/kecil.
Nama titik akhir:
- Harus unik secara global.
- Digunakan sebagai id operasi OpenAPI saat dukungan OpenAPI diaktifkan. Untuk informasi selengkapnya, lihat OpenAPI.
Parameter Rute
Parameter rute dapat ditangkap sebagai bagian dari definisi pola rute:
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();
Kode sebelumnya dikembalikan The user id is 3 and book id is 7 dari URI /users/3/books/7.
Handler rute dapat mendeklarasikan parameter yang akan diambil. Ketika permintaan dibuat ke rute dengan parameter yang dinyatakan untuk diambil, parameter diurai dan diteruskan ke handler. Ini memudahkan untuk menangkap nilai dengan cara yang aman. Dalam kode sebelumnya, userId dan bookId keduanya int.
Dalam kode sebelumnya, jika salah satu nilai rute tidak dapat dikonversi ke int, pengecualian akan dilemparkan. Permintaan /users/hello/books/3 GET melemparkan pengecualian berikut:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Kartubebas dan tangkap semua rute
Berikut ini menangkap semua rute yang Routing to hello dikembalikan dari titik akhir '/posts/hello':
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Batasan rute
Batasan rute membatasi perilaku pencocokan rute.
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();
Tabel berikut menunjukkan templat rute sebelumnya dan perilakunya:
| Templat Rute | Contoh URI yang Cocok |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Untuk informasi selengkapnya, lihat Referensi batasan rute dalam Perutean di ASP.NET Core.
Grup rute
Metode MapGroup ekstensi membantu mengatur grup titik akhir dengan awalan umum. Ini mengurangi kode berulang dan memungkinkan untuk menyesuaikan seluruh grup titik akhir dengan satu panggilan ke metode seperti RequireAuthorization dan WithMetadata yang menambahkan metadata titik akhir.
Misalnya, kode berikut membuat dua grup titik akhir serupa:
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;
}
Dalam skenario ini, Anda dapat menggunakan alamat relatif untuk Location header dalam hasil 201 Created :
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Grup titik akhir pertama hanya akan cocok dengan permintaan yang diawali dan /public/todos dapat diakses tanpa autentikasi apa pun. Grup titik akhir kedua hanya akan cocok dengan permintaan yang diawali dan /private/todos memerlukan autentikasi.
Pabrik QueryPrivateTodos filter titik akhir adalah fungsi lokal yang memodifikasi parameter handler TodoDb rute untuk memungkinkan mengakses dan menyimpan data todo privat.
Grup rute juga mendukung grup berlapis dan pola awalan kompleks dengan parameter rute dan batasan. Dalam contoh berikut, dan handler rute yang dipetakan user ke grup dapat menangkap {org} parameter rute dan {group} yang ditentukan dalam awalan grup luar.
Awalan juga dapat kosong. Ini dapat berguna untuk menambahkan metadata titik akhir atau filter ke sekelompok titik akhir tanpa mengubah pola rute.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Menambahkan filter atau metadata ke grup berulah dengan cara yang sama seperti menambahkannya satu per satu ke setiap titik akhir sebelum menambahkan filter atau metadata tambahan yang mungkin telah ditambahkan ke grup dalam atau titik akhir tertentu.
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);
});
Dalam contoh di atas, filter luar akan mencatat permintaan masuk sebelum filter dalam meskipun ditambahkan kedua. Karena filter diterapkan ke grup yang berbeda, urutan yang ditambahkan relatif satu sama lain tidak masalah. Filter pesanan ditambahkan tidak masalah jika diterapkan ke grup yang sama atau titik akhir tertentu.
Permintaan untuk /outer/inner/ mencatat hal berikut:
/outer group filter
/inner group filter
MapGet filter
Pengikatan parameter
Pengikatan parameter adalah proses mengonversi data permintaan menjadi parameter yang diekspresikan dengan kuat yang dinyatakan oleh penangan rute. Sumber pengikatan menentukan dari mana parameter terikat. Sumber pengikatan dapat eksplisit atau disimpulkan berdasarkan metode HTTP dan jenis parameter.
Sumber pengikatan yang didukung:
- Nilai rute
- Untai kueri
- Header
- Isi (sebagai JSON)
- Layanan yang disediakan oleh injeksi dependensi
- Custom
Pengikatan dari nilai formulir tidak didukung secara asli di .NET 6 dan 7.
Handler rute berikut GET menggunakan beberapa sumber pengikatan parameter ini:
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 { }
Tabel berikut menunjukkan hubungan antara parameter yang digunakan dalam contoh sebelumnya dan sumber pengikatan terkait.
| Parameter | Sumber Pengikatan |
|---|---|
id |
nilai rute |
page |
string kueri |
customHeader |
header |
service |
Disediakan oleh injeksi dependensi |
Metode HTTP , GET, HEAD, dan OPTIONS tidak secara implisit DELETEmengikat dari isi. Untuk mengikat dari isi (sebagai JSON) untuk metode HTTP ini, ikat secara eksplisit dengan [FromBody] atau baca dari HttpRequest.
Contoh handler rute POST berikut menggunakan sumber isi pengikatan (sebagai JSON) untuk person parameter :
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/", (Person person) => { });
record Person(string Name, int Age);
Parameter dalam contoh sebelumnya semuanya terikat dari data permintaan secara otomatis. Untuk menunjukkan kenyamanan yang disediakan pengikatan parameter, penangan rute berikut menunjukkan cara membaca data permintaan langsung dari permintaan:
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>();
// ...
});
Pengikatan Parameter Eksplisit
Atribut dapat digunakan untuk secara eksplisit menyatakan dari mana parameter terikat.
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 | Sumber Pengikatan |
|---|---|
id |
nilai rute dengan nama id |
page |
string kueri dengan nama "p" |
service |
Disediakan oleh injeksi dependensi |
contentType |
header dengan nama "Content-Type" |
Note
Pengikatan dari nilai formulir tidak didukung secara asli di .NET 6 dan 7.
Pengikatan parameter dengan injeksi dependensi
Pengikatan parameter untuk API minimal mengikat parameter melalui injeksi dependensi saat jenis dikonfigurasi sebagai layanan. Tidak perlu menerapkan [FromServices] atribut secara eksplisit ke parameter. Dalam kode berikut, kedua tindakan mengembalikan waktu:
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();
Parameter opsional
Parameter yang dideklarasikan dalam handler rute diperlakukan sesuai kebutuhan:
- Jika permintaan cocok dengan rute, handler rute hanya berjalan jika semua parameter yang diperlukan disediakan dalam permintaan.
- Kegagalan untuk memberikan semua parameter yang diperlukan menghasilkan kesalahan.
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 dikembalikan |
/products |
BadHttpRequestException: Parameter yang diperlukan "int pageNumber" tidak disediakan dari string kueri. |
/products/1 |
Kesalahan HTTP 404, tidak ada rute yang cocok |
Untuk membuat pageNumber opsional, tentukan jenis sebagai opsional atau berikan nilai default:
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 dikembalikan |
/products |
1 dikembalikan |
/products2 |
1 dikembalikan |
Nilai nullable dan default sebelumnya berlaku untuk semua sumber:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/products", (Product? product) => { });
app.Run();
Kode sebelumnya memanggil metode dengan produk null jika tidak ada isi permintaan yang dikirim.
CATATAN: Jika data yang tidak valid disediakan dan parameter dapat diubah ke null, handler rute tidak dijalankan.
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 Kembali |
/products |
1 Kembali |
/products?pageNumber=two |
BadHttpRequestException: Gagal mengikat parameter "Nullable<int> pageNumber" dari "dua". |
/products/two |
Kesalahan HTTP 404, tidak ada rute yang cocok |
Lihat bagian Kegagalan Pengikatan untuk informasi selengkapnya.
Jenis khusus
Jenis berikut terikat tanpa atribut eksplisit:
HttpContext: Konteks yang menyimpan semua informasi tentang permintaan atau respons HTTP saat ini:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));HttpRequest dan HttpResponse: Permintaan HTTP dan respons HTTP:
app.MapGet("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));CancellationToken: Token pembatalan yang terkait dengan permintaan HTTP saat ini:
app.MapGet("/", async (CancellationToken cancellationToken) => await MakeLongRunningRequestAsync(cancellationToken));ClaimsPrincipal: Pengguna yang terkait dengan permintaan, terikat dari HttpContext.User:
app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
Mengikat isi permintaan sebagai Stream atau PipeReader
Isi permintaan dapat mengikat sebagai Stream atau PipeReader untuk mendukung skenario secara efisien di mana pengguna harus memproses data dan:
- Simpan data ke penyimpanan blob atau antrekan data ke penyedia antrean.
- Proses data yang disimpan dengan proses pekerja atau fungsi cloud.
Misalnya, data mungkin diantrekan ke penyimpanan Azure Queue atau disimpan di penyimpanan Azure Blob.
Kode berikut mengimplementasikan antrean latar belakang:
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;
}
Kode berikut mengikat isi permintaan ke 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);
});
Kode berikut menunjukkan file lengkap Program.cs :
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();
- Saat membaca data,
Streamadalah objek yang sama denganHttpRequest.Body. - Isi permintaan tidak di-buffer secara default. Setelah tubuh dibaca, itu tidak dapat digulung balik. Aliran tidak dapat dibaca beberapa kali.
-
StreamdanPipeReadertidak dapat digunakan di luar handler tindakan minimal karena buffer yang mendasar akan dibuang atau digunakan kembali.
Unggahan file menggunakan IFormFile dan IFormFileCollection
Kode berikut menggunakan IFormFile dan IFormFileCollection untuk mengunggah file:
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();
Permintaan unggahan file yang diautentikasi didukung menggunakan header Otorisasi, sertifikat klien, atau cookie header.
Tidak ada dukungan bawaan untuk antiforgery di ASP.NET Core di .NET 7.
Antiforgery tersedia di ASP.NET Core di .NET 8 atau versi yang lebih baru. Namun, dapat diimplementasikan menggunakan IAntiforgery layanan .
Mengikat array dan nilai string dari header dan string kueri
Kode berikut menunjukkan pengikatan string kueri ke array jenis primitif, array string, dan 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]}");
Mengikat string kueri atau nilai header ke array jenis kompleks didukung ketika jenis telah TryParse diimplementasikan. Kode berikut mengikat ke array string dan mengembalikan semua item dengan tag yang ditentukan:
// 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();
});
Kode berikut menunjukkan model dan implementasi yang diperlukan TryParse :
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;
}
}
Kode berikut mengikat ke int array:
// 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();
});
Untuk menguji kode sebelumnya, tambahkan titik akhir berikut untuk mengisi database dengan Todo item:
// POST /todoitems/batch
app.MapPost("/todoitems/batch", async (Todo[] todos, TodoDb db) =>
{
await db.Todos.AddRangeAsync(todos);
await db.SaveChangesAsync();
return Results.Ok(todos);
});
Gunakan alat pengujian API seperti HttpRepl untuk meneruskan data berikut ke titik akhir sebelumnya:
[
{
"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"
}
}
]
Kode berikut mengikat ke kunci X-Todo-Id header dan mengembalikan Todo item dengan nilai yang Id cocok:
// 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
Saat mengikat string[] dari string kueri, tidak adanya nilai string kueri yang cocok akan menghasilkan array kosong alih-alih nilai null.
Pengikatan parameter untuk daftar argumen dengan [AsParameters]
AsParametersAttribute memungkinkan pengikatan parameter sederhana ke jenis dan bukan pengikatan model yang kompleks atau rekursif.
Pertimbangkan gambar berikut:
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.
Pertimbangkan titik akhir berikut GET :
app.MapGet("/todoitems/{id}",
async (int Id, TodoDb Db) =>
await Db.Todos.FindAsync(Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
Berikut ini struct dapat digunakan untuk mengganti parameter yang disorot sebelumnya:
struct TodoItemRequest
{
public int Id { get; set; }
public TodoDb Db { get; set; }
}
Titik akhir yang direfaktor GET menggunakan yang sebelumnya struct dengan atribut 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());
Kode berikut menunjukkan titik akhir tambahan di aplikasi:
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();
});
Kelas berikut digunakan untuk merefaktor daftar parameter:
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!;
}
Kode berikut menunjukkan titik akhir yang direfaktor menggunakan AsParameters dan kelas dan sebelumnya struct :
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();
});
Jenis berikut record dapat digunakan untuk mengganti parameter sebelumnya:
record TodoItemRequest(int Id, TodoDb Db);
record CreateTodoItemRequest(TodoItemDTO Dto, TodoDb Db);
record EditTodoItemRequest(int Id, TodoItemDTO Dto, TodoDb Db);
struct Menggunakan dengan AsParameters bisa lebih berkinerja daripada menggunakan record jenis.
Kode sampel lengkap di repositori AspNetCore.Docs.Samples.
Pengikatan Kustom
Ada tiga cara untuk menyesuaikan pengikatan parameter:
- Untuk sumber pengikatan rute, kueri, dan header, ikat jenis kustom dengan menambahkan metode statis
TryParseuntuk jenis tersebut. - Kontrol proses pengikatan dengan menerapkan
BindAsyncmetode pada jenis. - Untuk skenario tingkat lanjut, terapkan IBindableFromHttpContext<TSelf> antarmuka untuk menyediakan logika pengikatan kustom langsung dari
HttpContext.
TryParse
TryParse memiliki dua API:
public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);
Kode berikut ditampilkan Point: 12.3, 10.1 dengan 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 memiliki API berikut:
public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);
Kode berikut ditampilkan SortBy:xyz, SortDirection:Desc, CurrentPage:99 dengan 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
}
Pengikatan parameter kustom dengan IBindableFromHttpContext
ASP.NET Core menyediakan dukungan untuk pengikatan parameter kustom di API Minimal menggunakan IBindableFromHttpContext<TSelf> antarmuka . Antarmuka ini, yang diperkenalkan dengan anggota abstrak statis di C# 11, memungkinkan Anda untuk membuat tipe yang dapat terikat dari konteks HTTP langsung dalam parameter penangan rute.
public interface IBindableFromHttpContext<TSelf>
where TSelf : class, IBindableFromHttpContext<TSelf>
{
static abstract ValueTask<TSelf?> BindAsync(HttpContext context, ParameterInfo parameter);
}
Dengan menerapkan IBindableFromHttpContext<TSelf> antarmuka, Anda dapat membuat tipe kustom yang menangani logika pengikatan mereka sendiri dari HttpContext. Ketika handler rute menyertakan parameter jenis ini, kerangka kerja secara otomatis memanggil metode BindAsync statis untuk membuat instans:
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}";
});
Berikut ini adalah contoh implementasi parameter kustom yang mengikat dari header 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
});
}
}
Anda juga dapat menerapkan validasi dalam logika pengikatan kustom Anda:
app.MapGet("/validated", (ValidatedParameter param) =>
{
if (string.IsNullOrEmpty(param.Value))
{
return Results.BadRequest("Value cannot be empty");
}
return Results.Ok($"Validated value: {param.Value}");
});
Melihat atau mengunduh kode sampel (cara mengunduh)
Kegagalan pengikatan
Saat pengikatan gagal, kerangka kerja mencatat pesan debug dan mengembalikan berbagai kode status ke klien tergantung pada mode kegagalan.
| Mode kegagalan | Jenis Parameter Nullable | Sumber Pengikatan | Kode status |
|---|---|---|---|
{ParameterType}.TryParse menghasilkan false |
yes | route/query/header | 400 |
{ParameterType}.BindAsync menghasilkan null |
yes | custom | 400 |
{ParameterType}.BindAsync Melempar |
tidak masalah | custom | 500 |
| Kegagalan untuk mendeserialisasi isi JSON | tidak masalah | body | 400 |
Tipe isi salah (bukan application/json) |
tidak masalah | body | 415 |
Prioritas Pengikatan
Aturan untuk menentukan sumber pengikatan dari parameter:
- Atribut eksplisit yang ditentukan pada parameter (Dari* atribut) dalam urutan berikut:
- Nilai rute:
[FromRoute] - String kueri:
[FromQuery] - Judul:
[FromHeader] - Badan:
[FromBody] - Layanan:
[FromServices] - Nilai parameter:
[AsParameters]
- Nilai rute:
- Jenis khusus
HttpContext-
HttpRequestAku akan menemuinya.HttpContext.Request -
HttpResponseAku akan menemuinya.HttpContext.Response -
ClaimsPrincipalAku akan menemuinya.HttpContext.User -
CancellationTokenAku akan menemuinya.HttpContext.RequestAborted -
IFormFileCollectionAku akan menemuinya.HttpContext.Request.Form.Files -
IFormFileAku akan menemuinya.HttpContext.Request.Form.Files[paramName] -
StreamAku akan menemuinya.HttpContext.Request.Body -
PipeReaderAku akan menemuinya.HttpContext.Request.BodyReader
- Jenis parameter memiliki metode statis
BindAsyncyang valid. - Jenis parameter adalah string atau memiliki metode statis
TryParseyang valid.- Jika nama parameter ada di templat rute. Dalam
app.Map("/todo/{id}", (int id) => {});,idterikat dari rute. - Terikat dari string kueri.
- Jika nama parameter ada di templat rute. Dalam
- Jika jenis parameter adalah layanan yang disediakan oleh injeksi dependensi, ia menggunakan layanan tersebut sebagai sumbernya.
- Parameternya adalah dari isinya.
Mengonfigurasi opsi deserialisasi JSON untuk pengikatan isi
Sumber pengikatan isi menggunakan System.Text.Json untuk deserialisasi. Tidak dimungkinkan untuk mengubah default ini, tetapi opsi serialisasi dan deserialisasi JSON dapat dikonfigurasi.
Mengonfigurasi opsi deserialisasi JSON secara global
Opsi yang berlaku secara global untuk aplikasi dapat dikonfigurasi dengan memanggil ConfigureHttpJsonOptions. Contoh berikut mencakup bidang publik dan format output 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
// }
Karena kode sampel mengonfigurasi serialisasi dan deserialisasi, kode tersebut dapat membaca NameField dan menyertakan NameField dalam output JSON.
Mengonfigurasi opsi deserialisasi JSON untuk titik akhir
ReadFromJsonAsync memiliki kelebihan beban yang menerima JsonSerializerOptions objek. Contoh berikut mencakup bidang publik dan format output 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
// }
Karena kode sebelumnya menerapkan opsi yang disesuaikan hanya untuk deserialisasi, output JSON mengecualikan NameField.
Membaca isi permintaan
Baca isi permintaan secara langsung menggunakan HttpContext parameter atau 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();
Kode sebelumnya:
- Mengakses isi permintaan menggunakan HttpRequest.BodyReader.
- Menyalin isi permintaan ke file lokal.
Responses
Handler rute mendukung jenis nilai pengembalian berikut:
-
IResultberbasis - Ini termasukTask<IResult>danValueTask<IResult> -
string- Ini termasukTask<string>danValueTask<string> -
T(Jenis lainnya) - Ini termasukTask<T>danValueTask<T>
| Mengembalikan nilai | Behavior | Content-Type |
|---|---|---|
IResult |
Kerangka kerja memanggil IResult.ExecuteAsync | Diputuskan IResult oleh implementasi |
string |
Kerangka kerja menulis string langsung ke respons | text/plain |
T (Jenis lainnya) |
Kerangka kerja JSON-menserialisasikan respons | application/json |
Untuk panduan yang lebih mendalam untuk merutekan nilai pengembalian handler, lihat Membuat respons di aplikasi API Minimal
Contoh nilai pengembalian
nilai pengembalian string
app.MapGet("/hello", () => "Hello World");
JSON mengembalikan nilai
app.MapGet("/hello", () => new { Message = "Hello World" });
Mengembalikan TypedResults
Kode berikut mengembalikan TypedResults:
app.MapGet("/hello", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
TypedResults Mengembalikan lebih disukai untuk mengembalikan Results. Untuk informasi selengkapnya, lihat TypedResults vs Results.
Nilai pengembalian IResult
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
Contoh berikut menggunakan jenis hasil bawaan untuk mengkustomisasi respons:
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" }));
Kode Status Kustom
app.MapGet("/405", () => Results.StatusCode(405));
Text
app.MapGet("/text", () => Results.Text("This is some text"));
Stream
var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
Lihat Membuat respons di aplikasi API Minimal untuk contoh selengkapnya.
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Hasil bawaan
Pembantu hasil umum ada di Results kelas statis dan TypedResults .
TypedResults Mengembalikan lebih disukai untuk mengembalikan Results. Untuk informasi selengkapnya, lihat TypedResults vs Results.
Menyesuaikan hasil
Aplikasi dapat mengontrol respons dengan menerapkan jenis kustom IResult . Kode berikut adalah contoh jenis hasil 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);
}
}
Sebaiknya tambahkan metode ekstensi untuk Microsoft.AspNetCore.Http.IResultExtensions membuat hasil kustom ini lebih dapat ditemukan.
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();
Hasil yang ditik
Antarmuka IResult dapat mewakili nilai yang dikembalikan dari API minimal yang tidak menggunakan dukungan implisit untuk JSON yang menserialisasikan objek yang dikembalikan ke respons HTTP. Kelas Hasil statis digunakan untuk membuat berbagai IResult objek yang mewakili berbagai jenis respons. Misalnya, mengatur kode status respons atau mengalihkan ke URL lain.
Jenis yang IResult diterapkan bersifat publik, memungkinkan pernyataan jenis saat pengujian. Contohnya:
[TestClass()]
public class WeatherApiTests
{
[TestMethod()]
public void MapWeatherApiTest()
{
var result = WeatherApi.GetAllWeathers();
Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
}
}
Anda dapat melihat jenis pengembalian metode yang sesuai pada kelas TypedResults statis untuk menemukan jenis publik IResult yang benar untuk ditransmisikan.
Lihat Membuat respons di aplikasi API Minimal untuk contoh selengkapnya.
Filters
Lihat Filter di aplikasi API Minimal
Authorization
Rute dapat dilindungi menggunakan kebijakan otorisasi. Ini dapat dideklarasikan melalui [Authorize] atribut atau dengan menggunakan RequireAuthorization metode :
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();
Kode sebelumnya dapat ditulis dengan RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
Sampel berikut menggunakan otorisasi berbasis kebijakan:
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();
Perbolehkan pengguna yang tidak diaauthenticated untuk mengakses titik akhir
memungkinkan [AllowAnonymous] pengguna yang tidak diaauthenticated untuk mengakses titik akhir:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Rute dapat diaktifkan CORS menggunakan kebijakan CORS. CORS dapat dideklarasikan melalui [EnableCors] atribut atau dengan menggunakan RequireCors metode . Sampel berikut mengaktifkan 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();
Untuk informasi selengkapnya, lihat Mengaktifkan Permintaan Lintas Asal (CORS) di ASP.NET Core
Lihat juga
Dokumen ini:
- Menyediakan referensi cepat untuk API minimal.
- Ditujukan untuk pengembang berpengalaman. Untuk pengenalan, lihat Tutorial: Membuat API minimal dengan ASP.NET Core
API minimal terdiri dari:
- WebApplication dan WebApplicationBuilder
- Penangan rute
WebApplication
Kode berikut dihasilkan oleh templat ASP.NET Core:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Kode sebelumnya dapat dibuat melalui dotnet new web pada baris perintah atau memilih templat Web Kosong di Visual Studio.
Kode berikut membuat WebApplication (app) tanpa secara eksplisit membuat WebApplicationBuilder:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.Create menginisialisasi instans WebApplication baru kelas dengan default yang telah dikonfigurasi sebelumnya.
Bekerja dengan port
Saat aplikasi web dibuat dengan Visual Studio atau dotnet new, Properties/launchSettings.json file dibuat yang menentukan port yang ditanggapi aplikasi. Di sampel pengaturan port berikut, menjalankan aplikasi dari Visual Studio mengembalikan dialog Unable to connect to web server 'AppName'kesalahan . Jalankan sampel perubahan port berikut dari baris perintah.
Bagian berikut mengatur port yang direspons aplikasi.
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
Dalam kode sebelumnya, aplikasi merespons port 3000.
Beberapa port
Dalam kode berikut, aplikasi merespons port 3000 dan 4000.
var app = WebApplication.Create(args);
app.Urls.Add("http://localhost:3000");
app.Urls.Add("http://localhost:4000");
app.MapGet("/", () => "Hello World");
app.Run();
Mengatur port dari baris perintah
Perintah berikut membuat aplikasi merespons port 7777:
dotnet run --urls="https://localhost:7777"
Kestrel Jika titik akhir juga dikonfigurasi dalam appsettings.json file, appsettings.json URL yang ditentukan file akan digunakan. Untuk informasi selengkapnya, lihat Kestrel konfigurasi titik akhir
Membaca port dari lingkungan
Kode berikut membaca port dari lingkungan:
var app = WebApplication.Create(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
app.MapGet("/", () => "Hello World");
app.Run($"http://localhost:{port}");
Cara yang disukai untuk mengatur port dari lingkungan adalah dengan menggunakan ASPNETCORE_URLS variabel lingkungan, yang ditunjukkan di bagian berikut.
Mengatur port melalui variabel lingkungan ASPNETCORE_URLS
Variabel ASPNETCORE_URLS lingkungan tersedia untuk mengatur port:
ASPNETCORE_URLS=http://localhost:3000
ASPNETCORE_URLS mendukung beberapa URL:
ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
Dengarkan semua antarmuka
Sampel berikut menunjukkan mendengarkan di semua antarmuka
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();
Dengarkan semua antarmuka menggunakan ASPNETCORE_URLS
Sampel sebelumnya dapat menggunakan ASPNETCORE_URLS
ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
Tentukan HTTPS dengan sertifikat pengembangan
var app = WebApplication.Create(args);
app.Urls.Add("https://localhost:3000");
app.MapGet("/", () => "Hello World");
app.Run();
Untuk informasi selengkapnya tentang sertifikat pengembangan, lihat Mempercayai sertifikat pengembangan ASP.NET Core HTTPS di Windows dan macOS.
Tentukan HTTPS menggunakan sertifikat kustom
Bagian berikut menunjukkan cara menentukan sertifikat kustom menggunakan appsettings.json file dan melalui konfigurasi.
Tentukan sertifikat kustom dengan appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Certificates": {
"Default": {
"Path": "cert.pem",
"KeyPath": "key.pem"
}
}
}
}
Tentukan sertifikat kustom melalui konfigurasi
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();
Menggunakan API sertifikat
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();
Membaca lingkungan
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();
Untuk informasi lebih lanjut tentang penggunaan lingkungan runtime, lihat lingkungan runtime ASP.NET Core.
Configuration
Kode berikut membaca dari sistem konfigurasi:
var app = WebApplication.Create(args);
var message = app.Configuration["HelloKey"] ?? "Hello";
app.MapGet("/", () => message);
app.Run();
Untuk informasi selengkapnya, lihat Konfigurasi di ASP.NET Core
Logging
Kode berikut menulis pesan ke startup aplikasi masuk:
var app = WebApplication.Create(args);
app.Logger.LogInformation("The app started");
app.MapGet("/", () => "Hello World");
app.Run();
Untuk informasi selengkapnya, lihat Logging di .NET dan ASP.NET Core
Mengakses kontainer Injeksi Dependensi (DI)
Kode berikut menunjukkan cara mendapatkan layanan dari kontainer DI selama pengaktifan aplikasi:
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();
Untuk informasi lebih lanjut, lihat Injeksi dependensi di ASP.NET Core.
WebApplicationBuilder
Bagian ini berisi kode sampel menggunakan WebApplicationBuilder.
Mengubah akar konten, nama aplikasi, dan lingkungan
Kode berikut mengatur akar konten, nama aplikasi, dan lingkungan:
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 menginisialisasi instans baru kelas WebApplicationBuilder dengan default yang telah dikonfigurasi sebelumnya.
Untuk informasi selengkapnya, lihat gambaran umum dasar-dasar ASP.NET Core
Mengubah akar konten, nama aplikasi, dan lingkungan menurut variabel lingkungan atau baris perintah
Tabel berikut ini memperlihatkan variabel lingkungan dan argumen baris perintah yang digunakan untuk mengubah akar konten, nama aplikasi, dan lingkungan:
| fitur | Variabel lingkungan | Argumen baris perintah |
|---|---|---|
| Nama aplikasi | ASPNETCORE_APPLICATIONNAME | --applicationName |
| Nama lingkungan | ASPNETCORE_ENVIRONMENT | --environment |
| Akar konten | ASPNETCORE_CONTENTROOT | --contentRoot |
Menambahkan penyedia konfigurasi
Contoh berikut menambahkan penyedia konfigurasi INI:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddIniFile("appsettings.ini");
var app = builder.Build();
Untuk informasi terperinci, lihat Penyedia konfigurasi file di Konfigurasi di ASP.NET Core.
Membaca konfigurasi
Secara default WebApplicationBuilder , konfigurasi baca dari beberapa sumber, termasuk:
-
appSettings.jsondanappSettings.{environment}.json - Variabel lingkungan
- Baris perintah
Untuk daftar lengkap sumber konfigurasi yang dibaca, lihat Konfigurasi default dalam Konfigurasi di ASP.NET Core
Kode berikut membaca HelloKey dari konfigurasi dan menampilkan nilai di / titik akhir. Jika nilai konfigurasi null, "Halo" ditetapkan ke message:
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Membaca lingkungan
var builder = WebApplication.CreateBuilder(args);
var message = builder.Configuration["HelloKey"] ?? "Hello";
var app = builder.Build();
app.MapGet("/", () => message);
app.Run();
Menambahkan penyedia pengelogan
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();
Menambahkan layanan
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();
Menyesuaikan IHostBuilder
Metode ekstensi yang ada di IHostBuilder dapat diakses menggunakan properti 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();
Menyesuaikan IWebHostBuilder
Metode ekstensi pada IWebHostBuilder dapat diakses menggunakan properti 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();
Mengubah akar web
Secara default, akar web relatif terhadap akar konten di wwwroot folder. Direktori root web adalah tempat Middleware File Statis mencari file statis. Akar web dapat diubah dengan WebHostOptions, baris perintah, atau dengan UseWebRoot metode :
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Look for static files in webroot
WebRootPath = "webroot"
});
var app = builder.Build();
app.Run();
Kontainer injeksi dependensi kustom (DI)
Contoh berikut menggunakan 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();
Tambahkan Middleware
Middleware ASP.NET Core yang ada dapat dikonfigurasi pada WebApplication:
var app = WebApplication.Create(args);
// Setup the file server to serve static files.
app.UseFileServer();
app.MapGet("/", () => "Hello World!");
app.Run();
Untuk informasi selengkapnya, lihat ASP.NET Core Middleware
Halaman pengecualian pengembang
WebApplication.CreateBuilder menginisialisasi instans WebApplicationBuilder baru kelas dengan default yang telah dikonfigurasi sebelumnya. Halaman pengecualian pengembang diaktifkan dalam default yang telah dikonfigurasi sebelumnya. Saat kode berikut dijalankan di lingkungan pengembangan, navigasi untuk / merender halaman ramah yang menunjukkan pengecualian.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () =>
{
throw new InvalidOperationException("Oops, the '/' route has thrown an exception.");
});
app.Run();
ASP.NET Core Middleware
Tabel berikut mencantumkan beberapa middleware yang sering digunakan dengan API minimal.
| Middleware | Description | API |
|---|---|---|
| Authentication | Menyediakan dukungan autentikasi. | UseAuthentication |
| Authorization | Menyediakan dukungan otorisasi. | UseAuthorization |
| CORS | Mengonfigurasi Cross-Origin Resource Sharing. | UseCors |
| Handler Pengecualian | Secara global menangani pengecualian yang dilemparkan oleh alur middleware. | UseExceptionHandler |
| Header yang Diteruskan | Meneruskan header yang diproksi ke permintaan saat ini. | UseForwardedHeaders |
| Pengalihan HTTPS | Mengalihkan semua permintaan HTTP ke HTTPS. | UseHttpsRedirection |
| Http Strict Transport Security (HSTS) | Middleware peningkatan keamanan yang menambahkan header respons khusus. | UseHsts |
| Pengelogan Permintaan | Menyediakan dukungan untuk mencatat permintaan dan respons HTTP. | UseHttpLogging |
| Pengelogan Permintaan W3C | Menyediakan dukungan untuk mencatat permintaan dan respons HTTP dalam format W3C. | UseW3CLogging |
| Penembolokan Respons | Menyediakan dukungan untuk respons penembolokan. | UseResponseCaching |
| Kompresi Respons | Memberikan dukungan untuk memadatkan respons. | UseResponseCompression |
| Session | Menyediakan dukungan untuk mengelola sesi pengguna. | UseSession |
| File Statis | Menyediakan dukungan untuk menyajikan file statis dan penjelajahan direktori. | UseStaticFiles, UseFileServer |
| WebSockets | Mengaktifkan protokol WebSocket. | UseWebSockets |
Penanganan permintaan
Bagian berikut mencakup perutean, pengikatan parameter, dan respons.
Routing
Dukungan yang dikonfigurasi WebApplicationMap{Verb} dan 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();
Penangan Rute
Penangan rute adalah metode yang dijalankan saat rute cocok. Penangan rute dapat menjadi fungsi dari bentuk apa pun, termasuk sinkron atau asinkron. Penangan rute dapat berupa ekspresi lambda, fungsi lokal, metode instans, atau metode statis.
Ekspresi 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();
Fungsi lokal
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Metode instans
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";
}
}
Metode statis
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";
}
}
Titik akhir dan pembuatan tautan bernama
Titik akhir dapat diberikan nama untuk menghasilkan URL ke titik akhir. Menggunakan titik akhir bernama menghindari harus melakukan jalur kode keras di aplikasi:
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();
Kode sebelumnya ditampilkan The link to the hello endpoint is /hello dari / titik akhir.
CATATAN: Nama titik akhir peka huruf besar/kecil.
Nama titik akhir:
- Harus unik secara global.
- Digunakan sebagai id operasi OpenAPI saat dukungan OpenAPI diaktifkan. Untuk informasi selengkapnya, lihat OpenAPI.
Parameter Rute
Parameter rute dapat ditangkap sebagai bagian dari definisi pola rute:
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();
Kode sebelumnya dikembalikan The user id is 3 and book id is 7 dari URI /users/3/books/7.
Handler rute dapat mendeklarasikan parameter yang akan diambil. Ketika permintaan dibuat rute dengan parameter yang dinyatakan untuk diambil, parameter diurai dan diteruskan ke handler. Ini memudahkan untuk menangkap nilai dengan cara yang aman. Dalam kode sebelumnya, userId dan bookId keduanya int.
Dalam kode sebelumnya, jika salah satu nilai rute tidak dapat dikonversi ke int, pengecualian akan dilemparkan. Permintaan /users/hello/books/3 GET melemparkan pengecualian berikut:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Kartubebas dan tangkap semua rute
Berikut ini menangkap semua rute yang Routing to hello dikembalikan dari titik akhir '/posts/hello':
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Batasan rute
Batasan rute membatasi perilaku pencocokan rute.
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();
Tabel berikut menunjukkan templat rute sebelumnya dan perilakunya:
| Templat Rute | Contoh URI yang Cocok |
|---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Untuk informasi selengkapnya, lihat Referensi batasan rute dalam Perutean di ASP.NET Core.
Pengikatan Parameter
Pengikatan parameter adalah proses mengonversi data permintaan menjadi parameter yang diekspresikan dengan kuat yang dinyatakan oleh penangan rute. Sumber pengikatan menentukan dari mana parameter terikat. Sumber pengikatan dapat eksplisit atau disimpulkan berdasarkan metode HTTP dan jenis parameter.
Sumber pengikatan yang didukung:
- Nilai rute
- Untai kueri
- Header
- Isi (sebagai JSON)
- Layanan yang disediakan oleh injeksi dependensi
- Custom
Note
Pengikatan dari nilai formulir tidak didukung secara asli di .NET.
Contoh handler rute GET berikut menggunakan beberapa sumber pengikatan parameter ini:
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 { }
Tabel berikut menunjukkan hubungan antara parameter yang digunakan dalam contoh sebelumnya dan sumber pengikatan terkait.
| Parameter | Sumber Pengikatan |
|---|---|
id |
nilai rute |
page |
string kueri |
customHeader |
header |
service |
Disediakan oleh injeksi dependensi |
Metode HTTP , GET, HEAD, dan OPTIONS tidak secara implisit DELETEmengikat dari isi. Untuk mengikat dari isi (sebagai JSON) untuk metode HTTP ini, ikat secara eksplisit dengan [FromBody] atau baca dari HttpRequest.
Contoh handler rute POST berikut menggunakan sumber isi pengikatan (sebagai JSON) untuk person parameter :
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/", (Person person) => { });
record Person(string Name, int Age);
Parameter dalam contoh sebelumnya semuanya terikat dari data permintaan secara otomatis. Untuk menunjukkan kenyamanan yang disediakan pengikatan parameter, contoh penanganan rute berikut menunjukkan cara membaca data permintaan langsung dari permintaan:
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>();
// ...
});
Pengikatan Parameter Eksplisit
Atribut dapat digunakan untuk secara eksplisit menyatakan dari mana parameter terikat.
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 | Sumber Pengikatan |
|---|---|
id |
nilai rute dengan nama id |
page |
string kueri dengan nama "p" |
service |
Disediakan oleh injeksi dependensi |
contentType |
header dengan nama "Content-Type" |
Note
Pengikatan dari nilai formulir tidak didukung secara asli di .NET.
Pengikatan parameter dengan DI
Pengikatan parameter untuk API minimal mengikat parameter melalui injeksi dependensi saat jenis dikonfigurasi sebagai layanan. Tidak perlu menerapkan [FromServices] atribut secara eksplisit ke parameter. Dalam kode berikut, kedua tindakan mengembalikan waktu:
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();
Parameter opsional
Parameter yang dideklarasikan dalam handler rute diperlakukan sesuai kebutuhan:
- Jika permintaan cocok dengan rute, handler rute hanya berjalan jika semua parameter yang diperlukan disediakan dalam permintaan.
- Kegagalan untuk memberikan semua parameter yang diperlukan menghasilkan kesalahan.
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 dikembalikan |
/products |
BadHttpRequestException: Parameter yang diperlukan "int pageNumber" tidak disediakan dari string kueri. |
/products/1 |
Kesalahan HTTP 404, tidak ada rute yang cocok |
Untuk membuat pageNumber opsional, tentukan jenis sebagai opsional atau berikan nilai default:
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 dikembalikan |
/products |
1 dikembalikan |
/products2 |
1 dikembalikan |
Nilai nullable dan default sebelumnya berlaku untuk semua sumber:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/products", (Product? product) => { });
app.Run();
Kode sebelumnya memanggil metode dengan produk null jika tidak ada isi permintaan yang dikirim.
CATATAN: Jika data yang tidak valid disediakan dan parameter dapat diubah ke null, handler rute tidak dijalankan.
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 Kembali |
/products |
1 Kembali |
/products?pageNumber=two |
BadHttpRequestException: Gagal mengikat parameter "Nullable<int> pageNumber" dari "dua". |
/products/two |
Kesalahan HTTP 404, tidak ada rute yang cocok |
Lihat bagian Kegagalan Pengikatan untuk informasi selengkapnya.
Jenis khusus
Jenis berikut terikat tanpa atribut eksplisit:
HttpContext: Konteks yang menyimpan semua informasi tentang permintaan atau respons HTTP saat ini:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));HttpRequest dan HttpResponse: Permintaan HTTP dan respons HTTP:
app.MapGet("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));CancellationToken: Token pembatalan yang terkait dengan permintaan HTTP saat ini:
app.MapGet("/", async (CancellationToken cancellationToken) => await MakeLongRunningRequestAsync(cancellationToken));ClaimsPrincipal: Pengguna yang terkait dengan permintaan, terikat dari HttpContext.User:
app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
Pengikatan Kustom
Ada dua cara untuk menyesuaikan pengikatan parameter:
- Untuk sumber pengikatan rute, kueri, dan header, ikat jenis kustom dengan menambahkan metode statis
TryParseuntuk jenis tersebut. - Kontrol proses pengikatan dengan menerapkan
BindAsyncmetode pada jenis.
TryParse
TryParse memiliki dua API:
public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);
Kode berikut ditampilkan Point: 12.3, 10.1 dengan 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 memiliki API berikut:
public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);
Kode berikut ditampilkan SortBy:xyz, SortDirection:Desc, CurrentPage:99 dengan 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
}
Kegagalan pengikatan
Saat pengikatan gagal, kerangka kerja mencatat pesan debug dan mengembalikan berbagai kode status ke klien tergantung pada mode kegagalan.
| Mode kegagalan | Jenis Parameter Nullable | Sumber Pengikatan | Kode status |
|---|---|---|---|
{ParameterType}.TryParse menghasilkan false |
yes | route/query/header | 400 |
{ParameterType}.BindAsync menghasilkan null |
yes | custom | 400 |
{ParameterType}.BindAsync Melempar |
tidak masalah | custom | 500 |
| Kegagalan untuk mendeserialisasi isi JSON | tidak masalah | body | 400 |
Tipe isi salah (bukan application/json) |
tidak masalah | body | 415 |
Prioritas Pengikatan
Aturan untuk menentukan sumber pengikatan dari parameter:
- Atribut eksplisit yang ditentukan pada parameter (Dari* atribut) dalam urutan berikut:
- Nilai rute:
[FromRoute] - String kueri:
[FromQuery] - Judul:
[FromHeader] - Badan:
[FromBody] - Layanan:
[FromServices]
- Nilai rute:
- Jenis khusus
HttpContext-
HttpRequestAku akan menemuinya.HttpContext.Request -
HttpResponseAku akan menemuinya.HttpContext.Response -
ClaimsPrincipalAku akan menemuinya.HttpContext.User -
CancellationTokenAku akan menemuinya.HttpContext.RequestAborted
- Jenis parameter memiliki metode yang valid
BindAsync. - Jenis parameter adalah string atau memiliki metode yang valid
TryParse.- Jika nama parameter ada di templat rute. Dalam
app.Map("/todo/{id}", (int id) => {});,idterikat dari rute. - Terikat dari string kueri.
- Jika nama parameter ada di templat rute. Dalam
- Jika jenis parameter adalah layanan yang disediakan oleh injeksi dependensi, ia menggunakan layanan tersebut sebagai sumbernya.
- Parameternya adalah dari isinya.
Menyesuaikan pengikatan JSON
Sumber pengikatan isi menggunakan System.Text.Json untuk de-serialisasi. Tidak dimungkinkan untuk mengubah default ini, tetapi pengikatan dapat disesuaikan menggunakan teknik lain yang dijelaskan sebelumnya. Untuk mengkustomisasi opsi serializer JSON, gunakan kode yang mirip dengan yang berikut ini:
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;
}
Kode sebelumnya:
- Mengonfigurasi opsi JSON default input dan output.
- Mengembalikan JSON berikut
Saat memposting{ "id": 1, "name": "Joe Smith" }{ "Id": 1, "Name": "Joe Smith" }
Membaca isi permintaan
Baca isi permintaan secara langsung menggunakan HttpContext parameter atau 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();
Kode sebelumnya:
- Mengakses isi permintaan menggunakan HttpRequest.BodyReader.
- Menyalin isi permintaan ke file lokal.
Responses
Handler rute mendukung jenis nilai pengembalian berikut:
-
IResultberbasis - Ini termasukTask<IResult>danValueTask<IResult> -
string- Ini termasukTask<string>danValueTask<string> -
T(Jenis lainnya) - Ini termasukTask<T>danValueTask<T>
| Mengembalikan nilai | Behavior | Content-Type |
|---|---|---|
IResult |
Kerangka kerja memanggil IResult.ExecuteAsync | Diputuskan IResult oleh implementasi |
string |
Kerangka kerja menulis string langsung ke respons | text/plain |
T (Jenis lainnya) |
Kerangka kerja akan menserialisasikan respons JSON | application/json |
Contoh nilai pengembalian
nilai pengembalian string
app.MapGet("/hello", () => "Hello World");
JSON mengembalikan nilai
app.MapGet("/hello", () => new { Message = "Hello World" });
Nilai pengembalian IResult
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
Contoh berikut menggunakan jenis hasil bawaan untuk mengkustomisasi respons:
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" }));
Kode Status Kustom
app.MapGet("/405", () => Results.StatusCode(405));
Text
app.MapGet("/text", () => Results.Text("This is some text"));
Stream
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () =>
{
var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
app.Run();
Redirect
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
File
app.MapGet("/download", () => Results.File("myfile.text"));
Hasil bawaan
Pembantu hasil umum ada di Microsoft.AspNetCore.Http.Results kelas statis.
| Description | Jenis respons | Kode Status | API |
|---|---|---|---|
| Menulis respons JSON dengan opsi tingkat lanjut | application/json | 200 | Results.Json |
| Menulis respons JSON | application/json | 200 | Results.Ok |
| Menulis respons teks | text/plain (default), dapat dikonfigurasi | 200 | Results.Text |
| Menulis respons sebagai byte | application/octet-stream (default), dapat dikonfigurasi | 200 | Results.Bytes |
| Menulis aliran byte ke respons | application/octet-stream (default), dapat dikonfigurasi | 200 | Results.Stream |
| Mengalirkan file ke respons untuk diunduh dengan header disposisi konten | application/octet-stream (default), dapat dikonfigurasi | 200 | Results.File |
| Atur kode status ke 404, dengan respons JSON opsional | N/A | 404 | Results.NotFound |
| Atur kode status ke 204 | N/A | 204 | Results.NoContent |
| Atur kode status ke 422, dengan respons JSON opsional | N/A | 422 | Results.UnprocessableEntity |
| Atur kode status ke 400, dengan respons JSON opsional | N/A | 400 | Results.BadRequest |
| Atur kode status ke 409, dengan respons JSON opsional | N/A | 409 | Results.Conflict |
| Menulis detail masalah objek JSON ke respons | N/A | 500 (default), dapat dikonfigurasi | Results.Problem |
| Menulis detail masalah objek JSON ke respons dengan kesalahan validasi | N/A | N/A, dapat dikonfigurasi | Results.ValidationProblem |
Menyesuaikan hasil
Aplikasi dapat mengontrol respons dengan menerapkan jenis kustom IResult . Kode berikut adalah contoh jenis hasil 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);
}
}
Sebaiknya tambahkan metode ekstensi untuk Microsoft.AspNetCore.Http.IResultExtensions membuat hasil kustom ini lebih dapat ditemukan.
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
Rute dapat dilindungi menggunakan kebijakan otorisasi. Ini dapat dideklarasikan melalui [Authorize] atribut atau dengan menggunakan RequireAuthorization metode :
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();
Kode sebelumnya dapat ditulis dengan RequireAuthorization:
app.MapGet("/auth", () => "This endpoint requires authorization")
.RequireAuthorization();
Sampel berikut menggunakan otorisasi berbasis kebijakan:
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();
Perbolehkan pengguna yang tidak diaauthenticated untuk mengakses titik akhir
memungkinkan [AllowAnonymous] pengguna yang tidak diaauthenticated untuk mengakses titik akhir:
app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for all roles.");
app.MapGet("/login2", () => "This endpoint also for all roles.")
.AllowAnonymous();
CORS
Rute dapat diaktifkan CORS menggunakan kebijakan CORS. CORS dapat dideklarasikan melalui [EnableCors] atribut atau dengan menggunakan RequireCors metode . Sampel berikut mengaktifkan 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();
Untuk informasi selengkapnya, lihat Mengaktifkan Permintaan Lintas Asal (CORS) di ASP.NET Core
Lihat juga
ASP.NET Core