Tutorial: Membuat API minimal dengan ASP.NET Core
Catatan
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 8 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 8 dari artikel ini.
Oleh Rick Anderson dan Tom Dykstra
API minimal dirancang untuk membuat API HTTP dengan dependensi minimal. Mereka ideal untuk layanan mikro dan aplikasi yang hanya ingin menyertakan file, fitur, dan dependensi minimum di ASP.NET Core.
Tutorial ini mengajarkan dasar-dasar membangun API minimal dengan ASP.NET Core. Pendekatan lain untuk membuat API di ASP.NET Core adalah menggunakan pengontrol. Untuk bantuan dalam memilih antara API minimal dan API berbasis pengontrol, lihat Gambaran umum API. Untuk tutorial tentang membuat proyek API berdasarkan pengontrol yang berisi lebih banyak fitur, lihat Membuat API web.
Gambaran Umum
Tutorial ini membuat API berikut:
API | Deskripsi | Isi permintaan | Isi respons |
---|---|---|---|
GET /todoitems |
Dapatkan semua item yang harus dilakukan | Tidak | Array item yang harus dilakukan |
GET /todoitems/complete |
Selesaikan item yang harus dilakukan | Tidak | Array item yang harus dilakukan |
GET /todoitems/{id} |
Mendapatkan item menurut ID | Tidak | Item yang harus dilakukan |
POST /todoitems |
Menambahkan item baru | Item yang harus dilakukan | Item yang harus dilakukan |
PUT /todoitems/{id} |
Memperbarui item yang sudah ada | Item yang harus dilakukan | Tidak |
DELETE /todoitems/{id} |
Menghapus item | Tidak | Tidak |
Prasyarat
Pratinjau Visual Studio 2022 dengan beban kerja pengembangan ASP.NET dan web.
Membuat proyek API
Mulai Visual Studio 2022 dan pilih Buat proyek baru.
Dalam dialog Buat proyek baru:
- Masukkan
Empty
di kotak pencarian Cari templat . - Pilih templat ASP.NET Core Empty dan pilih Berikutnya.
- Masukkan
Beri nama proyek TodoApi dan pilih Berikutnya.
Dalam dialog Informasi tambahan:
- Pilih .NET 9.0 (Pratinjau)
- Hapus centang Jangan gunakan pernyataan tingkat atas
- Pilih Buat
Memeriksa kode
File Program.cs
berisi kode berikut:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Kode sebelumnya:
- WebApplicationBuilder Membuat dan WebApplication dengan default yang telah dikonfigurasi sebelumnya.
- Membuat titik
/
akhir HTTP GET yang mengembalikanHello World!
:
Menjalankan aplikasi
Tekan Ctrl+F5 untuk menjalankan tanpa debugger.
Visual Studio menampilkan dialog berikut:
Pilih Ya jika Anda mempercayai sertifikat IIS Express SSL.
Dialog berikut ditampilkan:
Pilih Ya jika Anda setuju untuk mempercayai sertifikat pengembangan tersebut.
Untuk informasi tentang mempercayai browser Firefox, lihat Kesalahan sertifikat Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.
Visual Studio meluncurkan Kestrel server web dan membuka jendela browser.
Hello World!
ditampilkan di browser. File Program.cs
berisi aplikasi minimal tetapi lengkap.
Tutup jendela browser.
Menambahkan paket NuGet
Paket NuGet harus ditambahkan untuk mendukung database dan diagnostik yang digunakan dalam tutorial ini.
- Dari menu Alat, pilih Pengelola > Paket NuGet Kelola Paket NuGet untuk Solusi.
- Pilih tab Telusuri.
- Pilih Sertakan Pralease.
- Masukkan Microsoft.EntityFrameworkCore.InMemory di kotak pencarian, lalu pilih
Microsoft.EntityFrameworkCore.InMemory
. - Pilih kotak centang Proyek di panel kanan lalu pilih Instal.
- Ikuti instruksi sebelumnya untuk menambahkan
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
paket.
Kelas konteks model dan database
- Di folder proyek, buat file bernama
Todo.cs
dengan kode berikut:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Kode sebelumnya membuat model untuk aplikasi ini. Model adalah kelas yang mewakili data yang dikelola aplikasi.
- Buat file bernama
TodoDb.cs
dengan kode berikut:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
Kode sebelumnya mendefinisikan konteks database, yang merupakan kelas utama yang mengoordinasikan fungsionalitas Kerangka Kerja Entitas untuk model data. Kelas ini berasal dari Microsoft.EntityFrameworkCore.DbContext kelas .
Menambahkan kode API
- Ganti isi file
Program.cs
dengan kode berikut:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync());
app.MapGet("/todoitems/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
app.MapPut("/todoitems/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.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.NoContent();
}
return Results.NotFound();
});
app.Run();
Kode yang disorot berikut menambahkan konteks database ke kontainer injeksi dependensi (DI) dan memungkinkan menampilkan pengecualian terkait database:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
Kontainer DI menyediakan akses ke konteks database dan layanan lainnya.
Tutorial ini menggunakan file Endpoints Explorer dan .http untuk menguji API.
Menguji data posting
Kode berikut dalam Program.cs
membuat titik /todoitems
akhir HTTP POST yang menambahkan data ke database dalam memori:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
Jalankan aplikasi. Browser menampilkan kesalahan 404 karena tidak ada lagi /
titik akhir.
Titik akhir POST akan digunakan untuk menambahkan data ke aplikasi.
Pilih Tampilkan>Penjelajah Titik Akhir Windows>Lainnya.
Klik kanan titik akhir POST dan pilih Buat permintaan.
File baru dibuat di folder proyek bernama
TodoApi.http
, dengan konten yang mirip dengan contoh berikut:@TodoApi_HostAddress = https://localhost:7031 Post {{TodoApi_HostAddress}}/todoitems ###
- Baris pertama membuat variabel yang digunakan untuk semua titik akhir.
- Baris berikutnya menentukan permintaan POST.
- Baris hashtag triple (
###
) adalah pemisah permintaan: apa yang terjadi setelahnya adalah untuk permintaan yang berbeda.
Permintaan POST memerlukan header dan isi. Untuk menentukan bagian permintaan tersebut, tambahkan baris berikut segera setelah baris permintaan POST:
Content-Type: application/json { "name":"walk dog", "isComplete":true }
Kode sebelumnya menambahkan header Jenis Konten dan JSisi permintaan ON. File TodoApi.http sekarang akan terlihat seperti contoh berikut, tetapi dengan nomor port Anda:
@TodoApi_HostAddress = https://localhost:7057 Post {{TodoApi_HostAddress}}/todoitems Content-Type: application/json { "name":"walk dog", "isComplete":true } ###
Jalankan aplikasi.
Pilih tautan Kirim permintaan yang berada di atas
POST
baris permintaan.Permintaan POST dikirim ke aplikasi dan respons ditampilkan di panel Respons .
Memeriksa titik akhir GET
Aplikasi sampel mengimplementasikan beberapa titik akhir GET dengan memanggil MapGet
:
API | Deskripsi | Isi permintaan | Isi respons |
---|---|---|---|
GET /todoitems |
Dapatkan semua item yang harus dilakukan | Tidak | Array item yang harus dilakukan |
GET /todoitems/complete |
Dapatkan semua item tugas yang sudah selesai | Tidak | Array item yang harus dilakukan |
GET /todoitems/{id} |
Mendapatkan item menurut ID | Tidak | Item yang harus dilakukan |
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync());
app.MapGet("/todoitems/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
Menguji titik akhir GET
Uji aplikasi dengan memanggil GET
titik akhir dari browser atau dengan menggunakan Endpoints Explorer. Langkah-langkah berikut adalah untuk Penjelajah Titik Akhir.
Di Penjelajah Titik Akhir, klik kanan titik akhir GET pertama, dan pilih Buat permintaan.
Konten berikut ditambahkan ke
TodoApi.http
file:Get {{TodoApi_HostAddress}}/todoitems ###
Pilih tautan Kirim permintaan yang berada di atas baris permintaan baru
GET
.Permintaan GET dikirim ke aplikasi dan respons ditampilkan di panel Respons .
Isi respons mirip dengan ON berikut JS:
[ { "id": 1, "name": "walk dog", "isComplete": true } ]
Di Penjelajah Titik Akhir, klik
/todoitems/{id}
kanan titik akhir GET dan pilih Hasilkan permintaan. Konten berikut ditambahkan keTodoApi.http
file:GET {{TodoApi_HostAddress}}/todoitems/{id} ###
Ganti
{id}
dengan1
.Pilih tautan Kirim permintaan yang berada di atas baris permintaan GET baru.
Permintaan GET dikirim ke aplikasi dan respons ditampilkan di panel Respons .
Isi respons mirip dengan ON berikut JS:
{ "id": 1, "name": "walk dog", "isComplete": true }
Aplikasi ini menggunakan database dalam memori. Jika aplikasi dimulai ulang, permintaan GET tidak mengembalikan data apa pun. Jika tidak ada data yang dikembalikan, KIRIM data ke aplikasi dan coba permintaan GET lagi.
Mengembalikan nilai
ASP.NET Core secara otomatis menserialisasikan objek ke JSAKTIF dan menulis JSON ke dalam isi pesan respons. Kode respons untuk jenis pengembalian ini adalah 200 OK, dengan asumsi tidak ada pengecualian yang tidak tertangani. Pengecualian yang tidak tertangani diterjemahkan ke dalam kesalahan 5xx.
Jenis pengembalian dapat mewakili berbagai kode status HTTP. Misalnya, GET /todoitems/{id}
dapat mengembalikan dua nilai status yang berbeda:
- Jika tidak ada item yang cocok dengan ID yang diminta, metode mengembalikan kode kesalahan status NotFound 404.
- Jika tidak, metode mengembalikan 200 dengan JSisi respons ON. Mengembalikan
item
hasil dalam respons HTTP 200.
Memeriksa titik akhir PUT
Aplikasi sampel mengimplementasikan satu titik akhir PUT menggunakan MapPut
:
app.MapPut("/todoitems/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
});
Metode ini mirip MapPost
dengan metode , kecuali menggunakan HTTP PUT. Respons yang berhasil mengembalikan 204 (Tanpa Konten). Menurut spesifikasi HTTP, permintaan PUT mengharuskan klien untuk mengirim seluruh entitas yang diperbarui, bukan hanya perubahan. Untuk mendukung pembaruan parsial, gunakan HTTP PATCH.
Menguji titik akhir PUT
Sampel ini menggunakan database dalam memori yang harus diinisialisasi setiap kali aplikasi dimulai. Harus ada item dalam database sebelum Anda melakukan panggilan PUT. Panggil GET untuk memastikan ada item dalam database sebelum melakukan panggilan PUT.
Perbarui item yang harus dilakukan yang memiliki Id = 1
dan atur namanya ke "feed fish"
.
Di Penjelajah Titik Akhir, klik kanan titik akhir PUT , dan pilih Buat permintaan.
Konten berikut ditambahkan ke
TodoApi.http
file:Put {{TodoApi_HostAddress}}/todoitems/{id} ###
Di baris permintaan PUT, ganti
{id}
dengan1
.Tambahkan baris berikut segera setelah baris permintaan PUT:
Content-Type: application/json { "name": "feed fish", "isComplete": false }
Kode sebelumnya menambahkan header Jenis Konten dan JSisi permintaan ON.
Pilih tautan Kirim permintaan yang berada di atas baris permintaan PUT baru.
Permintaan PUT dikirim ke aplikasi dan respons ditampilkan di panel Respons . Isi respons kosong, dan kode status adalah 204.
Memeriksa dan menguji titik akhir DELETE
Aplikasi sampel mengimplementasikan satu titik akhir DELETE menggunakan MapDelete
:
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.NoContent();
}
return Results.NotFound();
});
Di Penjelajah Titik Akhir, klik kanan titik akhir DELETE dan pilih Hasilkan permintaan.
Permintaan DELETE ditambahkan ke
TodoApi.http
.Ganti
{id}
di baris permintaan DELETE dengan1
. Permintaan DELETE akan terlihat seperti contoh berikut:DELETE {{TodoApi_HostAddress}}/todoitems/1 ###
Pilih tautan Kirim permintaan untuk permintaan DELETE.
Permintaan DELETE dikirim ke aplikasi dan respons ditampilkan di panel Respons . Isi respons kosong, dan kode status adalah 204.
Menggunakan API MapGroup
Kode aplikasi sampel mengulangi awalan todoitems
URL setiap kali menyiapkan titik akhir. API sering memiliki grup titik akhir dengan awalan URL umum, dan metode ini tersedia untuk membantu mengatur grup tersebut MapGroup . Ini mengurangi kode berulang dan memungkinkan untuk menyesuaikan seluruh grup titik akhir dengan satu panggilan ke metode seperti RequireAuthorization dan WithMetadata.
Ganti isi Program.cs
dengan kode berikut:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", async (TodoDb db) =>
await db.Todos.ToListAsync());
todoItems.MapGet("/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
todoItems.MapGet("/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
todoItems.MapPost("/", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
todoItems.MapPut("/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
});
todoItems.MapDelete("/{id}", async (int id, TodoDb db) =>
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return Results.NoContent();
}
return Results.NotFound();
});
app.Run();
Kode sebelumnya memiliki perubahan berikut:
var todoItems = app.MapGroup("/todoitems");
Menambahkan untuk menyiapkan grup menggunakan awalan/todoitems
URL .- Mengubah semua metode menjadi
app.Map<HttpVerb>
todoItems.Map<HttpVerb>
. - Menghapus awalan
/todoitems
URL dariMap<HttpVerb>
panggilan metode.
Uji titik akhir untuk memverifikasi bahwa titik akhir berfungsi sama.
Menggunakan API TypedResults
TypedResults Mengembalikan daripada Results memiliki beberapa keuntungan, termasuk uji coba dan secara otomatis mengembalikan metadata jenis respons untuk OpenAPI untuk menjelaskan titik akhir. Untuk informasi selengkapnya, lihat TypedResults vs Results.
Metode ini Map<HttpVerb>
dapat memanggil metode handler rute alih-alih menggunakan lambda. Untuk melihat contoh, perbarui Program.cs dengan kode berikut:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", GetAllTodos);
todoItems.MapGet("/complete", GetCompleteTodos);
todoItems.MapGet("/{id}", GetTodo);
todoItems.MapPost("/", CreateTodo);
todoItems.MapPut("/{id}", UpdateTodo);
todoItems.MapDelete("/{id}", DeleteTodo);
app.Run();
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
static async Task<IResult> GetCompleteTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).ToListAsync());
}
static async Task<IResult> GetTodo(int id, TodoDb db)
{
return await db.Todos.FindAsync(id)
is Todo todo
? TypedResults.Ok(todo)
: TypedResults.NotFound();
}
static async Task<IResult> CreateTodo(Todo todo, TodoDb db)
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return TypedResults.Created($"/todoitems/{todo.Id}", todo);
}
static async Task<IResult> UpdateTodo(int id, Todo inputTodo, TodoDb db)
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
static async Task<IResult> DeleteTodo(int id, TodoDb db)
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
return TypedResults.NotFound();
}
Kode Map<HttpVerb>
sekarang memanggil metode alih-alih lambda:
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", GetAllTodos);
todoItems.MapGet("/complete", GetCompleteTodos);
todoItems.MapGet("/{id}", GetTodo);
todoItems.MapPost("/", CreateTodo);
todoItems.MapPut("/{id}", UpdateTodo);
todoItems.MapDelete("/{id}", DeleteTodo);
Metode ini mengembalikan objek yang mengimplementasikan IResult dan didefinisikan oleh TypedResults:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
static async Task<IResult> GetCompleteTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).ToListAsync());
}
static async Task<IResult> GetTodo(int id, TodoDb db)
{
return await db.Todos.FindAsync(id)
is Todo todo
? TypedResults.Ok(todo)
: TypedResults.NotFound();
}
static async Task<IResult> CreateTodo(Todo todo, TodoDb db)
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return TypedResults.Created($"/todoitems/{todo.Id}", todo);
}
static async Task<IResult> UpdateTodo(int id, Todo inputTodo, TodoDb db)
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
static async Task<IResult> DeleteTodo(int id, TodoDb db)
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
return TypedResults.NotFound();
}
Pengujian unit dapat memanggil metode ini dan menguji bahwa mereka mengembalikan jenis yang benar. Misalnya, jika metodenya adalah GetAllTodos
:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Kode pengujian unit dapat memverifikasi bahwa objek jenis Ok<Todo[]> dikembalikan dari metode handler. Contohnya:
public async Task GetAllTodos_ReturnsOkOfTodosResult()
{
// Arrange
var db = CreateDbContext();
// Act
var result = await TodosApi.GetAllTodos(db);
// Assert: Check for the correct returned type
Assert.IsType<Ok<Todo[]>>(result);
}
Cegah postingan berlebihan
Saat ini aplikasi sampel mengekspos seluruh Todo
objek. Aplikasi produksi Dalam aplikasi produksi, subset model sering digunakan untuk membatasi data yang dapat diinput dan dikembalikan. Ada beberapa alasan di balik ini dan keamanan adalah yang utama. Subset model biasanya disebut sebagai Objek Transfer Data (DTO), model input, atau model tampilan. DTO digunakan dalam artikel ini.
DTO dapat digunakan untuk:
- Cegah postingan berlebihan.
- Sembunyikan properti yang seharusnya tidak dilihat klien.
- Hilangkan beberapa properti untuk mengurangi ukuran payload.
- Meratakan grafik objek yang berisi objek berlapis. Grafik objek yang diratakan bisa lebih nyaman untuk klien.
Untuk menunjukkan pendekatan DTO, perbarui Todo
kelas untuk menyertakan bidang rahasia:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Bidang rahasia perlu disembunyikan dari aplikasi ini, tetapi aplikasi administratif dapat memilih untuk mengeksposnya.
Verifikasi bahwa Anda dapat memposting dan mendapatkan bidang rahasia.
Buat file bernama TodoItemDTO.cs
dengan kode berikut:
public class TodoItemDTO
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public TodoItemDTO() { }
public TodoItemDTO(Todo todoItem) =>
(Id, Name, IsComplete) = (todoItem.Id, todoItem.Name, todoItem.IsComplete);
}
Ganti konten Program.cs
file dengan kode berikut untuk menggunakan model DTO ini:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
RouteGroupBuilder todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", GetAllTodos);
todoItems.MapGet("/complete", GetCompleteTodos);
todoItems.MapGet("/{id}", GetTodo);
todoItems.MapPost("/", CreateTodo);
todoItems.MapPut("/{id}", UpdateTodo);
todoItems.MapDelete("/{id}", DeleteTodo);
app.Run();
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.Select(x => new TodoItemDTO(x)).ToArrayAsync());
}
static async Task<IResult> GetCompleteTodos(TodoDb db) {
return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).Select(x => new TodoItemDTO(x)).ToListAsync());
}
static async Task<IResult> GetTodo(int id, TodoDb db)
{
return await db.Todos.FindAsync(id)
is Todo todo
? TypedResults.Ok(new TodoItemDTO(todo))
: TypedResults.NotFound();
}
static async Task<IResult> CreateTodo(TodoItemDTO todoItemDTO, TodoDb db)
{
var todoItem = new Todo
{
IsComplete = todoItemDTO.IsComplete,
Name = todoItemDTO.Name
};
db.Todos.Add(todoItem);
await db.SaveChangesAsync();
todoItemDTO = new TodoItemDTO(todoItem);
return TypedResults.Created($"/todoitems/{todoItem.Id}", todoItemDTO);
}
static async Task<IResult> UpdateTodo(int id, TodoItemDTO todoItemDTO, TodoDb db)
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
todo.Name = todoItemDTO.Name;
todo.IsComplete = todoItemDTO.IsComplete;
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
static async Task<IResult> DeleteTodo(int id, TodoDb db)
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
return TypedResults.NotFound();
}
Verifikasi bahwa Anda bisa memposting dan mendapatkan semua bidang kecuali bidang rahasia.
Pemecahan masalah dengan sampel yang telah selesai
Jika Anda mengalami masalah yang tidak dapat Anda atasi, bandingkan kode Anda dengan proyek yang telah selesai. Lihat atau unduh proyek yang selesai (cara mengunduh).
Langkah berikutnya
- Konfigurasikan JSopsi serialisasi ON.
- Menangani kesalahan dan pengecualian: Halaman pengecualian pengembang diaktifkan secara default di lingkungan pengembangan untuk aplikasi API minimal. Untuk informasi tentang cara menangani kesalahan dan pengecualian, lihat Menangani kesalahan di API inti ASP.NET.
- Untuk contoh pengujian aplikasi API minimal, lihat sampel GitHub ini.
- Dukungan OpenAPI dalam API minimal.
- Mulai cepat: Terbitkan ke Azure.
- Mengatur API Minimal Inti ASP.NET.
Pelajari lebih lanjut
API minimal dirancang untuk membuat API HTTP dengan dependensi minimal. Mereka sangat ideal untuk layanan mikro dan aplikasi yang hanya ingin menyertakan file, fitur, dan dependensi minimum di ASP.NET Core.
Tutorial ini mengajarkan dasar-dasar membangun API minimal dengan ASP.NET Core. Pendekatan lain untuk membuat API di ASP.NET Core adalah menggunakan pengontrol. Untuk bantuan dalam memilih antara API minimal dan API berbasis pengontrol, lihat Gambaran umum API. Untuk tutorial tentang membuat proyek API berdasarkan pengontrol yang berisi lebih banyak fitur, lihat Membuat API web.
Gambaran Umum
Tutorial ini membuat API berikut:
API | Deskripsi | Isi permintaan | Isi respons |
---|---|---|---|
GET /todoitems |
Dapatkan semua item yang harus dilakukan | Tidak | Array item yang harus dilakukan |
GET /todoitems/complete |
Selesaikan item yang harus dilakukan | Tidak | Array item yang harus dilakukan |
GET /todoitems/{id} |
Mendapatkan item menurut ID | Tidak | Item yang harus dilakukan |
POST /todoitems |
Menambahkan item baru | Item yang harus dilakukan | Item yang harus dilakukan |
PUT /todoitems/{id} |
Memperbarui item yang sudah ada | Item yang harus dilakukan | Tidak |
DELETE /todoitems/{id} |
Menghapus item | Tidak | Tidak |
Prasyarat
Visual Studio 2022 dengan beban kerja ASP.NET serta pengembangan web.
Membuat proyek API
Mulai Visual Studio 2022 dan pilih Buat proyek baru.
Dalam dialog Buat proyek baru:
- Masukkan
Empty
di kotak pencarian Cari templat . - Pilih templat ASP.NET Core Empty dan pilih Berikutnya.
- Masukkan
Beri nama proyek TodoApi dan pilih Berikutnya.
Dalam dialog Informasi tambahan:
- Pilih .NET 7.0
- Hapus centang Jangan gunakan pernyataan tingkat atas
- Pilih Buat
Memeriksa kode
File Program.cs
berisi kode berikut:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Kode sebelumnya:
- WebApplicationBuilder Membuat dan WebApplication dengan default yang telah dikonfigurasi sebelumnya.
- Membuat titik
/
akhir HTTP GET yang mengembalikanHello World!
:
Menjalankan aplikasi
Tekan Ctrl+F5 untuk menjalankan tanpa debugger.
Visual Studio menampilkan dialog berikut:
Pilih Ya jika Anda mempercayai sertifikat IIS Express SSL.
Dialog berikut ditampilkan:
Pilih Ya jika Anda setuju untuk mempercayai sertifikat pengembangan tersebut.
Untuk informasi tentang mempercayai browser Firefox, lihat Kesalahan sertifikat Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.
Visual Studio meluncurkan Kestrel server web dan membuka jendela browser.
Hello World!
ditampilkan di browser. File Program.cs
berisi aplikasi minimal tetapi lengkap.
Menambahkan paket NuGet
Paket NuGet harus ditambahkan untuk mendukung database dan diagnostik yang digunakan dalam tutorial ini.
- Dari menu Alat, pilih Pengelola > Paket NuGet Kelola Paket NuGet untuk Solusi.
- Pilih tab Telusuri.
- Masukkan Microsoft.EntityFrameworkCore.InMemory di kotak pencarian, lalu pilih
Microsoft.EntityFrameworkCore.InMemory
. - Pilih kotak centang Proyek di panel kanan.
- Di menu drop-down Versi pilih versi terbaru 7 yang tersedia, misalnya
7.0.17
, lalu pilih Instal. - Ikuti instruksi sebelumnya untuk menambahkan
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
paket dengan versi terbaru 7 yang tersedia.
Kelas konteks model dan database
Di folder proyek, buat file bernama Todo.cs
dengan kode berikut:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Kode sebelumnya membuat model untuk aplikasi ini. Model adalah kelas yang mewakili data yang dikelola aplikasi.
Buat file bernama TodoDb.cs
dengan kode berikut:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
Kode sebelumnya mendefinisikan konteks database, yang merupakan kelas utama yang mengoordinasikan fungsionalitas Kerangka Kerja Entitas untuk model data. Kelas ini berasal dari Microsoft.EntityFrameworkCore.DbContext kelas .
Menambahkan kode API
Ganti isi file Program.cs
dengan kode berikut:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync());
app.MapGet("/todoitems/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
app.MapPut("/todoitems/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.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.NoContent();
}
return Results.NotFound();
});
app.Run();
Kode yang disorot berikut menambahkan konteks database ke kontainer injeksi dependensi (DI) dan memungkinkan menampilkan pengecualian terkait database:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
Kontainer DI menyediakan akses ke konteks database dan layanan lainnya.
Membuat UI pengujian API dengan Swagger
Ada banyak alat pengujian API web yang tersedia untuk dipilih, dan Anda dapat mengikuti langkah-langkah pengujian API pengantar tutorial ini dengan alat pilihan Anda sendiri.
Tutorial ini menggunakan paket .NET NSwag.AspNetCore, yang mengintegrasikan alat Swagger untuk menghasilkan UI pengujian yang mematuhi spesifikasi OpenAPI:
- NSwag: Pustaka .NET yang mengintegrasikan Swagger langsung ke aplikasi ASP.NET Core, menyediakan middleware dan konfigurasi.
- Swagger: Sekumpulan alat sumber terbuka seperti OpenAPIGenerator dan SwaggerUI yang menghasilkan halaman pengujian API yang mengikuti spesifikasi OpenAPI.
- Spesifikasi OpenAPI: Dokumen yang menjelaskan kemampuan API, berdasarkan XML dan anotasi atribut dalam pengontrol dan model.
Untuk informasi selengkapnya tentang menggunakan OpenAPI dan NSwag dengan ASP.NET, lihat dokumentasi API web ASP.NET Core dengan Swagger / OpenAPI.
Menginstal alat Swagger
Jalankan perintah berikut:
dotnet add package NSwag.AspNetCore
Perintah sebelumnya menambahkan paket NSwag.AspNetCore , yang berisi alat untuk menghasilkan dokumen dan UI Swagger.
Mengonfigurasi middleware Swagger
Tambahkan kode yang disorot berikut sebelum
app
ditentukan sebarisvar app = builder.Build();
using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList")); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddOpenApiDocument(config => { config.DocumentName = "TodoAPI"; config.Title = "TodoAPI v1"; config.Version = "v1"; }); var app = builder.Build();
Dalam kode sebelumnya:
builder.Services.AddEndpointsApiExplorer();
: Mengaktifkan API Explorer, yang merupakan layanan yang menyediakan metadata tentang API HTTP. API Explorer digunakan oleh Swagger untuk menghasilkan dokumen Swagger.builder.Services.AddOpenApiDocument(config => {...});
: Menambahkan generator dokumen Swagger OpenAPI ke layanan aplikasi dan mengonfigurasinya untuk memberikan informasi lebih lanjut tentang API, seperti judul dan versinya. Untuk informasi tentang memberikan detail API yang lebih kuat, lihat Mulai menggunakan NSwag dan ASP.NET CoreTambahkan kode yang disorot berikut ke baris berikutnya setelah
app
ditentukan dalam barisvar app = builder.Build();
var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseOpenApi(); app.UseSwaggerUi(config => { config.DocumentTitle = "TodoAPI"; config.Path = "/swagger"; config.DocumentPath = "/swagger/{documentName}/swagger.json"; config.DocExpansion = "list"; }); }
Kode sebelumnya memungkinkan middleware Swagger untuk melayani dokumen ON yang dihasilkan JSdan antarmuka pengguna Swagger. Swagger hanya diaktifkan di lingkungan pengembangan. Mengaktifkan Swagger di lingkungan produksi dapat mengekspos detail yang berpotensi sensitif tentang struktur dan implementasi API.
Menguji data posting
Kode berikut dalam Program.cs
membuat titik /todoitems
akhir HTTP POST yang menambahkan data ke database dalam memori:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
Jalankan aplikasi. Browser menampilkan kesalahan 404 karena tidak ada lagi /
titik akhir.
Titik akhir POST akan digunakan untuk menambahkan data ke aplikasi.
Dengan aplikasi yang masih berjalan, di browser, navigasikan ke
https://localhost:<port>/swagger
untuk menampilkan halaman pengujian API yang dihasilkan oleh Swagger.Pada halaman pengujian API Swagger, pilih Posting /todoitems>Cobalah.
Perhatikan bahwa bidang Isi permintaan berisi format contoh yang dihasilkan yang mencerminkan parameter untuk API.
Dalam isi permintaan masukkan JSAKTIF untuk item yang harus dilakukan, tanpa menentukan opsional
id
:{ "name":"walk dog", "isComplete":true }
Pilih Jalankan.
Swagger menyediakan panel Respons di bawah tombol Jalankan.
Perhatikan beberapa detail yang berguna:
- cURL: Swagger menyediakan contoh perintah cURL dalam sintaks Unix/Linux, yang dapat dijalankan di baris perintah dengan shell bash apa pun yang menggunakan sintaks Unix/Linux, termasuk Git Bash dari Git untuk Windows.
- URL Permintaan: Representasi yang disederhanakan dari permintaan HTTP yang dibuat oleh kode JavaScript UI Swagger untuk panggilan API. Permintaan aktual dapat menyertakan detail seperti header dan parameter kueri dan isi permintaan.
- Respons server: Menyertakan isi respons dan header. Isi respons menunjukkan
id
diatur ke1
. - Kode Respons: Kode status 200
HTTP
dikembalikan yang menunjukkan permintaan berhasil diproses.
Memeriksa titik akhir GET
Aplikasi sampel mengimplementasikan beberapa titik akhir GET dengan memanggil MapGet
:
API | Deskripsi | Isi permintaan | Isi respons |
---|---|---|---|
GET /todoitems |
Dapatkan semua item yang harus dilakukan | Tidak | Array item yang harus dilakukan |
GET /todoitems/complete |
Dapatkan semua item tugas yang sudah selesai | Tidak | Array item yang harus dilakukan |
GET /todoitems/{id} |
Mendapatkan item menurut ID | Tidak | Item yang harus dilakukan |
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync());
app.MapGet("/todoitems/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
Menguji titik akhir GET
Uji aplikasi dengan memanggil titik akhir dari browser atau Swagger.
Di Swagger pilih GET /todoitems>Cobalah Jalankan>.
Atau, panggil GET /todoitems dari browser dengan memasukkan URI
http://localhost:<port>/todoitems
. Misalnya:http://localhost:5001/todoitems
Panggilan untuk GET /todoitems
menghasilkan respons yang mirip dengan yang berikut ini:
[
{
"id": 1,
"name": "walk dog",
"isComplete": true
}
]
Panggil GET /todoitems/{id} di Swagger untuk mengembalikan data dari id tertentu:
- Pilih GET /todoitems>Cobalah.
- Atur bidang id ke
1
dan pilih Jalankan.
Atau, panggil GET /todoitems dari browser dengan memasukkan URI
https://localhost:<port>/todoitems/1
. Misalnya:https://localhost:5001/todoitems/1
Responsnya mirip dengan yang berikut ini:
{ "id": 1, "name": "walk dog", "isComplete": true }
Aplikasi ini menggunakan database dalam memori. Jika aplikasi dimulai ulang, permintaan GET tidak mengembalikan data apa pun. Jika tidak ada data yang dikembalikan, KIRIM data ke aplikasi dan coba permintaan GET lagi.
Mengembalikan nilai
ASP.NET Core secara otomatis menserialisasikan objek ke JSAKTIF dan menulis JSON ke dalam isi pesan respons. Kode respons untuk jenis pengembalian ini adalah 200 OK, dengan asumsi tidak ada pengecualian yang tidak tertangani. Pengecualian yang tidak tertangani diterjemahkan ke dalam kesalahan 5xx.
Jenis pengembalian dapat mewakili berbagai kode status HTTP. Misalnya, GET /todoitems/{id}
dapat mengembalikan dua nilai status yang berbeda:
- Jika tidak ada item yang cocok dengan ID yang diminta, metode mengembalikan kode kesalahan status NotFound 404.
- Jika tidak, metode mengembalikan 200 dengan JSisi respons ON. Mengembalikan
item
hasil dalam respons HTTP 200.
Memeriksa titik akhir PUT
Aplikasi sampel mengimplementasikan satu titik akhir PUT menggunakan MapPut
:
app.MapPut("/todoitems/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
});
Metode ini mirip MapPost
dengan metode , kecuali menggunakan HTTP PUT. Respons yang berhasil mengembalikan 204 (Tanpa Konten). Menurut spesifikasi HTTP, permintaan PUT mengharuskan klien untuk mengirim seluruh entitas yang diperbarui, bukan hanya perubahan. Untuk mendukung pembaruan parsial, gunakan HTTP PATCH.
Menguji titik akhir PUT
Sampel ini menggunakan database dalam memori yang harus diinisialisasi setiap kali aplikasi dimulai. Harus ada item dalam database sebelum Anda melakukan panggilan PUT. Panggil GET untuk memastikan ada item dalam database sebelum melakukan panggilan PUT.
Perbarui item yang harus dilakukan yang memiliki Id = 1
dan atur namanya ke "feed fish"
.
Gunakan Swagger untuk mengirim permintaan PUT:
Pilih Letakkan /todoitems/{id}>Cobalah.
Atur bidang id ke
1
.Atur isi permintaan ke ON berikut JS:
{ "name": "feed fish", "isComplete": false }
Pilih Jalankan.
Memeriksa dan menguji titik akhir DELETE
Aplikasi sampel mengimplementasikan satu titik akhir DELETE menggunakan MapDelete
:
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.NoContent();
}
return Results.NotFound();
});
Gunakan Swagger untuk mengirim permintaan DELETE:
Pilih HAPUS /todoitems/{id}>Cobalah.
Atur bidang ID ke
1
dan pilih Jalankan.Permintaan DELETE dikirim ke aplikasi dan respons ditampilkan di panel Respons . Isi respons kosong, dan kode status respons Server adalah 204.
Menggunakan API MapGroup
Kode aplikasi sampel mengulangi awalan todoitems
URL setiap kali menyiapkan titik akhir. API sering memiliki grup titik akhir dengan awalan URL umum, dan metode ini tersedia untuk membantu mengatur grup tersebut MapGroup . Ini mengurangi kode berulang dan memungkinkan untuk menyesuaikan seluruh grup titik akhir dengan satu panggilan ke metode seperti RequireAuthorization dan WithMetadata.
Ganti isi Program.cs
dengan kode berikut:
using NSwag.AspNetCore;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApiDocument(config =>
{
config.DocumentName = "TodoAPI";
config.Title = "TodoAPI v1";
config.Version = "v1";
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseOpenApi();
app.UseSwaggerUi(config =>
{
config.DocumentTitle = "TodoAPI";
config.Path = "/swagger";
config.DocumentPath = "/swagger/{documentName}/swagger.json";
config.DocExpansion = "list";
});
}
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", async (TodoDb db) =>
await db.Todos.ToListAsync());
todoItems.MapGet("/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
todoItems.MapGet("/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
todoItems.MapPost("/", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
todoItems.MapPut("/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
});
todoItems.MapDelete("/{id}", async (int id, TodoDb db) =>
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return Results.NoContent();
}
return Results.NotFound();
});
app.Run();
Kode sebelumnya memiliki perubahan berikut:
var todoItems = app.MapGroup("/todoitems");
Menambahkan untuk menyiapkan grup menggunakan awalan/todoitems
URL .- Mengubah semua metode menjadi
app.Map<HttpVerb>
todoItems.Map<HttpVerb>
. - Menghapus awalan
/todoitems
URL dariMap<HttpVerb>
panggilan metode.
Uji titik akhir untuk memverifikasi bahwa titik akhir berfungsi sama.
Menggunakan API TypedResults
TypedResults Mengembalikan daripada Results memiliki beberapa keuntungan, termasuk uji coba dan secara otomatis mengembalikan metadata jenis respons untuk OpenAPI untuk menjelaskan titik akhir. Untuk informasi selengkapnya, lihat TypedResults vs Results.
Metode ini Map<HttpVerb>
dapat memanggil metode handler rute alih-alih menggunakan lambda. Untuk melihat contoh, perbarui Program.cs dengan kode berikut:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", GetAllTodos);
todoItems.MapGet("/complete", GetCompleteTodos);
todoItems.MapGet("/{id}", GetTodo);
todoItems.MapPost("/", CreateTodo);
todoItems.MapPut("/{id}", UpdateTodo);
todoItems.MapDelete("/{id}", DeleteTodo);
app.Run();
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
static async Task<IResult> GetCompleteTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).ToListAsync());
}
static async Task<IResult> GetTodo(int id, TodoDb db)
{
return await db.Todos.FindAsync(id)
is Todo todo
? TypedResults.Ok(todo)
: TypedResults.NotFound();
}
static async Task<IResult> CreateTodo(Todo todo, TodoDb db)
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return TypedResults.Created($"/todoitems/{todo.Id}", todo);
}
static async Task<IResult> UpdateTodo(int id, Todo inputTodo, TodoDb db)
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
static async Task<IResult> DeleteTodo(int id, TodoDb db)
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
return TypedResults.NotFound();
}
Kode Map<HttpVerb>
sekarang memanggil metode alih-alih lambda:
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", GetAllTodos);
todoItems.MapGet("/complete", GetCompleteTodos);
todoItems.MapGet("/{id}", GetTodo);
todoItems.MapPost("/", CreateTodo);
todoItems.MapPut("/{id}", UpdateTodo);
todoItems.MapDelete("/{id}", DeleteTodo);
Metode ini mengembalikan objek yang mengimplementasikan IResult dan didefinisikan oleh TypedResults:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
static async Task<IResult> GetCompleteTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).ToListAsync());
}
static async Task<IResult> GetTodo(int id, TodoDb db)
{
return await db.Todos.FindAsync(id)
is Todo todo
? TypedResults.Ok(todo)
: TypedResults.NotFound();
}
static async Task<IResult> CreateTodo(Todo todo, TodoDb db)
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return TypedResults.Created($"/todoitems/{todo.Id}", todo);
}
static async Task<IResult> UpdateTodo(int id, Todo inputTodo, TodoDb db)
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
static async Task<IResult> DeleteTodo(int id, TodoDb db)
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
return TypedResults.NotFound();
}
Pengujian unit dapat memanggil metode ini dan menguji bahwa mereka mengembalikan jenis yang benar. Misalnya, jika metodenya adalah GetAllTodos
:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Kode pengujian unit dapat memverifikasi bahwa objek jenis Ok<Todo[]> dikembalikan dari metode handler. Contohnya:
public async Task GetAllTodos_ReturnsOkOfTodosResult()
{
// Arrange
var db = CreateDbContext();
// Act
var result = await TodosApi.GetAllTodos(db);
// Assert: Check for the correct returned type
Assert.IsType<Ok<Todo[]>>(result);
}
Cegah postingan berlebihan
Saat ini aplikasi sampel mengekspos seluruh Todo
objek. Aplikasi produksi Dalam aplikasi produksi, subset model sering digunakan untuk membatasi data yang dapat diinput dan dikembalikan. Ada beberapa alasan di balik ini dan keamanan adalah yang utama. Subset model biasanya disebut sebagai Objek Transfer Data (DTO), model input, atau model tampilan. DTO digunakan dalam artikel ini.
DTO dapat digunakan untuk:
- Cegah postingan berlebihan.
- Sembunyikan properti yang seharusnya tidak dilihat klien.
- Hilangkan beberapa properti untuk mengurangi ukuran payload.
- Meratakan grafik objek yang berisi objek berlapis. Grafik objek yang diratakan bisa lebih nyaman untuk klien.
Untuk menunjukkan pendekatan DTO, perbarui Todo
kelas untuk menyertakan bidang rahasia:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Bidang rahasia perlu disembunyikan dari aplikasi ini, tetapi aplikasi administratif dapat memilih untuk mengeksposnya.
Verifikasi bahwa Anda dapat memposting dan mendapatkan bidang rahasia.
Buat file bernama TodoItemDTO.cs
dengan kode berikut:
public class TodoItemDTO
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public TodoItemDTO() { }
public TodoItemDTO(Todo todoItem) =>
(Id, Name, IsComplete) = (todoItem.Id, todoItem.Name, todoItem.IsComplete);
}
Ganti konten Program.cs
file dengan kode berikut untuk menggunakan model DTO ini:
using Microsoft.EntityFrameworkCore;
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());
app.MapPost("/todoitems", async (TodoItemDTO todoItemDTO, TodoDb db) =>
{
var todoItem = new Todo
{
IsComplete = todoItemDTO.IsComplete,
Name = todoItemDTO.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 todoItemDTO, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = todoItemDTO.Name;
todo.IsComplete = todoItemDTO.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.NoContent();
}
return Results.NotFound();
});
app.Run();
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
public class TodoItemDTO
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public TodoItemDTO() { }
public TodoItemDTO(Todo todoItem) =>
(Id, Name, IsComplete) = (todoItem.Id, todoItem.Name, todoItem.IsComplete);
}
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
Verifikasi bahwa Anda bisa memposting dan mendapatkan semua bidang kecuali bidang rahasia.
Pemecahan masalah dengan sampel yang telah selesai
Jika Anda mengalami masalah yang tidak dapat Anda atasi, bandingkan kode Anda dengan proyek yang telah selesai. Lihat atau unduh proyek yang selesai (cara mengunduh).
Langkah berikutnya
- Konfigurasikan JSopsi serialisasi ON.
- Menangani kesalahan dan pengecualian: Halaman pengecualian pengembang diaktifkan secara default di lingkungan pengembangan untuk aplikasi API minimal. Untuk informasi tentang cara menangani kesalahan dan pengecualian, lihat Menangani kesalahan di API inti ASP.NET.
- Untuk contoh pengujian aplikasi API minimal, lihat sampel GitHub ini.
- Dukungan OpenAPI dalam API minimal.
- Mulai cepat: Terbitkan ke Azure.
- Mengatur API Minimal Inti ASP.NET.
Pelajari lebih lanjut
API minimal dirancang untuk membuat API HTTP dengan dependensi minimal. Mereka sangat ideal untuk layanan mikro dan aplikasi yang hanya ingin menyertakan file, fitur, dan dependensi minimum di ASP.NET Core.
Tutorial ini mengajarkan dasar-dasar membangun API minimal dengan ASP.NET Core. Pendekatan lain untuk membuat API di ASP.NET Core adalah menggunakan pengontrol. Untuk bantuan dalam memilih antara API minimal dan API berbasis pengontrol, lihat Gambaran umum API. Untuk tutorial tentang membuat proyek API berdasarkan pengontrol yang berisi lebih banyak fitur, lihat Membuat API web.
Gambaran Umum
Tutorial ini membuat API berikut:
API | Deskripsi | Isi permintaan | Isi respons |
---|---|---|---|
GET /todoitems |
Dapatkan semua item yang harus dilakukan | Tidak | Array item yang harus dilakukan |
GET /todoitems/complete |
Selesaikan item yang harus dilakukan | Tidak | Array item yang harus dilakukan |
GET /todoitems/{id} |
Mendapatkan item menurut ID | Tidak | Item yang harus dilakukan |
POST /todoitems |
Menambahkan item baru | Item yang harus dilakukan | Item yang harus dilakukan |
PUT /todoitems/{id} |
Memperbarui item yang sudah ada | Item yang harus dilakukan | Tidak |
DELETE /todoitems/{id} |
Menghapus item | Tidak | Tidak |
Prasyarat
- Visual Studio 2022 dengan beban kerja ASP.NET serta pengembangan web.
- .NET 6.0 SDK
Membuat proyek API
Mulai Visual Studio 2022 dan pilih Buat proyek baru.
Dalam dialog Buat proyek baru:
- Masukkan
Empty
di kotak pencarian Cari templat . - Pilih templat ASP.NET Core Empty dan pilih Berikutnya.
- Masukkan
Beri nama proyek TodoApi dan pilih Berikutnya.
Dalam dialog Informasi tambahan:
- Pilih .NET 6.0
- Hapus centang Jangan gunakan pernyataan tingkat atas
- Pilih Buat
Memeriksa kode
File Program.cs
berisi kode berikut:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Kode sebelumnya:
- WebApplicationBuilder Membuat dan WebApplication dengan default yang telah dikonfigurasi sebelumnya.
- Membuat titik
/
akhir HTTP GET yang mengembalikanHello World!
:
Menjalankan aplikasi
Tekan Ctrl+F5 untuk menjalankan tanpa debugger.
Visual Studio menampilkan dialog berikut:
Pilih Ya jika Anda mempercayai sertifikat IIS Express SSL.
Dialog berikut ditampilkan:
Pilih Ya jika Anda setuju untuk mempercayai sertifikat pengembangan tersebut.
Untuk informasi tentang mempercayai browser Firefox, lihat Kesalahan sertifikat Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.
Visual Studio meluncurkan Kestrel server web dan membuka jendela browser.
Hello World!
ditampilkan di browser. File Program.cs
berisi aplikasi minimal tetapi lengkap.
Menambahkan paket NuGet
Paket NuGet harus ditambahkan untuk mendukung database dan diagnostik yang digunakan dalam tutorial ini.
- Dari menu Alat, pilih Pengelola > Paket NuGet Kelola Paket NuGet untuk Solusi.
- Pilih tab Telusuri.
- Masukkan Microsoft.EntityFrameworkCore.InMemory di kotak pencarian, lalu pilih
Microsoft.EntityFrameworkCore.InMemory
. - Pilih kotak centang Proyek di panel kanan.
- Di menu drop-down Versi pilih versi terbaru 7 yang tersedia, misalnya
6.0.28
, lalu pilih Instal. - Ikuti instruksi sebelumnya untuk menambahkan
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
paket dengan versi terbaru 7 yang tersedia.
Kelas konteks model dan database
Di folder proyek, buat file bernama Todo.cs
dengan kode berikut:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Kode sebelumnya membuat model untuk aplikasi ini. Model adalah kelas yang mewakili data yang dikelola aplikasi.
Buat file bernama TodoDb.cs
dengan kode berikut:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
Kode sebelumnya mendefinisikan konteks database, yang merupakan kelas utama yang mengoordinasikan fungsionalitas Kerangka Kerja Entitas untuk model data. Kelas ini berasal dari Microsoft.EntityFrameworkCore.DbContext kelas .
Menambahkan kode API
Ganti isi file Program.cs
dengan kode berikut:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync());
app.MapGet("/todoitems/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
app.MapPut("/todoitems/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.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.NoContent();
}
return Results.NotFound();
});
app.Run();
Kode yang disorot berikut menambahkan konteks database ke kontainer injeksi dependensi (DI) dan memungkinkan menampilkan pengecualian terkait database:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
Kontainer DI menyediakan akses ke konteks database dan layanan lainnya.
Membuat UI pengujian API dengan Swagger
Ada banyak alat pengujian API web yang tersedia untuk dipilih, dan Anda dapat mengikuti langkah-langkah pengujian API pengantar tutorial ini dengan alat pilihan Anda sendiri.
Tutorial ini menggunakan paket .NET NSwag.AspNetCore, yang mengintegrasikan alat Swagger untuk menghasilkan UI pengujian yang mematuhi spesifikasi OpenAPI:
- NSwag: Pustaka .NET yang mengintegrasikan Swagger langsung ke aplikasi ASP.NET Core, menyediakan middleware dan konfigurasi.
- Swagger: Sekumpulan alat sumber terbuka seperti OpenAPIGenerator dan SwaggerUI yang menghasilkan halaman pengujian API yang mengikuti spesifikasi OpenAPI.
- Spesifikasi OpenAPI: Dokumen yang menjelaskan kemampuan API, berdasarkan XML dan anotasi atribut dalam pengontrol dan model.
Untuk informasi selengkapnya tentang menggunakan OpenAPI dan NSwag dengan ASP.NET, lihat dokumentasi API web ASP.NET Core dengan Swagger / OpenAPI.
Menginstal alat Swagger
Jalankan perintah berikut:
dotnet add package NSwag.AspNetCore
Perintah sebelumnya menambahkan paket NSwag.AspNetCore , yang berisi alat untuk menghasilkan dokumen dan UI Swagger.
Mengonfigurasi middleware Swagger
Di Program.cs tambahkan pernyataan berikut
using
di bagian atas:using NSwag.AspNetCore;
Tambahkan kode yang disorot berikut sebelum
app
ditentukan sebarisvar app = builder.Build();
using NSwag.AspNetCore; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList")); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddOpenApiDocument(config => { config.DocumentName = "TodoAPI"; config.Title = "TodoAPI v1"; config.Version = "v1"; }); var app = builder.Build();
Dalam kode sebelumnya:
builder.Services.AddEndpointsApiExplorer();
: Mengaktifkan API Explorer, yang merupakan layanan yang menyediakan metadata tentang API HTTP. API Explorer digunakan oleh Swagger untuk menghasilkan dokumen Swagger.builder.Services.AddOpenApiDocument(config => {...});
: Menambahkan generator dokumen Swagger OpenAPI ke layanan aplikasi dan mengonfigurasinya untuk memberikan informasi lebih lanjut tentang API, seperti judul dan versinya. Untuk informasi tentang memberikan detail API yang lebih kuat, lihat Mulai menggunakan NSwag dan ASP.NET CoreTambahkan kode yang disorot berikut ke baris berikutnya setelah
app
ditentukan dalam barisvar app = builder.Build();
var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseOpenApi(); app.UseSwaggerUi(config => { config.DocumentTitle = "TodoAPI"; config.Path = "/swagger"; config.DocumentPath = "/swagger/{documentName}/swagger.json"; config.DocExpansion = "list"; }); }
Kode sebelumnya memungkinkan middleware Swagger untuk melayani dokumen ON yang dihasilkan JSdan antarmuka pengguna Swagger. Swagger hanya diaktifkan di lingkungan pengembangan. Mengaktifkan Swagger di lingkungan produksi dapat mengekspos detail yang berpotensi sensitif tentang struktur dan implementasi API.
Menguji data posting
Kode berikut dalam Program.cs
membuat titik /todoitems
akhir HTTP POST yang menambahkan data ke database dalam memori:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
Jalankan aplikasi. Browser menampilkan kesalahan 404 karena tidak ada lagi /
titik akhir.
Titik akhir POST akan digunakan untuk menambahkan data ke aplikasi.
Dengan aplikasi yang masih berjalan, di browser, navigasikan ke
https://localhost:<port>/swagger
untuk menampilkan halaman pengujian API yang dihasilkan oleh Swagger.Pada halaman pengujian API Swagger, pilih Posting /todoitems>Cobalah.
Perhatikan bahwa bidang Isi permintaan berisi format contoh yang dihasilkan yang mencerminkan parameter untuk API.
Dalam isi permintaan masukkan JSAKTIF untuk item yang harus dilakukan, tanpa menentukan opsional
id
:{ "name":"walk dog", "isComplete":true }
Pilih Jalankan.
Swagger menyediakan panel Respons di bawah tombol Jalankan.
Perhatikan beberapa detail yang berguna:
- cURL: Swagger menyediakan contoh perintah cURL dalam sintaks Unix/Linux, yang dapat dijalankan di baris perintah dengan shell bash apa pun yang menggunakan sintaks Unix/Linux, termasuk Git Bash dari Git untuk Windows.
- URL Permintaan: Representasi yang disederhanakan dari permintaan HTTP yang dibuat oleh kode JavaScript UI Swagger untuk panggilan API. Permintaan aktual dapat menyertakan detail seperti header dan parameter kueri dan isi permintaan.
- Respons server: Menyertakan isi respons dan header. Isi respons menunjukkan
id
diatur ke1
. - Kode Respons: Kode status 200
HTTP
dikembalikan yang menunjukkan permintaan berhasil diproses.
Memeriksa titik akhir GET
Aplikasi sampel mengimplementasikan beberapa titik akhir GET dengan memanggil MapGet
:
API | Deskripsi | Isi permintaan | Isi respons |
---|---|---|---|
GET /todoitems |
Dapatkan semua item yang harus dilakukan | Tidak | Array item yang harus dilakukan |
GET /todoitems/complete |
Dapatkan semua item tugas yang sudah selesai | Tidak | Array item yang harus dilakukan |
GET /todoitems/{id} |
Mendapatkan item menurut ID | Tidak | Item yang harus dilakukan |
app.MapGet("/", () => "Hello World!");
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync());
app.MapGet("/todoitems/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
Menguji titik akhir GET
Uji aplikasi dengan memanggil titik akhir dari browser atau Swagger.
Di Swagger pilih GET /todoitems>Cobalah Jalankan>.
Atau, panggil GET /todoitems dari browser dengan memasukkan URI
http://localhost:<port>/todoitems
. Misalnya:http://localhost:5001/todoitems
Panggilan untuk GET /todoitems
menghasilkan respons yang mirip dengan yang berikut ini:
[
{
"id": 1,
"name": "walk dog",
"isComplete": true
}
]
Panggil GET /todoitems/{id} di Swagger untuk mengembalikan data dari id tertentu:
- Pilih GET /todoitems>Cobalah.
- Atur bidang id ke
1
dan pilih Jalankan.
Atau, panggil GET /todoitems dari browser dengan memasukkan URI
https://localhost:<port>/todoitems/1
. Misalnya, Misalnya,https://localhost:5001/todoitems/1
Responsnya mirip dengan yang berikut ini:
{ "id": 1, "name": "walk dog", "isComplete": true }
Aplikasi ini menggunakan database dalam memori. Jika aplikasi dimulai ulang, permintaan GET tidak mengembalikan data apa pun. Jika tidak ada data yang dikembalikan, KIRIM data ke aplikasi dan coba permintaan GET lagi.
Mengembalikan nilai
ASP.NET Core secara otomatis menserialisasikan objek ke JSAKTIF dan menulis JSON ke dalam isi pesan respons. Kode respons untuk jenis pengembalian ini adalah 200 OK, dengan asumsi tidak ada pengecualian yang tidak tertangani. Pengecualian yang tidak tertangani diterjemahkan ke dalam kesalahan 5xx.
Jenis pengembalian dapat mewakili berbagai kode status HTTP. Misalnya, GET /todoitems/{id}
dapat mengembalikan dua nilai status yang berbeda:
- Jika tidak ada item yang cocok dengan ID yang diminta, metode mengembalikan kode kesalahan status NotFound 404.
- Jika tidak, metode mengembalikan 200 dengan JSisi respons ON. Mengembalikan
item
hasil dalam respons HTTP 200.
Memeriksa titik akhir PUT
Aplikasi sampel mengimplementasikan satu titik akhir PUT menggunakan MapPut
:
app.MapPut("/todoitems/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
});
Metode ini mirip MapPost
dengan metode , kecuali menggunakan HTTP PUT. Respons yang berhasil mengembalikan 204 (Tanpa Konten). Menurut spesifikasi HTTP, permintaan PUT mengharuskan klien untuk mengirim seluruh entitas yang diperbarui, bukan hanya perubahan. Untuk mendukung pembaruan parsial, gunakan HTTP PATCH.
Menguji titik akhir PUT
Sampel ini menggunakan database dalam memori yang harus diinisialisasi setiap kali aplikasi dimulai. Harus ada item dalam database sebelum Anda melakukan panggilan PUT. Panggil GET untuk memastikan ada item dalam database sebelum melakukan panggilan PUT.
Perbarui item yang harus dilakukan yang memiliki Id = 1
dan atur namanya ke "feed fish"
.
Gunakan Swagger untuk mengirim permintaan PUT:
Pilih Letakkan /todoitems/{id}>Cobalah.
Atur bidang id ke
1
.Atur isi permintaan ke ON berikut JS:
{ "name": "feed fish", "isComplete": false }
Pilih Jalankan.
Memeriksa dan menguji titik akhir DELETE
Aplikasi sampel mengimplementasikan satu titik akhir DELETE menggunakan MapDelete
:
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.NoContent();
}
return Results.NotFound();
});
Gunakan Swagger untuk mengirim permintaan DELETE:
Pilih HAPUS /todoitems/{id}>Cobalah.
Atur bidang ID ke
1
dan pilih Jalankan.Permintaan DELETE dikirim ke aplikasi dan respons ditampilkan di panel Respons . Isi respons kosong, dan kode status respons Server adalah 204.
Cegah postingan berlebihan
Saat ini aplikasi sampel mengekspos seluruh Todo
objek. Aplikasi produksi Dalam aplikasi produksi, subset model sering digunakan untuk membatasi data yang dapat diinput dan dikembalikan. Ada beberapa alasan di balik ini dan keamanan adalah yang utama. Subset model biasanya disebut sebagai Objek Transfer Data (DTO), model input, atau model tampilan. DTO digunakan dalam artikel ini.
DTO dapat digunakan untuk:
- Cegah postingan berlebihan.
- Sembunyikan properti yang seharusnya tidak dilihat klien.
- Hilangkan beberapa properti untuk mengurangi ukuran payload.
- Meratakan grafik objek yang berisi objek berlapis. Grafik objek yang diratakan bisa lebih nyaman untuk klien.
Untuk menunjukkan pendekatan DTO, perbarui Todo
kelas untuk menyertakan bidang rahasia:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Bidang rahasia perlu disembunyikan dari aplikasi ini, tetapi aplikasi administratif dapat memilih untuk mengeksposnya.
Verifikasi bahwa Anda dapat memposting dan mendapatkan bidang rahasia.
Buat file bernama TodoItemDTO.cs
dengan kode berikut:
public class TodoItemDTO
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public TodoItemDTO() { }
public TodoItemDTO(Todo todoItem) =>
(Id, Name, IsComplete) = (todoItem.Id, todoItem.Name, todoItem.IsComplete);
}
Ganti konten Program.cs
file dengan kode berikut untuk menggunakan model DTO ini:
using Microsoft.EntityFrameworkCore;
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());
app.MapPost("/todoitems", async (TodoItemDTO todoItemDTO, TodoDb db) =>
{
var todoItem = new Todo
{
IsComplete = todoItemDTO.IsComplete,
Name = todoItemDTO.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 todoItemDTO, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = todoItemDTO.Name;
todo.IsComplete = todoItemDTO.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.NoContent();
}
return Results.NotFound();
});
app.Run();
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
public class TodoItemDTO
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public TodoItemDTO() { }
public TodoItemDTO(Todo todoItem) =>
(Id, Name, IsComplete) = (todoItem.Id, todoItem.Name, todoItem.IsComplete);
}
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
Verifikasi bahwa Anda bisa memposting dan mendapatkan semua bidang kecuali bidang rahasia.
Menguji API minimal
Untuk contoh pengujian aplikasi API minimal, lihat sampel GitHub ini.
Menerbitkan ke Azure
Untuk informasi tentang penyebaran ke Azure, lihat Mulai Cepat: Menyebarkan aplikasi web ASP.NET.
Sumber Daya Tambahan:
API minimal dirancang untuk membuat API HTTP dengan dependensi minimal. Mereka ideal untuk layanan mikro dan aplikasi yang hanya ingin menyertakan file, fitur, dan dependensi minimum di ASP.NET Core.
Tutorial ini mengajarkan dasar-dasar membangun API minimal dengan ASP.NET Core. Pendekatan lain untuk membuat API di ASP.NET Core adalah menggunakan pengontrol. Untuk bantuan dalam memilih antara API minimal dan API berbasis pengontrol, lihat Gambaran umum API. Untuk tutorial tentang membuat proyek API berdasarkan pengontrol yang berisi lebih banyak fitur, lihat Membuat API web.
Gambaran Umum
Tutorial ini membuat API berikut:
API | Deskripsi | Isi permintaan | Isi respons |
---|---|---|---|
GET /todoitems |
Dapatkan semua item yang harus dilakukan | Tidak | Array item yang harus dilakukan |
GET /todoitems/complete |
Selesaikan item yang harus dilakukan | Tidak | Array item yang harus dilakukan |
GET /todoitems/{id} |
Mendapatkan item menurut ID | Tidak | Item yang harus dilakukan |
POST /todoitems |
Menambahkan item baru | Item yang harus dilakukan | Item yang harus dilakukan |
PUT /todoitems/{id} |
Memperbarui item yang sudah ada | Item yang harus dilakukan | Tidak |
DELETE /todoitems/{id} |
Menghapus item | Tidak | Tidak |
Prasyarat
Visual Studio 2022 dengan beban kerja ASP.NET serta pengembangan web.
Membuat proyek API
Mulai Visual Studio 2022 dan pilih Buat proyek baru.
Dalam dialog Buat proyek baru:
- Masukkan
Empty
di kotak pencarian Cari templat . - Pilih templat ASP.NET Core Empty dan pilih Berikutnya.
- Masukkan
Beri nama proyek TodoApi dan pilih Berikutnya.
Dalam dialog Informasi tambahan:
- Pilih .NET 8.0 (Dukungan Jangka Panjang)
- Hapus centang Jangan gunakan pernyataan tingkat atas
- Pilih Buat
Memeriksa kode
File Program.cs
berisi kode berikut:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Kode sebelumnya:
- WebApplicationBuilder Membuat dan WebApplication dengan default yang telah dikonfigurasi sebelumnya.
- Membuat titik
/
akhir HTTP GET yang mengembalikanHello World!
:
Menjalankan aplikasi
Tekan Ctrl+F5 untuk menjalankan tanpa debugger.
Visual Studio menampilkan dialog berikut:
Pilih Ya jika Anda mempercayai sertifikat IIS Express SSL.
Dialog berikut ditampilkan:
Pilih Ya jika Anda setuju untuk mempercayai sertifikat pengembangan tersebut.
Untuk informasi tentang mempercayai browser Firefox, lihat Kesalahan sertifikat Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.
Visual Studio meluncurkan Kestrel server web dan membuka jendela browser.
Hello World!
ditampilkan di browser. File Program.cs
berisi aplikasi minimal tetapi lengkap.
Tutup jendela browser.
Menambahkan paket NuGet
Paket NuGet harus ditambahkan untuk mendukung database dan diagnostik yang digunakan dalam tutorial ini.
- Dari menu Alat, pilih Pengelola > Paket NuGet Kelola Paket NuGet untuk Solusi.
- Pilih tab Telusuri.
- Masukkan Microsoft.EntityFrameworkCore.InMemory di kotak pencarian, lalu pilih
Microsoft.EntityFrameworkCore.InMemory
. - Pilih kotak centang Proyek di panel kanan lalu pilih Instal.
- Ikuti instruksi sebelumnya untuk menambahkan
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
paket.
Kelas konteks model dan database
- Di folder proyek, buat file bernama
Todo.cs
dengan kode berikut:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Kode sebelumnya membuat model untuk aplikasi ini. Model adalah kelas yang mewakili data yang dikelola aplikasi.
- Buat file bernama
TodoDb.cs
dengan kode berikut:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
Kode sebelumnya mendefinisikan konteks database, yang merupakan kelas utama yang mengoordinasikan fungsionalitas Kerangka Kerja Entitas untuk model data. Kelas ini berasal dari Microsoft.EntityFrameworkCore.DbContext kelas .
Menambahkan kode API
- Ganti isi file
Program.cs
dengan kode berikut:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync());
app.MapGet("/todoitems/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
app.MapPut("/todoitems/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.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.NoContent();
}
return Results.NotFound();
});
app.Run();
Kode yang disorot berikut menambahkan konteks database ke kontainer injeksi dependensi (DI) dan memungkinkan menampilkan pengecualian terkait database:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
Kontainer DI menyediakan akses ke konteks database dan layanan lainnya.
Tutorial ini menggunakan file Endpoints Explorer dan .http untuk menguji API.
Menguji data posting
Kode berikut dalam Program.cs
membuat titik /todoitems
akhir HTTP POST yang menambahkan data ke database dalam memori:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
Jalankan aplikasi. Browser menampilkan kesalahan 404 karena tidak ada lagi /
titik akhir.
Titik akhir POST akan digunakan untuk menambahkan data ke aplikasi.
Pilih Tampilkan>Penjelajah Titik Akhir Windows>Lainnya.
Klik kanan titik akhir POST dan pilih Buat permintaan.
File baru dibuat di folder proyek bernama
TodoApi.http
, dengan konten yang mirip dengan contoh berikut:@TodoApi_HostAddress = https://localhost:7031 Post {{TodoApi_HostAddress}}/todoitems ###
- Baris pertama membuat variabel yang digunakan untuk semua titik akhir.
- Baris berikutnya menentukan permintaan POST.
- Baris hashtag triple (
###
) adalah pemisah permintaan: apa yang terjadi setelahnya adalah untuk permintaan yang berbeda.
Permintaan POST memerlukan header dan isi. Untuk menentukan bagian permintaan tersebut, tambahkan baris berikut segera setelah baris permintaan POST:
Content-Type: application/json { "name":"walk dog", "isComplete":true }
Kode sebelumnya menambahkan header Jenis Konten dan JSisi permintaan ON. File TodoApi.http sekarang akan terlihat seperti contoh berikut, tetapi dengan nomor port Anda:
@TodoApi_HostAddress = https://localhost:7057 Post {{TodoApi_HostAddress}}/todoitems Content-Type: application/json { "name":"walk dog", "isComplete":true } ###
Jalankan aplikasi.
Pilih tautan Kirim permintaan yang berada di atas
POST
baris permintaan.Permintaan POST dikirim ke aplikasi dan respons ditampilkan di panel Respons .
Memeriksa titik akhir GET
Aplikasi sampel mengimplementasikan beberapa titik akhir GET dengan memanggil MapGet
:
API | Deskripsi | Isi permintaan | Isi respons |
---|---|---|---|
GET /todoitems |
Dapatkan semua item yang harus dilakukan | Tidak | Array item yang harus dilakukan |
GET /todoitems/complete |
Dapatkan semua item tugas yang sudah selesai | Tidak | Array item yang harus dilakukan |
GET /todoitems/{id} |
Mendapatkan item menurut ID | Tidak | Item yang harus dilakukan |
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync());
app.MapGet("/todoitems/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
Menguji titik akhir GET
Uji aplikasi dengan memanggil GET
titik akhir dari browser atau dengan menggunakan Endpoints Explorer. Langkah-langkah berikut adalah untuk Penjelajah Titik Akhir.
Di Penjelajah Titik Akhir, klik kanan titik akhir GET pertama, dan pilih Buat permintaan.
Konten berikut ditambahkan ke
TodoApi.http
file:Get {{TodoApi_HostAddress}}/todoitems ###
Pilih tautan Kirim permintaan yang berada di atas baris permintaan baru
GET
.Permintaan GET dikirim ke aplikasi dan respons ditampilkan di panel Respons .
Isi respons mirip dengan ON berikut JS:
[ { "id": 1, "name": "walk dog", "isComplete": true } ]
Di Penjelajah Titik Akhir, klik
/todoitems/{id}
kanan titik akhir GET dan pilih Hasilkan permintaan. Konten berikut ditambahkan keTodoApi.http
file:GET {{TodoApi_HostAddress}}/todoitems/{id} ###
Ganti
{id}
dengan1
.Pilih tautan Kirim permintaan yang berada di atas baris permintaan GET baru.
Permintaan GET dikirim ke aplikasi dan respons ditampilkan di panel Respons .
Isi respons mirip dengan ON berikut JS:
{ "id": 1, "name": "walk dog", "isComplete": true }
Aplikasi ini menggunakan database dalam memori. Jika aplikasi dimulai ulang, permintaan GET tidak mengembalikan data apa pun. Jika tidak ada data yang dikembalikan, KIRIM data ke aplikasi dan coba permintaan GET lagi.
Mengembalikan nilai
ASP.NET Core secara otomatis menserialisasikan objek ke JSAKTIF dan menulis JSON ke dalam isi pesan respons. Kode respons untuk jenis pengembalian ini adalah 200 OK, dengan asumsi tidak ada pengecualian yang tidak tertangani. Pengecualian yang tidak tertangani diterjemahkan ke dalam kesalahan 5xx.
Jenis pengembalian dapat mewakili berbagai kode status HTTP. Misalnya, GET /todoitems/{id}
dapat mengembalikan dua nilai status yang berbeda:
- Jika tidak ada item yang cocok dengan ID yang diminta, metode mengembalikan kode kesalahan status NotFound 404.
- Jika tidak, metode mengembalikan 200 dengan JSisi respons ON. Mengembalikan
item
hasil dalam respons HTTP 200.
Memeriksa titik akhir PUT
Aplikasi sampel mengimplementasikan satu titik akhir PUT menggunakan MapPut
:
app.MapPut("/todoitems/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
});
Metode ini mirip MapPost
dengan metode , kecuali menggunakan HTTP PUT. Respons yang berhasil mengembalikan 204 (Tanpa Konten). Menurut spesifikasi HTTP, permintaan PUT mengharuskan klien untuk mengirim seluruh entitas yang diperbarui, bukan hanya perubahan. Untuk mendukung pembaruan parsial, gunakan HTTP PATCH.
Menguji titik akhir PUT
Sampel ini menggunakan database dalam memori yang harus diinisialisasi setiap kali aplikasi dimulai. Harus ada item dalam database sebelum Anda melakukan panggilan PUT. Panggil GET untuk memastikan ada item dalam database sebelum melakukan panggilan PUT.
Perbarui item yang harus dilakukan yang memiliki Id = 1
dan atur namanya ke "feed fish"
.
Di Penjelajah Titik Akhir, klik kanan titik akhir PUT , dan pilih Buat permintaan.
Konten berikut ditambahkan ke
TodoApi.http
file:Put {{TodoApi_HostAddress}}/todoitems/{id} ###
Di baris permintaan PUT, ganti
{id}
dengan1
.Tambahkan baris berikut segera setelah baris permintaan PUT:
Content-Type: application/json { "name": "feed fish", "isComplete": false }
Kode sebelumnya menambahkan header Jenis Konten dan JSisi permintaan ON.
Pilih tautan Kirim permintaan yang berada di atas baris permintaan PUT baru.
Permintaan PUT dikirim ke aplikasi dan respons ditampilkan di panel Respons . Isi respons kosong, dan kode status adalah 204.
Memeriksa dan menguji titik akhir DELETE
Aplikasi sampel mengimplementasikan satu titik akhir DELETE menggunakan MapDelete
:
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.NoContent();
}
return Results.NotFound();
});
Di Penjelajah Titik Akhir, klik kanan titik akhir DELETE dan pilih Hasilkan permintaan.
Permintaan DELETE ditambahkan ke
TodoApi.http
.Ganti
{id}
di baris permintaan DELETE dengan1
. Permintaan DELETE akan terlihat seperti contoh berikut:DELETE {{TodoApi_HostAddress}}/todoitems/1 ###
Pilih tautan Kirim permintaan untuk permintaan DELETE.
Permintaan DELETE dikirim ke aplikasi dan respons ditampilkan di panel Respons . Isi respons kosong, dan kode status adalah 204.
Menggunakan API MapGroup
Kode aplikasi sampel mengulangi awalan todoitems
URL setiap kali menyiapkan titik akhir. API sering memiliki grup titik akhir dengan awalan URL umum, dan metode ini tersedia untuk membantu mengatur grup tersebut MapGroup . Ini mengurangi kode berulang dan memungkinkan untuk menyesuaikan seluruh grup titik akhir dengan satu panggilan ke metode seperti RequireAuthorization dan WithMetadata.
Ganti isi Program.cs
dengan kode berikut:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", async (TodoDb db) =>
await db.Todos.ToListAsync());
todoItems.MapGet("/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
todoItems.MapGet("/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
todoItems.MapPost("/", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
todoItems.MapPut("/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
});
todoItems.MapDelete("/{id}", async (int id, TodoDb db) =>
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return Results.NoContent();
}
return Results.NotFound();
});
app.Run();
Kode sebelumnya memiliki perubahan berikut:
var todoItems = app.MapGroup("/todoitems");
Menambahkan untuk menyiapkan grup menggunakan awalan/todoitems
URL .- Mengubah semua metode menjadi
app.Map<HttpVerb>
todoItems.Map<HttpVerb>
. - Menghapus awalan
/todoitems
URL dariMap<HttpVerb>
panggilan metode.
Uji titik akhir untuk memverifikasi bahwa titik akhir berfungsi sama.
Menggunakan API TypedResults
TypedResults Mengembalikan daripada Results memiliki beberapa keuntungan, termasuk uji coba dan secara otomatis mengembalikan metadata jenis respons untuk OpenAPI untuk menjelaskan titik akhir. Untuk informasi selengkapnya, lihat TypedResults vs Results.
Metode ini Map<HttpVerb>
dapat memanggil metode handler rute alih-alih menggunakan lambda. Untuk melihat contoh, perbarui Program.cs dengan kode berikut:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", GetAllTodos);
todoItems.MapGet("/complete", GetCompleteTodos);
todoItems.MapGet("/{id}", GetTodo);
todoItems.MapPost("/", CreateTodo);
todoItems.MapPut("/{id}", UpdateTodo);
todoItems.MapDelete("/{id}", DeleteTodo);
app.Run();
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
static async Task<IResult> GetCompleteTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).ToListAsync());
}
static async Task<IResult> GetTodo(int id, TodoDb db)
{
return await db.Todos.FindAsync(id)
is Todo todo
? TypedResults.Ok(todo)
: TypedResults.NotFound();
}
static async Task<IResult> CreateTodo(Todo todo, TodoDb db)
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return TypedResults.Created($"/todoitems/{todo.Id}", todo);
}
static async Task<IResult> UpdateTodo(int id, Todo inputTodo, TodoDb db)
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
static async Task<IResult> DeleteTodo(int id, TodoDb db)
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
return TypedResults.NotFound();
}
Kode Map<HttpVerb>
sekarang memanggil metode alih-alih lambda:
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", GetAllTodos);
todoItems.MapGet("/complete", GetCompleteTodos);
todoItems.MapGet("/{id}", GetTodo);
todoItems.MapPost("/", CreateTodo);
todoItems.MapPut("/{id}", UpdateTodo);
todoItems.MapDelete("/{id}", DeleteTodo);
Metode ini mengembalikan objek yang mengimplementasikan IResult dan didefinisikan oleh TypedResults:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
static async Task<IResult> GetCompleteTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).ToListAsync());
}
static async Task<IResult> GetTodo(int id, TodoDb db)
{
return await db.Todos.FindAsync(id)
is Todo todo
? TypedResults.Ok(todo)
: TypedResults.NotFound();
}
static async Task<IResult> CreateTodo(Todo todo, TodoDb db)
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return TypedResults.Created($"/todoitems/{todo.Id}", todo);
}
static async Task<IResult> UpdateTodo(int id, Todo inputTodo, TodoDb db)
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
static async Task<IResult> DeleteTodo(int id, TodoDb db)
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
return TypedResults.NotFound();
}
Pengujian unit dapat memanggil metode ini dan menguji bahwa mereka mengembalikan jenis yang benar. Misalnya, jika metodenya adalah GetAllTodos
:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Kode pengujian unit dapat memverifikasi bahwa objek jenis Ok<Todo[]> dikembalikan dari metode handler. Contohnya:
public async Task GetAllTodos_ReturnsOkOfTodosResult()
{
// Arrange
var db = CreateDbContext();
// Act
var result = await TodosApi.GetAllTodos(db);
// Assert: Check for the correct returned type
Assert.IsType<Ok<Todo[]>>(result);
}
Cegah postingan berlebihan
Saat ini aplikasi sampel mengekspos seluruh Todo
objek. Aplikasi produksi Dalam aplikasi produksi, subset model sering digunakan untuk membatasi data yang dapat diinput dan dikembalikan. Ada beberapa alasan di balik ini dan keamanan adalah yang utama. Subset model biasanya disebut sebagai Objek Transfer Data (DTO), model input, atau model tampilan. DTO digunakan dalam artikel ini.
DTO dapat digunakan untuk:
- Cegah postingan berlebihan.
- Sembunyikan properti yang seharusnya tidak dilihat klien.
- Hilangkan beberapa properti untuk mengurangi ukuran payload.
- Meratakan grafik objek yang berisi objek berlapis. Grafik objek yang diratakan bisa lebih nyaman untuk klien.
Untuk menunjukkan pendekatan DTO, perbarui Todo
kelas untuk menyertakan bidang rahasia:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Bidang rahasia perlu disembunyikan dari aplikasi ini, tetapi aplikasi administratif dapat memilih untuk mengeksposnya.
Verifikasi bahwa Anda dapat memposting dan mendapatkan bidang rahasia.
Buat file bernama TodoItemDTO.cs
dengan kode berikut:
public class TodoItemDTO
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public TodoItemDTO() { }
public TodoItemDTO(Todo todoItem) =>
(Id, Name, IsComplete) = (todoItem.Id, todoItem.Name, todoItem.IsComplete);
}
Ganti konten Program.cs
file dengan kode berikut untuk menggunakan model DTO ini:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
RouteGroupBuilder todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", GetAllTodos);
todoItems.MapGet("/complete", GetCompleteTodos);
todoItems.MapGet("/{id}", GetTodo);
todoItems.MapPost("/", CreateTodo);
todoItems.MapPut("/{id}", UpdateTodo);
todoItems.MapDelete("/{id}", DeleteTodo);
app.Run();
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.Select(x => new TodoItemDTO(x)).ToArrayAsync());
}
static async Task<IResult> GetCompleteTodos(TodoDb db) {
return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).Select(x => new TodoItemDTO(x)).ToListAsync());
}
static async Task<IResult> GetTodo(int id, TodoDb db)
{
return await db.Todos.FindAsync(id)
is Todo todo
? TypedResults.Ok(new TodoItemDTO(todo))
: TypedResults.NotFound();
}
static async Task<IResult> CreateTodo(TodoItemDTO todoItemDTO, TodoDb db)
{
var todoItem = new Todo
{
IsComplete = todoItemDTO.IsComplete,
Name = todoItemDTO.Name
};
db.Todos.Add(todoItem);
await db.SaveChangesAsync();
todoItemDTO = new TodoItemDTO(todoItem);
return TypedResults.Created($"/todoitems/{todoItem.Id}", todoItemDTO);
}
static async Task<IResult> UpdateTodo(int id, TodoItemDTO todoItemDTO, TodoDb db)
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
todo.Name = todoItemDTO.Name;
todo.IsComplete = todoItemDTO.IsComplete;
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
static async Task<IResult> DeleteTodo(int id, TodoDb db)
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return TypedResults.NoContent();
}
return TypedResults.NotFound();
}
Verifikasi bahwa Anda bisa memposting dan mendapatkan semua bidang kecuali bidang rahasia.
Pemecahan masalah dengan sampel yang telah selesai
Jika Anda mengalami masalah yang tidak dapat Anda atasi, bandingkan kode Anda dengan proyek yang telah selesai. Lihat atau unduh proyek yang selesai (cara mengunduh).
Langkah berikutnya
- Konfigurasikan JSopsi serialisasi ON.
- Menangani kesalahan dan pengecualian: Halaman pengecualian pengembang diaktifkan secara default di lingkungan pengembangan untuk aplikasi API minimal. Untuk informasi tentang cara menangani kesalahan dan pengecualian, lihat Menangani kesalahan di API inti ASP.NET.
- Untuk contoh pengujian aplikasi API minimal, lihat sampel GitHub ini.
- Dukungan OpenAPI dalam API minimal.
- Mulai cepat: Terbitkan ke Azure.
- Mengatur API Minimal Inti ASP.NET.
Pelajari lebih lanjut
ASP.NET Core