Referensi cepat API minimal
Catatan
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.
Peringatan
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 8 dari artikel ini.
Penting
Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.
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 tergantung Minimal API applications
pada kondisi tertentu:
UseDeveloperExceptionPage
ditambahkan terlebih dahulu ketikaHostingEnvironment
adalah"Development"
.UseRouting
ditambahkan kedua jika kode pengguna belum memanggilUseRouting
dan jika ada titik akhir yang dikonfigurasi, misalnyaapp.MapGet
.UseEndpoints
ditambahkan di akhir alur middleware jika ada titik akhir yang dikonfigurasi.UseAuthentication
ditambahkan segera setelahUseRouting
jika kode pengguna belum memanggilUseAuthentication
dan apakahIAuthenticationSchemeProvider
dapat terdeteksi di penyedia layanan.IAuthenticationSchemeProvider
ditambahkan secara default saat menggunakanAddAuthentication
, dan layanan terdeteksi menggunakanIServiceProviderIsService
.UseAuthorization
ditambahkan berikutnya jika kode pengguna belum memanggilUseAuthorization
dan apakahIAuthorizationHandlerProvider
dapat terdeteksi di penyedia layanan.IAuthorizationHandlerProvider
ditambahkan secara default saat menggunakanAddAuthorization
, dan layanan terdeteksi menggunakanIServiceProviderIsService
.- Middleware dan titik akhir yang dikonfigurasi pengguna ditambahkan antara
UseRouting
danUseEndpoints
.
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
UseRouting
danUseEndpoints
sehingga 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 selengkapnya menggunakan lingkungan, lihat Menggunakan beberapa lingkungan di ASP.NET Core
Konfigurasi
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
Pencatatan
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 Pengelogan di .NET Core 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 | --lingkungan |
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.json
danappSettings.{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. Akar 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 | Deskripsi | API |
---|---|---|
Autentikasi | Menyediakan dukungan autentikasi. | UseAuthentication |
Otorisasi | 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 |
Pemadatan Respons | Memberikan dukungan untuk memadatkan respons. | UseResponseCompression |
Sesi | Menyediakan dukungan untuk mengelola sesi pengguna. | UseSession |
File Statis | Menyediakan dukungan untuk menyajikan file statis dan penjelajahan direktori. | UseStaticFiles, UseFileServer |
WebSocket | Mengaktifkan protokol WebSocket. | UseWebSockets |
Bagian berikut mencakup penanganan permintaan: perutean, pengikatan parameter, dan respons.
Perutean
Dukungan yang dikonfigurasi WebApplication
Map{Verb}
dan MapMethods di mana {Verb}
adalah metode HTTP camel-cased seperti Get
, , Post
Put
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
- Adat
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 |
{i>header |
service |
Disediakan oleh injeksi dependensi |
Metode HTTP , HEAD
, OPTIONS
, dan DELETE
tidak secara implisit GET
mengikat 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" |
Pengikatan eksplisit dari nilai formulir
Atribut [FromForm]
mengikat nilai formulir:
app.MapPost("/todos", async ([FromForm] string name,
[FromForm] Visibility visibility, IFormFile? attachment, TodoDb db) =>
{
var todo = new Todo
{
Name = name,
Visibility = visibility
};
if (attachment is not null)
{
var attachmentName = Path.GetRandomFileName();
using var stream = File.Create(Path.Combine("wwwroot", attachmentName));
await attachment.CopyToAsync(stream);
}
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Ok();
});
// Remaining code removed for brevity.
Alternatifnya adalah menggunakan [AsParameters]
atribut dengan jenis kustom yang memiliki properti yang dianotasi dengan [FromForm]
. Misalnya, kode berikut mengikat dari nilai formulir ke properti NewTodoRequest
struktur rekaman:
app.MapPost("/ap/todos", async ([AsParameters] NewTodoRequest request, TodoDb db) =>
{
var todo = new Todo
{
Name = request.Name,
Visibility = request.Visibility
};
if (request.Attachment is not null)
{
var attachmentName = Path.GetRandomFileName();
using var stream = File.Create(Path.Combine("wwwroot", attachmentName));
await request.Attachment.CopyToAsync(stream);
todo.Attachment = attachmentName;
}
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Ok();
});
// Remaining code removed for brevity.
public record struct NewTodoRequest([FromForm] string Name,
[FromForm] Visibility Visibility, IFormFile? Attachment);
Untuk informasi selengkapnya, lihat bagian tentang AsParameters nanti di artikel ini.
Kode sampel lengkap ada di repositori AspNetCore.Docs.Samples.
Pengikatan aman dari IFormFile dan IFormFileCollection
Pengikatan formulir kompleks didukung menggunakan IFormFile dan IFormFileCollection menggunakan [FromForm]
:
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
// Generate a form with an anti-forgery token and an /upload endpoint.
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
var html = MyUtils.GenerateHtmlForm(token.FormFieldName, token.RequestToken!);
return Results.Content(html, "text/html");
});
app.MapPost("/upload", async Task<Results<Ok<string>, BadRequest<string>>>
([FromForm] FileUploadForm fileUploadForm, HttpContext context,
IAntiforgery antiforgery) =>
{
await MyUtils.SaveFileWithName(fileUploadForm.FileDocument!,
fileUploadForm.Name!, app.Environment.ContentRootPath);
return TypedResults.Ok($"Your file with the description:" +
$" {fileUploadForm.Description} has been uploaded successfully");
});
app.Run();
Parameter yang terikat pada permintaan dengan [FromForm]
menyertakan token antiforgery. Token antiforgery divalidasi ketika permintaan diproses. Untuk informasi selengkapnya, lihat Antiforgery dengan API Minimal.
Untuk informasi selengkapnya, lihat Pengikatan formulir di API minimal.
Kode sampel lengkap ada di repositori AspNetCore.Docs.Samples.
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 | hasil |
---|---|
/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 | hasil |
---|---|
/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 | hasil |
---|---|
/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,
Stream
adalah 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.
Stream
danPipeReader
tidak 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.
Pengikatan ke formulir dengan IFormCollection, IFormFile, dan IFormFileCollection
Pengikatan dari parameter berbasis formulir menggunakan IFormCollection, IFormFile, dan IFormFileCollection didukung. Metadata OpenAPI disimpulkan untuk parameter formulir untuk mendukung integrasi dengan antarmuka pengguna Swagger.
Kode berikut mengunggah file menggunakan pengikatan yang disimpulkan dari jenis :IFormFile
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http.HttpResults;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
string GetOrCreateFilePath(string fileName, string filesDirectory = "uploadFiles")
{
var directoryPath = Path.Combine(app.Environment.ContentRootPath, filesDirectory);
Directory.CreateDirectory(directoryPath);
return Path.Combine(directoryPath, fileName);
}
async Task UploadFileWithName(IFormFile file, string fileSaveName)
{
var filePath = GetOrCreateFilePath(fileSaveName);
await using var fileStream = new FileStream(filePath, FileMode.Create);
await file.CopyToAsync(fileStream);
}
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
var html = $"""
<html>
<body>
<form action="/upload" method="POST" enctype="multipart/form-data">
<input name="{token.FormFieldName}" type="hidden" value="{token.RequestToken}"/>
<input type="file" name="file" placeholder="Upload an image..." accept=".jpg,
.jpeg, .png" />
<input type="submit" />
</form>
</body>
</html>
""";
return Results.Content(html, "text/html");
});
app.MapPost("/upload", async Task<Results<Ok<string>,
BadRequest<string>>> (IFormFile file, HttpContext context, IAntiforgery antiforgery) =>
{
var fileSaveName = Guid.NewGuid().ToString("N") + Path.GetExtension(file.FileName);
await UploadFileWithName(file, fileSaveName);
return TypedResults.Ok("File uploaded successfully!");
});
app.Run();
Peringatan: Saat menerapkan formulir, aplikasi harus mencegah serangan Pemalsuan Permintaan Lintas Situs (XSRF/CSRF). Dalam kode sebelumnya, IAntiforgery layanan digunakan untuk mencegah serangan XSRF dengan menghasilkan dan memvalidasi token antiforgeri:
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http.HttpResults;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
string GetOrCreateFilePath(string fileName, string filesDirectory = "uploadFiles")
{
var directoryPath = Path.Combine(app.Environment.ContentRootPath, filesDirectory);
Directory.CreateDirectory(directoryPath);
return Path.Combine(directoryPath, fileName);
}
async Task UploadFileWithName(IFormFile file, string fileSaveName)
{
var filePath = GetOrCreateFilePath(fileSaveName);
await using var fileStream = new FileStream(filePath, FileMode.Create);
await file.CopyToAsync(fileStream);
}
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
var html = $"""
<html>
<body>
<form action="/upload" method="POST" enctype="multipart/form-data">
<input name="{token.FormFieldName}" type="hidden" value="{token.RequestToken}"/>
<input type="file" name="file" placeholder="Upload an image..." accept=".jpg,
.jpeg, .png" />
<input type="submit" />
</form>
</body>
</html>
""";
return Results.Content(html, "text/html");
});
app.MapPost("/upload", async Task<Results<Ok<string>,
BadRequest<string>>> (IFormFile file, HttpContext context, IAntiforgery antiforgery) =>
{
var fileSaveName = Guid.NewGuid().ToString("N") + Path.GetExtension(file.FileName);
await UploadFileWithName(file, fileSaveName);
return TypedResults.Ok("File uploaded successfully!");
});
app.Run();
Untuk informasi selengkapnya tentang serangan XSRF, lihat Antiforgery dengan API Minimal
Untuk informasi selengkapnya, lihat Pengikatan formulir dalam API minimal;
Ikat ke koleksi dan jenis kompleks dari formulir
Pengikatan didukung untuk:
Kode berikut menunjukkan:
- Titik akhir minimal yang mengikat input formulir multi-bagian ke objek kompleks.
- Cara menggunakan layanan antiforgery untuk mendukung pembuatan dan validasi token antiforgery.
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
var html = $"""
<html><body>
<form action="/todo" method="POST" enctype="multipart/form-data">
<input name="{token.FormFieldName}"
type="hidden" value="{token.RequestToken}" />
<input type="text" name="name" />
<input type="date" name="dueDate" />
<input type="checkbox" name="isCompleted" value="true" />
<input type="submit" />
<input name="isCompleted" type="hidden" value="false" />
</form>
</body></html>
""";
return Results.Content(html, "text/html");
});
app.MapPost("/todo", async Task<Results<Ok<Todo>, BadRequest<string>>>
([FromForm] Todo todo, HttpContext context, IAntiforgery antiforgery) =>
{
try
{
await antiforgery.ValidateRequestAsync(context);
return TypedResults.Ok(todo);
}
catch (AntiforgeryValidationException e)
{
return TypedResults.BadRequest("Invalid antiforgery token");
}
});
app.Run();
class Todo
{
public string Name { get; set; } = string.Empty;
public bool IsCompleted { get; set; } = false;
public DateTime DueDate { get; set; } = DateTime.Now.Add(TimeSpan.FromDays(1));
}
Dalam kode sebelumnya:
- Parameter target harus diannotasi dengan
[FromForm]
atribut untuk membedakan dari parameter yang harus dibaca dari isi JSON. - Pengikatan dari jenis kompleks atau koleksi tidak didukung untuk API minimal yang dikompilasi dengan Generator Delegasi Permintaan.
- Markup menunjukkan input tersembunyi tambahan dengan nama
isCompleted
dan nilaifalse
. Jika kotakisCompleted
centang dicentang saat formulir dikirimkan, kedua nilaitrue
danfalse
dikirimkan sebagai nilai. Jika kotak centang tidak dicentang, hanya nilaifalse
input tersembunyi yang dikirimkan. Proses pengikatan model ASP.NET Core hanya membaca nilai pertama saat mengikatbool
nilai, yang menghasilkan kotak centang yang dicentangtrue
danfalse
untuk kotak centang yang tidak dicentang.
Contoh data formulir yang dikirimkan ke titik akhir sebelumnya terlihat sebagai berikut:
__RequestVerificationToken: CfDJ8Bveip67DklJm5vI2PF2VOUZ594RC8kcGWpTnVV17zCLZi1yrs-CSz426ZRRrQnEJ0gybB0AD7hTU-0EGJXDU-OaJaktgAtWLIaaEWMOWCkoxYYm-9U9eLV7INSUrQ6yBHqdMEE_aJpD4AI72gYiCqc
name: Walk the dog
dueDate: 2024-04-06
isCompleted: true
isCompleted: false
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 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();
});
Catatan
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 dua cara untuk menyesuaikan pengikatan parameter:
- Untuk sumber pengikatan rute, kueri, dan header, ikat jenis kustom dengan menambahkan metode statis
TryParse
untuk jenis tersebut. - Kontrol proses pengikatan dengan menerapkan
BindAsync
metode 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 penting | custom | 500 |
Kegagalan untuk mendeserialisasi isi JSON | tidak penting | body | 400 |
Tipe isi salah (bukan application/json ) |
tidak penting | 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]
- Header:
[FromHeader]
- Badan:
[FromBody]
- Bentuk:
[FromForm]
- Layanan:
[FromServices]
- Nilai parameter:
[AsParameters]
- Nilai rute:
- Jenis khusus
HttpContext
HttpRequest
Aku akan menemuinya.HttpContext.Request
HttpResponse
Aku akan menemuinya.HttpContext.Response
ClaimsPrincipal
Aku akan menemuinya.HttpContext.User
CancellationToken
Aku akan menemuinya.HttpContext.RequestAborted
IFormCollection
Aku akan menemuinya.HttpContext.Request.Form
IFormFileCollection
Aku akan menemuinya.HttpContext.Request.Form.Files
IFormFile
Aku akan menemuinya.HttpContext.Request.Form.Files[paramName]
Stream
Aku akan menemuinya.HttpContext.Request.Body
PipeReader
Aku akan menemuinya.HttpContext.Request.BodyReader
- Jenis parameter memiliki metode statis
BindAsync
yang valid. - Jenis parameter adalah string atau memiliki metode statis
TryParse
yang valid.- Jika nama parameter ada di templat rute misalnya,
app.Map("/todo/{id}", (int id) => {});
, maka terikat dari rute. - Terikat dari string kueri.
- Jika nama parameter ada di templat rute misalnya,
- 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.
Respons
Handler rute mendukung jenis nilai pengembalian berikut:
IResult
berbasis - Ini termasukTask<IResult>
danValueTask<IResult>
string
- Ini termasukTask<string>
danValueTask<string>
T
(Jenis lainnya) - Ini termasukTask<T>
danValueTask<T>
Nilai hasil | Perilaku | 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));
Teks
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.
Pengalihan
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.
Filter
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 dalam API minimal
- 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 tergantung Minimal API applications
pada kondisi tertentu:
UseDeveloperExceptionPage
ditambahkan terlebih dahulu ketikaHostingEnvironment
adalah"Development"
.UseRouting
ditambahkan kedua jika kode pengguna belum memanggilUseRouting
dan jika ada titik akhir yang dikonfigurasi, misalnyaapp.MapGet
.UseEndpoints
ditambahkan di akhir alur middleware jika ada titik akhir yang dikonfigurasi.UseAuthentication
ditambahkan segera setelahUseRouting
jika kode pengguna belum memanggilUseAuthentication
dan apakahIAuthenticationSchemeProvider
dapat terdeteksi di penyedia layanan.IAuthenticationSchemeProvider
ditambahkan secara default saat menggunakanAddAuthentication
, dan layanan terdeteksi menggunakanIServiceProviderIsService
.UseAuthorization
ditambahkan berikutnya jika kode pengguna belum memanggilUseAuthorization
dan apakahIAuthorizationHandlerProvider
dapat terdeteksi di penyedia layanan.IAuthorizationHandlerProvider
ditambahkan secara default saat menggunakanAddAuthorization
, dan layanan terdeteksi menggunakanIServiceProviderIsService
.- Middleware dan titik akhir yang dikonfigurasi pengguna ditambahkan antara
UseRouting
danUseEndpoints
.
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
UseRouting
danUseEndpoints
sehingga 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 selengkapnya menggunakan lingkungan, lihat Menggunakan beberapa lingkungan di 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();
Konfigurasi
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
Pencatatan
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 Pengelogan di .NET Core 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 | --lingkungan |
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.json
danappSettings.{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. Akar 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 | Deskripsi | API |
---|---|---|
Autentikasi | Menyediakan dukungan autentikasi. | UseAuthentication |
Otorisasi | 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 |
Pemadatan Respons | Memberikan dukungan untuk memadatkan respons. | UseResponseCompression |
Sesi | Menyediakan dukungan untuk mengelola sesi pengguna. | UseSession |
File Statis | Menyediakan dukungan untuk menyajikan file statis dan penjelajahan direktori. | UseStaticFiles, UseFileServer |
WebSocket | Mengaktifkan protokol WebSocket. | UseWebSockets |
Bagian berikut mencakup penanganan permintaan: perutean, pengikatan parameter, dan respons.
Perutean
Dukungan yang dikonfigurasi WebApplication
Map{Verb}
dan MapMethods di mana {Verb}
adalah metode HTTP camel-cased seperti Get
, , Post
Put
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
- Adat
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 |
{i>header |
service |
Disediakan oleh injeksi dependensi |
Metode HTTP , HEAD
, OPTIONS
, dan DELETE
tidak secara implisit GET
mengikat 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" |
Catatan
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 | hasil |
---|---|
/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 | hasil |
---|---|
/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 | hasil |
---|---|
/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,
Stream
adalah 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.
Stream
danPipeReader
tidak 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 7.0. Antiforgery tersedia di ASP.NET Core 8.0 dan 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();
});
Catatan
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 dua cara untuk menyesuaikan pengikatan parameter:
- Untuk sumber pengikatan rute, kueri, dan header, ikat jenis kustom dengan menambahkan metode statis
TryParse
untuk jenis tersebut. - Kontrol proses pengikatan dengan menerapkan
BindAsync
metode 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]
- Header:
[FromHeader]
- Badan:
[FromBody]
- Layanan:
[FromServices]
- Nilai parameter:
[AsParameters]
- Nilai rute:
- Jenis khusus
HttpContext
HttpRequest
Aku akan menemuinya.HttpContext.Request
HttpResponse
Aku akan menemuinya.HttpContext.Response
ClaimsPrincipal
Aku akan menemuinya.HttpContext.User
CancellationToken
Aku akan menemuinya.HttpContext.RequestAborted
IFormFileCollection
Aku akan menemuinya.HttpContext.Request.Form.Files
IFormFile
Aku akan menemuinya.HttpContext.Request.Form.Files[paramName]
Stream
Aku akan menemuinya.HttpContext.Request.Body
PipeReader
Aku akan menemuinya.HttpContext.Request.BodyReader
- Jenis parameter memiliki metode statis
BindAsync
yang valid. - Jenis parameter adalah string atau memiliki metode statis
TryParse
yang valid.- Jika nama parameter ada di templat rute. Dalam
app.Map("/todo/{id}", (int id) => {});
,id
terikat 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.
Respons
Handler rute mendukung jenis nilai pengembalian berikut:
IResult
berbasis - Ini termasukTask<IResult>
danValueTask<IResult>
string
- Ini termasukTask<string>
danValueTask<string>
T
(Jenis lainnya) - Ini termasukTask<T>
danValueTask<T>
Nilai hasil | Perilaku | 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));
Teks
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.
Pengalihan
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.
Filter
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 selengkapnya menggunakan lingkungan, lihat Menggunakan beberapa lingkungan di ASP.NET Core
Konfigurasi
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
Pencatatan
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 Pengelogan di .NET Core 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 | --lingkungan |
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.json
danappSettings.{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. Akar 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 | Deskripsi | API |
---|---|---|
Autentikasi | Menyediakan dukungan autentikasi. | UseAuthentication |
Otorisasi | 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 |
Pemadatan Respons | Memberikan dukungan untuk memadatkan respons. | UseResponseCompression |
Sesi | Menyediakan dukungan untuk mengelola sesi pengguna. | UseSession |
File Statis | Menyediakan dukungan untuk menyajikan file statis dan penjelajahan direktori. | UseStaticFiles, UseFileServer |
WebSocket | Mengaktifkan protokol WebSocket. | UseWebSockets |
Penanganan permintaan
Bagian berikut mencakup perutean, pengikatan parameter, dan respons.
Perutean
Dukungan yang dikonfigurasi WebApplication
Map{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
- Adat
Catatan
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 |
{i>header |
service |
Disediakan oleh injeksi dependensi |
Metode HTTP , HEAD
, OPTIONS
, dan DELETE
tidak secara implisit GET
mengikat 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" |
Catatan
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 | hasil |
---|---|
/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 | hasil |
---|---|
/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 | hasil |
---|---|
/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
TryParse
untuk jenis tersebut. - Kontrol proses pengikatan dengan menerapkan
BindAsync
metode 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]
- Header:
[FromHeader]
- Badan:
[FromBody]
- Layanan:
[FromServices]
- Nilai rute:
- Jenis khusus
HttpContext
HttpRequest
Aku akan menemuinya.HttpContext.Request
HttpResponse
Aku akan menemuinya.HttpContext.Response
ClaimsPrincipal
Aku akan menemuinya.HttpContext.User
CancellationToken
Aku 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) => {});
,id
terikat 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.
Respons
Handler rute mendukung jenis nilai pengembalian berikut:
IResult
berbasis - Ini termasukTask<IResult>
danValueTask<IResult>
string
- Ini termasukTask<string>
danValueTask<string>
T
(Jenis lainnya) - Ini termasukTask<T>
danValueTask<T>
Nilai hasil | Perilaku | 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));
Teks
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();
Pengalihan
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.
Deskripsi | 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.Byte |
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 | T/A | 404 | Results.NotFound |
Atur kode status ke 204 | T/A | 204 | Results.NoContent |
Atur kode status ke 422, dengan respons JSON opsional | T/A | 422 | Results.UnprocessableEntity |
Atur kode status ke 400, dengan respons JSON opsional | T/A | 400 | Results.BadRequest |
Atur kode status ke 409, dengan respons JSON opsional | T/A | 409 | Results.Conflict |
Menulis detail masalah objek JSON ke respons | T/A | 500 (default), dapat dikonfigurasi | Results.Problem |
Menulis detail masalah objek JSON ke respons dengan kesalahan validasi | T/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