Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Note
Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 10 tohoto článku.
Warning
Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Aktuální verzi najdete ve verzi .NET 10 tohoto článku.
Autor: Wade Pickett a Tom Dykstra
Minimální rozhraní API jsou navržená tak, aby vytvářela rozhraní API HTTP s minimálními závislostmi. Jsou ideální pro mikroslužby a aplikace, které chtějí do ASP.NET Core zahrnout jenom minimální soubory, funkce a závislosti.
V tomto kurzu se naučíte základy vytváření minimálního rozhraní API s ASP.NET Core. Dalším přístupem k vytváření rozhraní API v ASP.NET Core je použití kontrolerů. Nápovědu k výběru mezi minimálními rozhraními API a rozhraními API založenými na kontroleru najdete v přehledu rozhraní API. Kurz vytvoření projektu rozhraní API na základě kontrolerů , které obsahují další funkce, najdete v tématu Vytvoření webového rozhraní API.
Overview
Tento kurz vytvoří následující rozhraní API:
| API | Description | Obsah požadavku | Tělo odpovědi |
|---|---|---|---|
GET /todoitems |
Získání všech položek úkolů | None | Pole položek úkolů |
GET /todoitems/complete |
Získejte dokončené úkoly | None | Pole položek úkolů |
GET /todoitems/{id} |
Získejte položku podle ID | None | Položka úkolu |
POST /todoitems |
Přidání nové položky | Položka úkolu | Položka úkolu |
PUT /todoitems/{id} |
Aktualizace existující položky | Položka úkolu | None |
PATCH /todoitems/{id} |
Částečné aktualizace položky | Částečný úkol k dokončení | None |
DELETE /todoitems/{id} |
Odstranění položky | None | None |
Prerequisites
Nejnovější verze sady Visual Studio s úlohou vývoje pro ASP.NET a web .
Vytvoření projektu rozhraní API
Spusťte Visual Studio 2026 a vyberte Vytvořit nový projekt.
V dialogovém okně Vytvořit nový projekt :
- Vyberte typ projektu základního webového rozhraní API ASP.NET a vyberte Další.
- Pojmenujte projekt TodoApi a vyberte Další.
V dialogovém okně Další informace :
- Ověřte, že Framework je .NET 10.0 (dlouhodobá podpora).
- Ověřte, že je zaškrtnuté políčko Povolit podporu OpenAPI.
- Ověřte, že políčko Použít kontrolerynení zaškrtnuté. Zrušte zaškrtnutí tohoto nastavení, pokud chcete vytvořit minimální projekt rozhraní API podle potřeby pro tento kurz místo projektu založeného na kontroleru.
- Vyberte Vytvořit.
Kontrola kódu
Soubor Program.cs vygenerovaný šablonou obsahuje následující kód:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast");
app.Run();
internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
Předchozí kód:
- Vytvoří WebApplicationBuilder a WebApplication s předkonfigurovanými výchozími nastaveními.
- Zaregistruje integrovaný generátor dokumentu OpenAPI pomocí
builder.Services.AddOpenApi(). - Namapuje vygenerovaný dokument OpenAPI na adrese
/openapi/v1.jsonpomocíapp.MapOpenApi()pouze v prostředí Development. - Definuje ukázkový
GET /weatherforecastkoncový bod, který vrací pět náhodně generovanýchWeatherForecastzáznamů.
V tomto kurzu nahradíte WeatherForecast novou ukázkou Todo s koncovými body pro vytváření, čtení, aktualizaci a odstraňování položek, která využívá model a databázi.
Přidání balíčků NuGet
Přidejte balíčky NuGet pro podporu databáze použité v tomto kurzu.
- V nabídce Nástroje vyberte NuGet Správce balíčků > Spravovat balíčky NuGet pro řešení.
- Vyberte kartu Procházet.
- Do vyhledávacího pole zadejte Microsoft.EntityFrameworkCore.InMemory a pak vyberte
Microsoft.EntityFrameworkCore.InMemory. - V pravém podokně zaškrtněte políčko Projekt a pak vyberte Nainstalovat.
Třídy kontextu modelu a databáze
- Ve složce projektu vytvořte soubor s názvem
Todo.css následujícím kódem:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Předchozí kód vytvoří model pro tuto aplikaci. Model je třída, která představuje data, která aplikace spravuje.
Součástí Secret této vlastnosti je ukázka běžné potřeby reálného světa: data, která aplikace ukládá a používá interně, například ID uživatele, který položku vlastní, nechcete, aby klienti viděli nebo nastavili. V kroku Zabránění nadměrnému odesílání dat, který následuje později v tomto kurzu, použijete objekt pro přenos dat (DTO), aby se pole jako Secret neobjevovala ve vstupech a odpovědích rozhraní API.
- Vytvořte soubor s názvem
TodoDb.css následujícím kódem:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
Předchozí kód definuje kontext databáze, což je hlavní třída, která koordinuje funkce Entity Framework pro datový model. Tato třída je odvozena od Microsoft.EntityFrameworkCore.DbContext třídy.
Aplikace používá databázi v paměti s názvem TodoList. Databáze se později zaregistruje v Program.cs pomocí builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));, kde řetězec "TodoList" představuje název databáze. Vzhledem k tomu, že data jsou uložená v paměti, obnoví se při každém restartování aplikace.
Nahraďte ukázku WeatherForecast rozhraním Todo API
Šablona webapi přidá do Program.cs ukázkový koncový bod GET /weatherforecast a na konci souboru záznam WeatherForecast. Ukázka je pouze zástupný text, který ukazuje, že šablona funguje – oba nahraďte koncovými body Todo popsanými v následující části.
- Nahraďte veškerý kód
Program.csnásledujícím kódem. UkázkovýWeatherForecastendpoint a záznam jsou odstraněny a nahrazují je endpointy Todo:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddOpenApi();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
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();
Řádky OpenAPI, které šablona přidá (builder.Services.AddOpenApi() a app.MapOpenApi()) zůstanou na místě. Nyní popisují endpointy Todo místo ukázkového endpointu pro počasí.
Následující zvýrazněný kód zaregistruje služby aplikace v kontejneru injektáže závislostí (DI): kontext databáze (AddDbContext) a generátor dokumentů OpenAPI (AddOpenApi):
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddOpenApi();
var app = builder.Build();
Kontejner DI poskytuje přístup k kontextu databáze a dalším službám.
V tomto kurzu se k otestování rozhraní API používá Průzkumník koncových bodů a soubory .http.
Testování zveřejňování dat
Následující kód v Program.cs vytvoří HTTP POST koncový bod /todoitems, který přidá data do paměťové databáze:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
- Stisknutím kláves Ctrl+F5 spusťte aplikaci bez ladění. Visual Studio spustí Kestrel webový server a v případě potřeby důvěřuje vývojovému certifikátu.
Visual Studio zobrazí následující dialogové okno:
Pokud důvěřujete certifikátu SSL služby IIS Express, vyberte Ano .
Zobrazí se následující dialogové okno:
Pokud souhlasíte s tím, že se má důvěřovat vývojovému certifikátu, vyberte Ano.
Informace o tom, jak důvěřovat prohlížeči Firefox, naleznete v části k chybě certifikátu Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.
Pomocí koncového bodu POST přidejte data do aplikace.
Vyberte Zobrazit>Další okna>Průzkumník koncových bodů.
Klikněte pravým tlačítkem na koncový bod POST a vyberte Vygenerovat požadavek.
Ve složce projektu s názvem
TodoApi.httpse vytvoří nový soubor s podobným obsahem jako v následujícím příkladu:
@TodoApi_HostAddress = https://localhost:7031
POST {{TodoApi_HostAddress}}/todoitems
###
První řádek vytvoří proměnnou, která se použije pro všechny koncové body.
Další řádek definuje požadavek POST.
Řádek s trojitým hashtagem (
###) je značka oddělující požadavek: co následuje za ní, je určené pro jiný požadavek.Požadavek POST potřebuje hlavičky a text. Pokud chcete definovat tyto části požadavku, přidejte následující řádky bezprostředně za řádek požadavku POST:
Content-Type: application/json
{
"name":"walk dog",
"isComplete":true
}
Předchozí kód přidá hlavičku Content-Type a text požadavku JSON. Soubor TodoApi.http by teď měl vypadat jako v následujícím příkladu, ale s číslem portu:
@TodoApi_HostAddress = https://localhost:7057
POST {{TodoApi_HostAddress}}/todoitems
Content-Type: application/json
{
"name":"walk dog",
"isComplete":true
}
###
Vyberte odkaz Odeslat žádost, který je nad řádkem
POSTžádosti.
Požadavek POST se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi .
Prozkoumání koncových bodů GET
Vaše Program.cs zahrnuje několik koncových bodů GET definovaných pomocí MapGet:
| API | Description | Obsah požadavku | Tělo odpovědi |
|---|---|---|---|
GET /todoitems |
Získání všech položek úkolů | None | Pole položek úkolů |
GET /todoitems/complete |
Získání všech dokončených položek úkolů | None | Pole položek úkolů |
GET /todoitems/{id} |
Získejte položku podle ID | None | Položka úkolu |
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());
Testování koncových bodů GET
Otestujte aplikaci voláním GET koncových bodů z prohlížeče nebo pomocí Průzkumníka koncových bodů. Následující kroky jsou určené pro Průzkumníka koncových bodů.
V Průzkumníku koncových bodů klikněte pravým tlačítkem na první koncový bod GET a vyberte Vygenerovat požadavek.
Do souboru
TodoApi.httpbyl přidán následující obsah:
GET {{TodoApi_HostAddress}}/todoitems
###
Vyberte odkaz Odeslat žádost, který je nad novým
GETřádkem žádosti.Požadavek GET se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi .
Text odpovědi je podobný následujícímu formátu JSON:
[
{
"id": 1,
"name": "walk dog",
"isComplete": true,
"secret": null
}
]
- V Průzkumníku koncových bodů klikněte pravým tlačítkem na
/todoitems/{id}koncový bod GET a vyberte Vygenerovat požadavek. Do souboruTodoApi.httpbyl přidán následující obsah:
GET {{TodoApi_HostAddress}}/todoitems/{id}
###
Nahraďte
{id}s1.Vyberte odkaz Odeslat požadavek, který je nad novým řádkem požadavku GET.
Požadavek GET se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi .
Text odpovědi je podobný následujícímu formátu JSON:
{
"id": 1,
"name": "walk dog",
"isComplete": true,
"secret": null
}
Tato aplikace používá databázi v paměti. Pokud aplikaci restartujete, data se ztratí: GET /todoitems vrátí prázdné pole ([]) a GET /todoitems/{id} vrátí hodnotu 404 Not Found. Pokud chcete aplikaci znovu naplnit, post data do aplikace a zkuste požadavek GET znovu.
Návratové hodnoty
ASP.NET Core automaticky serializuje objekt do formátu JSON a zapíše json do textu zprávy odpovědi. Kód odpovědi pro tento návratový typ je 200 OK, za předpokladu, že neexistují žádné neošetřené výjimky. Neošetřené výjimky se překládají do chyb 5xx.
Návratové typy mohou představovat širokou škálu stavových kódů HTTP. Může například GET /todoitems/{id} vrátit dvě různé hodnoty stavu:
- Pokud žádná položka neodpovídá požadovanému ID, vrátí metoda kód chyby stavuNotFound 404.
- V opačném případě metoda vrátí hodnotu 200 s textem odpovědi JSON. Vrácení
itemvede k odpovědi HTTP 200.
Prozkoumání koncového bodu PUT
Ukázková aplikace implementuje jeden koncový bod PUT pomocí 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();
});
Tato metoda je podobná MapPost metodě, s výjimkou použití HTTP PUT. Úspěšná odpověď vrátí hodnotu 204 (bez obsahu). Podle specifikace HTTP požadavek PUT vyžaduje, aby klient odeslal celou aktualizovanou entitu, nejen změny. Pokud chcete podporovat částečné aktualizace, použijte HTTP PATCH.
Testování koncového bodu PUT
Tato ukázka používá databázi v paměti, kterou musíte inicializovat při každém spuštění aplikace. Před voláním PUT potřebujete položku v databázi. Před voláním metody PUT zavolejte post a ujistěte se, že je v databázi položka.
Aktualizujte položku Todo, která má Id = 1, a nastavte její název na "feed fish".
V Průzkumníku koncových bodů klikněte pravým tlačítkem myši na koncový bod PUT a vyberte Vygenerovat požadavek.
Do souboru
TodoApi.httpbyl přidán následující obsah:
PUT {{TodoApi_HostAddress}}/todoitems/{id}
###
Na řádku požadavku PUT nahraďte
{id}s1.Přidejte následující řádky bezprostředně za řádek požadavku PUT:
Content-Type: application/json
{
"id": 1,
"name": "feed fish",
"isComplete": false
}
Předchozí kód přidá hlavičku Content-Type a text požadavku JSON.
- Vyberte odkaz Odeslat žádost, který je nad novým řádkem požadavku PUT.
Požadavek PUT se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi . Tělo odpovědi je prázdné a stavový kód je 204.
Vytvoření a prozkoumání koncového bodu PATCH
Koncový bod PATCH umožňuje klientům odesílat pouze pole, která chtějí aktualizovat, například přejmenování položky seznamu úkolů bez opětovného odeslání stavu dokončení. Tento přístup se liší od požadavku PUT, který nahrazuje celou položku, takže klient musí odesílat všechna pole, i když se změní jenom jedno.
V dalších krocích přidáte nový soubor a upravíte soubor Program.cs. Před provedením těchto změn zastavte aplikaci TodoApi. Nechte skalární stránku v prohlížeči spuštěnou.
- Pokud chcete aplikaci zastavit, vyberte na panelu nástrojů Visual Studio tlačítko Zastavit (červený čtverec).
Tato ukázka používá databázi v paměti, kterou musíte inicializovat při každém spuštění aplikace. Databáze musí obsahovat položku před voláním PATCH. Před voláním funkce PATCH zavolejte post a ujistěte se, že databáze obsahuje položku.
Koncový bod PATCH používá třídu s vlastnostmi s možnou TodoPatchDto hodnotou null ke správnému zpracování částečných aktualizací. Použitím nulovatelných vlastností může endpoint rozlišovat mezi polem, které nebylo zadáno (null), a polem explicitně nastaveným na hodnotu (včetně hodnoty false u booleovských polí). Bez vlastností s možnou hodnotou null má nenulovatelná hodnota typu bool ve výchozím stavu hodnotu false, což by mohlo nechtěně přepsat existující hodnotu true, když toto pole není zahrnuto v požadavku.
- Vytvořte soubor s názvem
TodoPatchDto.css následujícím kódem:
public class TodoPatchDto
{
public string? Name { get; set; }
public bool? IsComplete { get; set; }
}
Třída TodoPatchDto používá vlastnosti s možnou hodnotou null (string? a bool?) k rozlišení mezi polem, které nebylo zadané v požadavku, a pole explicitně nastavené na hodnotu.
- Do
Program.cspřidejte následující koncový bod PATCH bezprostředně za koncový bodMapPut:
app.MapPatch("/todoitems/{id}", async (int id, TodoPatchDto inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
if (inputTodo.Name is not null) todo.Name = inputTodo.Name;
if (inputTodo.IsComplete is not null) todo.IsComplete = inputTodo.IsComplete.Value;
await db.SaveChangesAsync();
return Results.NoContent();
});
Tato metoda je podobná MapPut metodě, ale používá http PATCH a aktualizuje pouze pole uvedená v požadavku. Úspěšná odpověď vrátí hodnotu 204 (bez obsahu).
Note
Operace PATCH umožňují částečné aktualizace prostředků. Pokročilejší částečné aktualizace pomocí dokumentů OPRAV JSON najdete v tématu JsonPatch ve webovém rozhraní API ASP.NET Core.
Otestujte koncový bod PATCH
- Stisknutím kombinace kláves Ctrl+F5 znovu sestavte a spusťte aplikaci s novým koncovým bodem PATCH.
Tato ukázka používá databázi v paměti, kterou musíte inicializovat při každém spuštění aplikace. Databáze musí obsahovat položku před voláním PATCH. Voláním metody POST se ujistěte, že databáze má položku před voláním PATCH.
Aktualizujte pouze vlastnost name položky Todo, která má Id = 1, a nastavte její název na "run errands".
V Průzkumníku koncových bodů vyberte tlačítko Aktualizovat. Potom klikněte pravým tlačítkem na koncový bod PATCH a vyberte Vygenerovat požadavek.
Do souboru
TodoApi.httpbyl přidán následující obsah:
PATCH {{TodoApi_HostAddress}}/todoitems/{id}
###
Na řádku požadavku PATCH nahraďte
{id}za1.Přidejte následující řádky bezprostředně za řádek žádosti PATCH:
Content-Type: application/json
{
"name": "run errands"
}
Předchozí kód přidá hlavičku Content-Type a text požadavku JSON pouze s polem, které se má aktualizovat.
- Vyberte odkaz Odeslat požadavek , který je nad novým řádkem žádosti PATCH.
Požadavek PATCH se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi . Tělo odpovědi je prázdné a stavový kód je 204.
Prozkoumání koncového bodu DELETE
Váš Program.cs soubor obsahuje jeden koncový bod DELETE definovaný pomocí 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();
});
Testování koncového bodu DELETE
V Průzkumníku koncových bodů klikněte pravým tlačítkem na koncový bod DELETE a vyberte Vygenerovat požadavek.
Přidá se DELETE požadavek k
TodoApi.http.Nahraďte
{id}v řádku žádosti DELETE textem1. Požadavek DELETE by měl vypadat jako v následujícím příkladu:
DELETE {{TodoApi_HostAddress}}/todoitems/1
###
- Vyberte odkaz Odeslat žádost pro požadavek DELETE.
Požadavek DELETE se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi . Tělo odpovědi je prázdné a stavový kód je 204.
Použití rozhraní MapGroup API
- Další kroky upraví
Program.cssoubor, proto aplikaci před provedením těchto změn zastavte. Nechte stránku Scalar otevřenou v prohlížeči.
- Pokud chcete aplikaci zastavit, vyberte na panelu nástrojů Visual Studio tlačítko Zastavit (červený čtverec).
Soubor Program.cs, který jste napsali, opakuje při každém nastavení endpointu předponu adresy URL todoitems. Rozhraní API často obsahují skupiny koncových bodů s běžnou předponou adresy URL a MapGroup metoda pomáhá tyto skupiny uspořádat. Redukuje opakující se kód a umožňuje přizpůsobit celé skupiny koncových bodů jediným voláním metod, jako a RequireAuthorizationWithMetadata.
-
Program.csObsah nahraďte následujícím kódem:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddOpenApi();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
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.MapPatch("/{id}", async (int id, TodoPatchDto inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
if (inputTodo.Name is not null) todo.Name = inputTodo.Name;
if (inputTodo.IsComplete is not null) todo.IsComplete = inputTodo.IsComplete.Value;
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();
Předchozí kód má následující změny:
Přidá
var todoItems = app.MapGroup("/todoitems");k nastavení skupiny pomocí předpony adresy URL/todoitems.Změní všechny
app.Map<HttpVerb>metody natodoItems.Map<HttpVerb>.Odebere předponu
/todoitemsadresy URL zMap<HttpVerb>volání metod.Spustí aplikaci a otestuje koncové body a ověří, že fungují stejně.
Použití rozhraní TypedResults API
Další kroky upraví Program.cs soubor, proto aplikaci před provedením těchto změn zastavte. Nechte stránku Scalar otevřenou v prohlížeči.
Vracení TypedResults místo Results má několik výhod, včetně testovatelnosti a automatického vracení metadat typu odpovědi pro OpenAPI k popisu koncového bodu. Další informace naleznete v tématu TypedResults vs Results.
- Metody
Map<HttpVerb>mohou místo lambda volat metody obslužné rutiny trasy. Pokud chcete zobrazit příklad, aktualizujte Program.cs následujícím kódem:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddOpenApi();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", GetAllTodos);
todoItems.MapGet("/complete", GetCompleteTodos);
todoItems.MapGet("/{id}", GetTodo);
todoItems.MapPost("/", CreateTodo);
todoItems.MapPut("/{id}", UpdateTodo);
todoItems.MapPatch("/{id}", PatchTodo);
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> PatchTodo(int id, TodoPatchDto inputTodo, TodoDb db)
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
if (inputTodo.Name is not null) todo.Name = inputTodo.Name;
if (inputTodo.IsComplete is not null) todo.IsComplete = inputTodo.IsComplete.Value;
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();
}
Kód Map<HttpVerb> teď volá metody místo 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.MapPatch("/{id}", PatchTodo);
todoItems.MapDelete("/{id}", DeleteTodo);
Následující metody vracejí objekty, které implementují IResult a jsou definovány 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> PatchTodo(int id, TodoPatchDto inputTodo, TodoDb db)
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
if (inputTodo.Name is not null) todo.Name = inputTodo.Name;
if (inputTodo.IsComplete is not null) todo.IsComplete = inputTodo.IsComplete.Value;
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();
}
Testy jednotek mohou volat tyto metody a testovat, že vracejí správný datový typ. Například pokud je metoda GetAllTodos:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Kód unit testu může ověřit, že objekt typu Ok<Todo[]> je vrácen z metody obslužné rutiny. Například:
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);
}
Prevence nadměrného zadávání
V současné době vaše rozhraní API zpřístupňuje celý Todo objekt, včetně Secret vlastnosti, kterou jste přidali do tříd kontextu modelu a databáze. V produkčních aplikacích použijte podmnožinu modelu k omezení dat, která můžou klienti zadávat a přijímat. Hlavním důvodem tohoto omezení je zabezpečení. Tato podmnožina modelu se obvykle označuje jako objekt pro přenos dat (DTO), vstupní model nebo model zobrazení. Tento článek používá DTO.
Použijte DTO k těmto účelům:
- Zabránit nadměrnému odesílání dat.
- Skryjte vlastnosti, které klienti nemají zobrazit.
- Vynechejte některé vlastnosti pro zmenšení velikosti datové části.
- Zplošťujte grafy objektů, které obsahují vnořené objekty. Grafy plochých objektů můžou být pro klienty pohodlnější.
Tato aplikace potřebuje skrýt Secret pole, ale aplikace pro správu by se mohla rozhodnout, že ho zveřejní.
Další kroky přidají soubor, proto aplikaci před provedením těchto změn zastavte. Nechte stránku scalar otevřenou v prohlížeči.
Vytvořte soubor s názvem
TodoItemDTO.css následujícím kódem:
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);
}
-
Program.csObsah souboru nahraďte následujícím kódem, který použije tento model DTO:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddOpenApi();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
var todoItems = app.MapGroup("/todoitems");
todoItems.MapGet("/", GetAllTodos);
todoItems.MapGet("/complete", GetCompleteTodos);
todoItems.MapGet("/{id}", GetTodo);
todoItems.MapPost("/", CreateTodo);
todoItems.MapPut("/{id}", UpdateTodo);
todoItems.MapPatch("/{id}", PatchTodo);
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> PatchTodo(int id, TodoPatchDto inputTodo, TodoDb db)
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return TypedResults.NotFound();
if (inputTodo.Name is not null) todo.Name = inputTodo.Name;
if (inputTodo.IsComplete is not null) todo.IsComplete = inputTodo.IsComplete.Value;
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();
}
- Spusťte aplikaci a ověřte, že můžete odesílat a získat všechna pole s výjimkou pole
Secret.
Řešení potíží s dokončeným příkladem
Pokud narazíte na problém, který nemůžete vyřešit, porovnejte kód s dokončeným projektem. Zobrazit nebo stáhnout dokončený projekt (jak stáhnout)
Další kroky
- Nakonfigurujte možnosti serializace JSON.
- Zpracování chyb a výjimek: Ve výchozím nastavení je stránka výjimky vývojáře povolená v
Developmentprostředí pro minimální aplikace API. Informace o zpracování chyb a výjimek najdete v tématu Zpracování chyb v rozhraních ASP.NET Core API. - Příklad testování minimální aplikace API najdete v této ukázce GitHubu.
- Podpora OpenAPI v minimálních rozhraních API
- Rychlý start: Publikování do Azure
- Uspořádání minimalních rozhraní API ASP.NET Core
Learn more
Stručné referenční informace k minimálním rozhraním API
Minimální rozhraní API jsou navržena tak, aby vytvářela rozhraní HTTP API s minimálními závislostmi. Jsou ideální pro mikroslužby a aplikace, které chtějí do ASP.NET Core zahrnout jenom minimální soubory, funkce a závislosti.
V tomto kurzu se naučíte základy vytváření minimálního rozhraní API s ASP.NET Core. Dalším přístupem k vytváření rozhraní API v ASP.NET Core je použití kontrolerů. Nápovědu k výběru mezi minimálními rozhraními API a rozhraními API založenými na kontroleru najdete v přehledu rozhraní API. Kurz vytvoření projektu rozhraní API na základě kontrolerů , které obsahují další funkce, najdete v tématu Vytvoření webového rozhraní API.
Overview
Tento kurz vytvoří následující rozhraní API:
| API | Description | Obsah požadavku | Tělo odpovědi |
|---|---|---|---|
GET /todoitems |
Získání všech položek úkolů | None | Pole položek úkolů |
GET /todoitems/complete |
Získejte dokončené úkoly | None | Pole položek úkolů |
GET /todoitems/{id} |
Získejte položku podle ID | None | Položka úkolu |
POST /todoitems |
Přidání nové položky | Položka úkolu | Položka úkolu |
PUT /todoitems/{id} |
Aktualizace existující položky | Položka úkolu | None |
PATCH /todoitems/{id} |
Částečné aktualizace položky | Částečný úkol k dokončení | None |
DELETE /todoitems/{id} |
Odstranění položky | None | None |
Prerequisites
Visual Studio 2022 s pracovním zatížením ASP.NET a vývoje webu
Vytvoření projektu rozhraní API
Spusťte Visual Studio 2022 a vyberte Vytvořit nový projekt.
V dialogovém okně Vytvořit nový projekt :
- Zadejte
Emptydo vyhledávacího pole Hledat šablony . - Vyberte šablonu ASP.NET Core Empty a vyberte Další.
- Zadejte
Pojmenujte projekt TodoApi a vyberte Další.
V dialogovém okně Další informace :
- Vyberte .NET 7.0.
- Zrušte zaškrtnutí políčka Nepoužívat příkazy nejvyšší úrovně
- Vyberte Vytvořit
Kontrola kódu
Soubor Program.cs obsahuje následující kód:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Předchozí kód:
- Vytvoří WebApplicationBuilder a WebApplication s předkonfigurovanými výchozími nastaveními.
- Vytvoří koncový bod
/HTTP GET, který vrátíHello World!:
Spustit aplikaci
Stisknutím kombinace kláves Ctrl+F5 spusťte bez debuggeru.
Visual Studio zobrazí následující dialogové okno:
Pokud důvěřujete certifikátu SSL služby IIS Express, vyberte Ano .
Zobrazí se následující dialogové okno:
Pokud souhlasíte s tím, že se má důvěřovat vývojovému certifikátu, vyberte Ano.
Informace o tom, jak důvěřovat prohlížeči Firefox, naleznete v části k chybě certifikátu Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.
Visual Studio spustí Kestrel webový server a otevře okno prohlížeče.
Hello World! se zobrazí v prohlížeči. Soubor Program.cs obsahuje minimální, ale kompletní aplikaci.
Přidání balíčků NuGet
Pro podporu databáze a diagnostiky používané v tomto kurzu je potřeba přidat balíčky NuGet.
- V nabídce Nástroje vyberte NuGet Správce balíčků > Spravovat balíčky NuGet pro řešení.
- Vyberte kartu Procházet.
- Do vyhledávacího pole zadejte Microsoft.EntityFrameworkCore.InMemory a pak vyberte
Microsoft.EntityFrameworkCore.InMemory. - Zaškrtněte políčko Project v pravém podokně.
- V rozevíracím seznamu Verze vyberte nejnovější dostupnou verzi 7, například
7.0.17a pak vyberte Nainstalovat. - Podle předchozích pokynů přidejte balíček s nejnovější dostupnou
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCoreverzí 7.
Třídy kontextu modelu a databáze
Ve složce projektu vytvořte soubor s názvem Todo.cs s následujícím kódem:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Předchozí kód vytvoří model pro tuto aplikaci. Model je třída, která představuje data, která aplikace spravuje.
Vytvořte soubor s názvem TodoDb.cs s následujícím kódem:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
Předchozí kód definuje kontext databáze, což je hlavní třída, která koordinuje funkce Entity Framework pro datový model. Tato třída je odvozena od Microsoft.EntityFrameworkCore.DbContext třídy.
Přidání kódu rozhraní API
Obsah souboru Program.cs nahraďte následujícím kódem:
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();
Následující zvýrazněný kód přidá kontext databáze do kontejneru injektáže závislostí (DI) a povolí zobrazení výjimek souvisejících s databází:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
Kontejner DI poskytuje přístup k kontextu databáze a dalším službám.
Vytvoření uživatelského rozhraní api pro testování pomocí Swaggeru
Můžete si vybrat z mnoha dostupných testovacích nástrojů webového rozhraní API a můžete postupovat podle úvodních testovacích kroků tohoto kurzu pomocí vlastního preferovaného nástroje.
Tento kurz využívá balíček .NET NSwag.AspNetCore, který integruje nástroje Swaggeru pro generování testovacího uživatelského rozhraní, které dodržuje specifikaci OpenAPI:
- NSwag: Knihovna .NET, která integruje Swagger přímo do aplikací ASP.NET Core a poskytuje middleware a konfiguraci.
- Swagger: Sada opensourcových nástrojů, jako jsou OpenAPIGenerator a SwaggerUI, které generují stránky testování rozhraní API, které se řídí specifikací OpenAPI.
- Specifikace OpenAPI: Dokument, který popisuje možnosti rozhraní API na základě anotací XML a atributů v rámci kontrolerů a modelů.
Další informace o používání OpenAPI a NSwag s ASP.NET najdete v dokumentaci k webovému rozhraní API pro ASP.NET Core pomocí Swaggeru nebo OpenAPI.
Instalace nástrojů Swagger
Spusťte následující příkaz:
dotnet add package NSwag.AspNetCore
Předchozí příkaz přidá balíček NSwag.AspNetCore , který obsahuje nástroje pro generování dokumentů a uživatelského rozhraní Swagger.
Konfigurace middlewaru Swaggeru
Před tím, než je
appdefinován na řádkuvar app = builder.Build();, přidejte následující zvýrazněný kód.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();
V předchozím kódu:
builder.Services.AddEndpointsApiExplorer();: Povolí Průzkumníka rozhraní API, což je služba, která poskytuje metadata o rozhraní HTTP API. Swagger používá Průzkumník rozhraní API k vygenerování dokumentu Swagger.builder.Services.AddOpenApiDocument(config => {...});: Přidá generátor dokumentů Swagger OpenAPI do aplikačních služeb a nakonfiguruje ho tak, aby poskytoval další informace o rozhraní API, jako je jeho název a verze. Informace o poskytování robustnějších podrobností rozhraní API najdete v tématu Začínáme se službou NSwag a ASP.NET Core.Přidejte následující zvýrazněný kód na další řádek za definici
appna řádkuvar 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"; }); }Předchozí kód umožňuje middleware Swagger pro obsluhu generovaného dokumentu JSON a uživatelského rozhraní Swagger. Swagger je povolený jenom ve vývojovém prostředí. Povolení Swaggeru v produkčním prostředí by mohlo vystavit potenciálně citlivé podrobnosti o struktuře a implementaci rozhraní API.
Testování zveřejňování dat
Následující kód v Program.cs vytvoří HTTP POST koncový bod /todoitems, který přidá data do paměťové databáze:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
Spustit aplikaci. Prohlížeč zobrazí chybu 404, protože / koncový bod již neexistuje.
Koncový bod POST se použije k přidání dat do aplikace.
Když aplikace stále běží, v prohlížeči přejděte na
https://localhost:<port>/swagger, abyste zobrazili stránku testování rozhraní API vygenerovanou Swaggerem.
Na stránce pro testování rozhraní Swagger API vyberte Post /todoitems>.
Všimněte si, že pole Text požadavku obsahuje vygenerovaný ukázkový formát, který odráží parametry rozhraní API.
V textu požadavku zadejte JSON pro položku úkolu bez zadání volitelné
idpoložky:{ "name":"walk dog", "isComplete":true }Vyberte Spustit.
Swagger poskytuje podokno Odpovědi pod tlačítkem Spustit.
Poznamenejte si několik užitečných podrobností:
- cURL: Swagger poskytuje příklad příkazu cURL v syntaxi systému Unix/Linux, který se dá spustit na příkazovém řádku s libovolným prostředím Bash, které používá syntaxi systému Unix/Linux, včetně Git Bashu z Gitu pro Windows.
- Adresa URL požadavku: Zjednodušená reprezentace požadavku HTTP vytvořeného kódem JavaScript uživatelského rozhraní Swagger pro volání rozhraní API. Skutečné požadavky můžou obsahovat podrobnosti, jako jsou hlavičky a parametry dotazu a text požadavku.
- Odpověď serveru: Obsahuje text odpovědi a hlavičky. Tělo odpovědi ukazuje, že
idbyla nastavena na1. - Kód odpovědi: Vrátil se stavový kód 201
HTTP, který indikoval, že požadavek byl úspěšně zpracován a způsobil vytvoření nového prostředku.
Prozkoumání koncových bodů GET
Ukázková aplikace implementuje několik koncových bodů GET voláním MapGet:
| API | Description | Obsah požadavku | Tělo odpovědi |
|---|---|---|---|
GET /todoitems |
Získání všech položek úkolů | None | Pole položek úkolů |
GET /todoitems/complete |
Získání všech dokončených položek úkolů | None | Pole položek úkolů |
GET /todoitems/{id} |
Získejte položku podle ID | None | Položka úkolu |
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());
Testování koncových bodů GET
Otestujte aplikaci voláním koncových bodů z prohlížeče nebo Swaggeru.
V Swaggeru vyberte GET /todoitems>Vyzkoušejte to>Spustit.
Alternativně volejte GET /todoitems v prohlížeči zadáním adresy URI
http://localhost:<port>/todoitems. Napříkladhttp://localhost:5001/todoitems
Volání na GET /todoitems vytvoří odpověď podobnou následující:
[
{
"id": 1,
"name": "walk dog",
"isComplete": true
}
]
Zavolejte GET /todoitems/{id} ve Swaggeru pro získání dat pro konkrétní ID.
- Vyberte GET /todoitems>Vyzkoušet.
-
Nastavte pole ID na
1a vyberte Spustit.
Alternativně volejte GET /todoitems v prohlížeči zadáním adresy URI
https://localhost:<port>/todoitems/1. Napříkladhttps://localhost:5001/todoitems/1Odpověď je podobná následující:
{ "id": 1, "name": "walk dog", "isComplete": true }
Tato aplikace používá databázi v paměti. Pokud se aplikace restartuje, požadavek GET nevrací žádná data. Pokud se nevrátí žádná data, post data do aplikace a zkuste požadavek GET zopakovat.
Návratové hodnoty
ASP.NET Core automaticky serializuje objekt do formátu JSON a zapíše json do textu zprávy odpovědi. Kód odpovědi pro tento návratový typ je 200 OK, za předpokladu, že neexistují žádné neošetřené výjimky. Neošetřené výjimky se překládají do chyb 5xx.
Návratové typy mohou představovat širokou škálu stavových kódů HTTP. Může například GET /todoitems/{id} vrátit dvě různé hodnoty stavu:
- Pokud žádná položka neodpovídá požadovanému ID, vrátí metoda kód chyby stavuNotFound 404.
- V opačném případě metoda vrátí hodnotu 200 s textem odpovědi JSON. Vrácení
itemvede k odpovědi HTTP 200.
Prozkoumání koncového bodu PUT
Ukázková aplikace implementuje jeden koncový bod PUT pomocí 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();
});
Tato metoda je podobná MapPost metodě, s výjimkou použití HTTP PUT. Úspěšná odpověď vrátí hodnotu 204 (bez obsahu). Podle specifikace HTTP požadavek PUT vyžaduje, aby klient odeslal celou aktualizovanou entitu, nejen změny. Pokud chcete podporovat částečné aktualizace, použijte HTTP PATCH.
Testování koncového bodu PUT
Tato ukázka používá databázi v paměti, která se musí inicializovat při každém spuštění aplikace. Před voláním PUT musí být v databázi položka. Před voláním metody PUT zavolejte get a ujistěte se, že je v databázi položka.
Aktualizujte položku úkolu, která má Id = 1, a nastavte její název na "feed fish".
Odeslání požadavku PUT pomocí Swaggeru:
Vyberte Put /todoitems/{id}>Vyzkoušet.
Nastavte pole ID na hodnotu
1.Nastavte text požadavku na následující JSON:
{ "name": "feed fish", "isComplete": false }Vyberte Spustit.
Prozkoumání koncového bodu PATCH
Vytvořte soubor s názvem TodoPatchDto.cs s následujícím kódem:
public class TodoPatchDto
{
public string? Name { get; set; }
public bool? IsComplete { get; set; }
}
Třída TodoPatchDto používá vlastnosti s možnou hodnotou null (string? a bool?) k rozlišení mezi polem, které nebylo zadané v požadavku, a pole explicitně nastavené na hodnotu.
Ukázková aplikace implementuje jeden koncový bod PATCH pomocí MapPatch:
app.MapPatch("/todoitems/{id}", async (int id, TodoPatchDto inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
if (inputTodo.Name is not null) todo.Name = inputTodo.Name;
if (inputTodo.IsComplete is not null) todo.IsComplete = inputTodo.IsComplete.Value;
await db.SaveChangesAsync();
return Results.NoContent();
});
Tato metoda je podobná MapPut metodě, s výjimkou použití HTTP PATCH a aktualizuje pouze pole uvedená v požadavku. Úspěšná odpověď vrátí hodnotu 204 (bez obsahu). Podle specifikace HTTP požadavek PATCH umožňuje částečné aktualizace, což klientům umožňuje odesílat pouze pole, která je potřeba změnit.
Koncový bod PATCH používá třídu s vlastnostmi s možnou TodoPatchDto hodnotou null ke správnému zpracování částečných aktualizací. Použití nullable vlastností umožňuje koncovému bodu rozlišovat mezi polem, které nebylo zadáno (null), a polem, které je explicitně nastavené na určitou hodnotu (včetně false pro logická pole). Bez vlastností umožňujících null by se ve výchozím nastavení hodnota nepravdivého stavu nastavila na false, což by potenciálně přepsalo existující hodnotu true, pokud toto pole nebylo zahrnuto v požadavku.
Note
Operace PATCH umožňují částečné aktualizace prostředků. Pokročilejší částečné aktualizace pomocí dokumentů OPRAV JSON najdete v tématu JsonPatch ve webovém rozhraní API ASP.NET Core.
Otestujte koncový bod PATCH
Tato ukázka používá databázi v paměti, která se musí inicializovat při každém spuštění aplikace. Před voláním PATCH musí být v databázi položka. Před voláním funkce PATCH zavolejte metodu GET, abyste měli jistotu, že je v databázi položka.
Aktualizujte pouze name vlastnost to-do položky, která má Id = 1 a nastavte její název na "run errands".
Pomocí Swaggeru odešlete požadavek PATCH:
Vyberte Patch /todoitems/{id}>Vyzkoušet.
Nastavte pole ID na hodnotu
1.Nastavte text požadavku na následující JSON:
{ "name": "run errands" }Vyberte Spustit.
Prozkoumání a otestování koncového bodu DELETE
Ukázková aplikace implementuje jeden koncový bod DELETE pomocí 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();
});
K odeslání požadavku DELETE použijte Swagger:
Vyberte DELETE /todoitems/{id}>Vyzkoušet.
Nastavte pole ID na
1a vyberte Spustit.Požadavek DELETE se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi . Tělo odpovědi je prázdné a stavový kód odpovědi serveru je 204.
Použití rozhraní MapGroup API
Vzorový kód aplikace opakuje předponu todoitems adresy URL pokaždé, když nastaví koncový bod. Rozhraní API často obsahují skupiny koncových bodů se společnou předponou adresy URL a metoda MapGroup je k dispozici k uspořádání takových skupin. Redukuje opakující se kód a umožňuje přizpůsobit celé skupiny koncových bodů jediným voláním metod, jako RequireAuthorization a WithMetadata.
Program.cs Obsah nahraďte následujícím kódem:
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();
Předchozí kód má následující změny:
- Přidá
var todoItems = app.MapGroup("/todoitems");k nastavení skupiny pomocí předpony adresy URL/todoitems. - Změní všechny
app.Map<HttpVerb>metody natodoItems.Map<HttpVerb>. - Odebere předponu
/todoitemsadresy URL zMap<HttpVerb>volání metod.
Otestujte koncové body a ověřte, že fungují stejně.
Použití rozhraní TypedResults API
Vracení TypedResults místo Results má několik výhod, včetně testovatelnosti a automatického vracení metadat typu odpovědi pro OpenAPI k popisu koncového bodu. Další informace naleznete v tématu TypedResults vs Results.
Metody Map<HttpVerb> mohou místo lambda volat metody obslužné rutiny trasy. Pokud chcete zobrazit příklad, aktualizujte Program.cs následujícím kódem:
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();
}
Kód Map<HttpVerb> teď volá metody místo 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);
Tyto metody vracejí objekty, které implementují IResult a jsou definovány 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();
}
Testy jednotek mohou volat tyto metody a testovat, že vracejí správný datový typ. Například pokud je metoda GetAllTodos:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Kód unit testu může ověřit, že objekt typu Ok<Todo[]> je vrácen z metody obslužné rutiny. Například:
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);
}
Prevence nadměrného zadávání
V současné době ukázková aplikace zveřejňuje celý Todo objekt. Produkční aplikace V produkčních aplikacích se podmnožina modelu často používá k omezení dat, která mohou být vstupní a vrácená. Z tohoto důvodu existuje několik důvodů a zabezpečení je hlavním důvodem. Podmnožina modelu se obvykle označuje jako objekt pro přenos dat (DTO), vstupní model nebo model zobrazení.
DTO se používá v tomto článku.
DTO lze použít k:
- Zabránit nadměrnému odesílání dat.
- Skryjte vlastnosti, které klienti nemají zobrazit.
- Vynechejte některé vlastnosti pro zmenšení velikosti datové části.
- Zplošťujte grafy objektů, které obsahují vnořené objekty. Grafy plochých objektů můžou být pro klienty pohodlnější.
Pokud chcete předvést přístup DTO, aktualizujte Todo třídu tak, aby obsahovala pole tajného kódu:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Pole tajného kódu musí být v této aplikaci skryté, ale aplikace pro správu by se mohla rozhodnout, že ho zveřejní.
Ověřte, že můžete publikovat a získat tajné pole.
Vytvořte soubor s názvem TodoItemDTO.cs s následujícím kódem:
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);
}
Program.cs Obsah souboru nahraďte následujícím kódem, který použije tento model DTO:
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>();
}
Ověřte, že můžete publikovat a získat všechna pole kromě tajného pole.
Řešení potíží s dokončeným příkladem
Pokud narazíte na problém, který nemůžete vyřešit, porovnejte kód s dokončeným projektem. Zobrazit nebo stáhnout dokončený projekt (jak stáhnout)
Další kroky
- Nakonfigurujte možnosti serializace JSON.
- Zpracování chyb a výjimek: Ve výchozím nastavení je stránka výjimky vývojáře povolená v
Developmentprostředí pro minimální aplikace API. Informace o zpracování chyb a výjimek najdete v tématu Zpracování chyb v rozhraních ASP.NET Core API. - Příklad testování minimální aplikace API najdete v této ukázce GitHubu.
- Podpora OpenAPI v minimálních rozhraních API
- Rychlý start: Publikování do Azure
- Uspořádání minimalních rozhraní API ASP.NET Core
Learn more
Stručné referenční informace k minimálním rozhraním API
Minimální rozhraní API jsou navržena tak, aby vytvářela rozhraní HTTP API s minimálními závislostmi. Jsou ideální pro mikroslužby a aplikace, které chtějí do ASP.NET Core zahrnout jenom minimální soubory, funkce a závislosti.
V tomto kurzu se naučíte základy vytváření minimálního rozhraní API s ASP.NET Core. Dalším přístupem k vytváření rozhraní API v ASP.NET Core je použití kontrolerů. Nápovědu k výběru mezi minimálními rozhraními API a rozhraními API založenými na kontroleru najdete v přehledu rozhraní API. Kurz vytvoření projektu rozhraní API na základě kontrolerů , které obsahují další funkce, najdete v tématu Vytvoření webového rozhraní API.
Overview
Tento kurz vytvoří následující rozhraní API:
| API | Description | Obsah požadavku | Tělo odpovědi |
|---|---|---|---|
GET /todoitems |
Získání všech položek úkolů | None | Pole položek úkolů |
GET /todoitems/complete |
Získejte dokončené úkoly | None | Pole položek úkolů |
GET /todoitems/{id} |
Získejte položku podle ID | None | Položka úkolu |
POST /todoitems |
Přidání nové položky | Položka úkolu | Položka úkolu |
PUT /todoitems/{id} |
Aktualizace existující položky | Položka úkolu | None |
DELETE /todoitems/{id} |
Odstranění položky | None | None |
Prerequisites
- Visual Studio 2022 s pracovním zatížením ASP.NET a vývoje webu
- Sada .NET 6 SDK
Vytvoření projektu rozhraní API
Spusťte Visual Studio 2022 a vyberte Vytvořit nový projekt.
V dialogovém okně Vytvořit nový projekt :
- Zadejte
Emptydo vyhledávacího pole Hledat šablony . - Vyberte šablonu ASP.NET Core Empty a vyberte Další.
- Zadejte
Pojmenujte projekt TodoApi a vyberte Další.
V dialogovém okně Další informace :
- Vyberte .NET 6.0
- Zrušte zaškrtnutí políčka Nepoužívat příkazy nejvyšší úrovně
- Vyberte Vytvořit
Kontrola kódu
Soubor Program.cs obsahuje následující kód:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Předchozí kód:
- Vytvoří WebApplicationBuilder a WebApplication s předkonfigurovanými výchozími nastaveními.
- Vytvoří koncový bod
/HTTP GET, který vrátíHello World!:
Spustit aplikaci
Stisknutím kombinace kláves Ctrl+F5 spusťte bez debuggeru.
Visual Studio zobrazí následující dialogové okno:
Pokud důvěřujete certifikátu SSL služby IIS Express, vyberte Ano .
Zobrazí se následující dialogové okno:
Pokud souhlasíte s tím, že se má důvěřovat vývojovému certifikátu, vyberte Ano.
Informace o tom, jak důvěřovat prohlížeči Firefox, naleznete v části k chybě certifikátu Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.
Visual Studio spustí Kestrel webový server a otevře okno prohlížeče.
Hello World! se zobrazí v prohlížeči. Soubor Program.cs obsahuje minimální, ale kompletní aplikaci.
Přidání balíčků NuGet
Pro podporu databáze a diagnostiky používané v tomto kurzu je potřeba přidat balíčky NuGet.
- V nabídce Nástroje vyberte NuGet Správce balíčků > Spravovat balíčky NuGet pro řešení.
- Vyberte kartu Procházet.
- Do vyhledávacího pole zadejte Microsoft.EntityFrameworkCore.InMemory a pak vyberte
Microsoft.EntityFrameworkCore.InMemory. - Zaškrtněte políčko Project v pravém podokně.
- V rozevíracím seznamu Verze vyberte nejnovější dostupnou verzi 7, například
6.0.28a pak vyberte Nainstalovat. - Podle předchozích pokynů přidejte balíček s nejnovější dostupnou
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCoreverzí 7.
Třídy kontextu modelu a databáze
Ve složce projektu vytvořte soubor s názvem Todo.cs s následujícím kódem:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Předchozí kód vytvoří model pro tuto aplikaci. Model je třída, která představuje data, která aplikace spravuje.
Vytvořte soubor s názvem TodoDb.cs s následujícím kódem:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
Předchozí kód definuje kontext databáze, což je hlavní třída, která koordinuje funkce Entity Framework pro datový model. Tato třída je odvozena od Microsoft.EntityFrameworkCore.DbContext třídy.
Přidání kódu rozhraní API
Obsah souboru Program.cs nahraďte následujícím kódem:
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();
Následující zvýrazněný kód přidá kontext databáze do kontejneru injektáže závislostí (DI) a povolí zobrazení výjimek souvisejících s databází:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
Kontejner DI poskytuje přístup k kontextu databáze a dalším službám.
Vytvoření uživatelského rozhraní api pro testování pomocí Swaggeru
Můžete si vybrat z mnoha dostupných testovacích nástrojů webového rozhraní API a můžete postupovat podle úvodních testovacích kroků tohoto kurzu pomocí vlastního preferovaného nástroje.
Tento kurz využívá balíček .NET NSwag.AspNetCore, který integruje nástroje Swaggeru pro generování testovacího uživatelského rozhraní, které dodržuje specifikaci OpenAPI:
- NSwag: Knihovna .NET, která integruje Swagger přímo do aplikací ASP.NET Core a poskytuje middleware a konfiguraci.
- Swagger: Sada opensourcových nástrojů, jako jsou OpenAPIGenerator a SwaggerUI, které generují stránky testování rozhraní API, které se řídí specifikací OpenAPI.
- Specifikace OpenAPI: Dokument, který popisuje možnosti rozhraní API na základě anotací XML a atributů v rámci kontrolerů a modelů.
Další informace o používání OpenAPI a NSwag s ASP.NET najdete v dokumentaci k webovému rozhraní API pro ASP.NET Core pomocí Swaggeru nebo OpenAPI.
Instalace nástrojů Swagger
Spusťte následující příkaz:
dotnet add package NSwag.AspNetCore
Předchozí příkaz přidá balíček NSwag.AspNetCore , který obsahuje nástroje pro generování dokumentů a uživatelského rozhraní Swagger.
Konfigurace middlewaru Swaggeru
V Program.cs na začátek přidejte následující
usingpříkazy:using NSwag.AspNetCore;Před tím, než je
appdefinován na řádkuvar app = builder.Build();, přidejte následující zvýrazněný kód.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();
V předchozím kódu:
builder.Services.AddEndpointsApiExplorer();: Povolí Průzkumníka rozhraní API, což je služba, která poskytuje metadata o rozhraní HTTP API. Swagger používá Průzkumník rozhraní API k vygenerování dokumentu Swagger.builder.Services.AddOpenApiDocument(config => {...});: Přidá generátor dokumentů Swagger OpenAPI do aplikačních služeb a nakonfiguruje ho tak, aby poskytoval další informace o rozhraní API, jako je jeho název a verze. Informace o poskytování robustnějších podrobností rozhraní API najdete v tématu Začínáme se službou NSwag a ASP.NET Core.Přidejte následující zvýrazněný kód na další řádek za definici
appna řádkuvar 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"; }); }Předchozí kód umožňuje middleware Swagger pro obsluhu generovaného dokumentu JSON a uživatelského rozhraní Swagger. Swagger je povolený jenom ve vývojovém prostředí. Povolení Swaggeru v produkčním prostředí by mohlo vystavit potenciálně citlivé podrobnosti o struktuře a implementaci rozhraní API.
Testování zveřejňování dat
Následující kód v Program.cs vytvoří HTTP POST koncový bod /todoitems, který přidá data do paměťové databáze:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
Spustit aplikaci. Prohlížeč zobrazí chybu 404, protože / koncový bod již neexistuje.
Koncový bod POST se použije k přidání dat do aplikace.
Když aplikace stále běží, v prohlížeči přejděte na
https://localhost:<port>/swagger, abyste zobrazili stránku testování rozhraní API vygenerovanou Swaggerem.
Na stránce pro testování rozhraní Swagger API vyberte Post /todoitems>.
Všimněte si, že pole Text požadavku obsahuje vygenerovaný ukázkový formát, který odráží parametry rozhraní API.
V textu požadavku zadejte JSON pro položku úkolu bez zadání volitelné
idpoložky:{ "name":"walk dog", "isComplete":true }Vyberte Spustit.
Swagger poskytuje podokno Odpovědi pod tlačítkem Spustit.
Poznamenejte si několik užitečných podrobností:
- cURL: Swagger poskytuje příklad příkazu cURL v syntaxi systému Unix/Linux, který se dá spustit na příkazovém řádku s libovolným prostředím Bash, které používá syntaxi systému Unix/Linux, včetně Git Bashu z Gitu pro Windows.
- Adresa URL požadavku: Zjednodušená reprezentace požadavku HTTP vytvořeného kódem JavaScript uživatelského rozhraní Swagger pro volání rozhraní API. Skutečné požadavky můžou obsahovat podrobnosti, jako jsou hlavičky a parametry dotazu a text požadavku.
- Odpověď serveru: Obsahuje text odpovědi a hlavičky. Tělo odpovědi ukazuje, že
idbyla nastavena na1. - Kód odpovědi: Vrátil se stavový kód 201
HTTP, který indikoval, že požadavek byl úspěšně zpracován a způsobil vytvoření nového prostředku.
Prozkoumání koncových bodů GET
Ukázková aplikace implementuje několik koncových bodů GET voláním MapGet:
| API | Description | Obsah požadavku | Tělo odpovědi |
|---|---|---|---|
GET /todoitems |
Získání všech položek úkolů | None | Pole položek úkolů |
GET /todoitems/complete |
Získání všech dokončených položek úkolů | None | Pole položek úkolů |
GET /todoitems/{id} |
Získejte položku podle ID | None | Položka úkolu |
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());
Testování koncových bodů GET
Otestujte aplikaci voláním koncových bodů z prohlížeče nebo Swaggeru.
V Swaggeru vyberte GET /todoitems>Vyzkoušejte to>Spustit.
Alternativně volejte GET /todoitems v prohlížeči zadáním adresy URI
http://localhost:<port>/todoitems. Napříkladhttp://localhost:5001/todoitems
Volání na GET /todoitems vytvoří odpověď podobnou následující:
[
{
"id": 1,
"name": "walk dog",
"isComplete": true
}
]
Zavolejte GET /todoitems/{id} ve Swaggeru pro získání dat pro konkrétní ID.
- Vyberte GET /todoitems>Vyzkoušet.
-
Nastavte pole ID na
1a vyberte Spustit.
Alternativně volejte GET /todoitems v prohlížeči zadáním adresy URI
https://localhost:<port>/todoitems/1. Příklad:https://localhost:5001/todoitems/1Odpověď je podobná následující:
{ "id": 1, "name": "walk dog", "isComplete": true }
Tato aplikace používá databázi v paměti. Pokud se aplikace restartuje, požadavek GET nevrací žádná data. Pokud se nevrátí žádná data, post data do aplikace a zkuste požadavek GET zopakovat.
Návratové hodnoty
ASP.NET Core automaticky serializuje objekt do formátu JSON a zapíše json do textu zprávy odpovědi. Kód odpovědi pro tento návratový typ je 200 OK, za předpokladu, že neexistují žádné neošetřené výjimky. Neošetřené výjimky se překládají do chyb 5xx.
Návratové typy mohou představovat širokou škálu stavových kódů HTTP. Může například GET /todoitems/{id} vrátit dvě různé hodnoty stavu:
- Pokud žádná položka neodpovídá požadovanému ID, vrátí metoda kód chyby stavuNotFound 404.
- V opačném případě metoda vrátí hodnotu 200 s textem odpovědi JSON. Vrácení
itemvede k odpovědi HTTP 200.
Prozkoumání koncového bodu PUT
Ukázková aplikace implementuje jeden koncový bod PUT pomocí 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();
});
Tato metoda je podobná MapPost metodě, s výjimkou použití HTTP PUT. Úspěšná odpověď vrátí hodnotu 204 (bez obsahu). Podle specifikace HTTP požadavek PUT vyžaduje, aby klient odeslal celou aktualizovanou entitu, nejen změny. Pokud chcete podporovat částečné aktualizace, použijte HTTP PATCH.
Testování koncového bodu PUT
Tato ukázka používá databázi v paměti, která se musí inicializovat při každém spuštění aplikace. Před voláním PUT musí být v databázi položka. Před voláním metody PUT zavolejte get a ujistěte se, že je v databázi položka.
Aktualizujte položku úkolu, která má Id = 1, a nastavte její název na "feed fish".
Odeslání požadavku PUT pomocí Swaggeru:
Vyberte Put /todoitems/{id}>Vyzkoušet.
Nastavte pole ID na hodnotu
1.Nastavte text požadavku na následující JSON:
{ "name": "feed fish", "isComplete": false }Vyberte Spustit.
Prozkoumání a otestování koncového bodu DELETE
Ukázková aplikace implementuje jeden koncový bod DELETE pomocí 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();
});
K odeslání požadavku DELETE použijte Swagger:
Vyberte DELETE /todoitems/{id}>Vyzkoušet.
Nastavte pole ID na
1a vyberte Spustit.Požadavek DELETE se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi . Tělo odpovědi je prázdné a stavový kód odpovědi serveru je 204.
Prevence nadměrného zadávání
V současné době ukázková aplikace zveřejňuje celý Todo objekt. Produkční aplikace V produkčních aplikacích se podmnožina modelu často používá k omezení dat, která mohou být vstupní a vrácená. Z tohoto důvodu existuje několik důvodů a zabezpečení je hlavním důvodem. Podmnožina modelu se obvykle označuje jako objekt pro přenos dat (DTO), vstupní model nebo model zobrazení.
DTO se používá v tomto článku.
DTO lze použít k:
- Zabránit nadměrnému odesílání dat.
- Skryjte vlastnosti, které klienti nemají zobrazit.
- Vynechejte některé vlastnosti pro zmenšení velikosti datové části.
- Zplošťujte grafy objektů, které obsahují vnořené objekty. Grafy plochých objektů můžou být pro klienty pohodlnější.
Pokud chcete předvést přístup DTO, aktualizujte Todo třídu tak, aby obsahovala pole tajného kódu:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Pole tajného kódu musí být v této aplikaci skryté, ale aplikace pro správu by se mohla rozhodnout, že ho zveřejní.
Ověřte, že můžete publikovat a získat tajné pole.
Vytvořte soubor s názvem TodoItemDTO.cs s následujícím kódem:
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);
}
Program.cs Obsah souboru nahraďte následujícím kódem, který použije tento model DTO:
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>();
}
Ověřte, že můžete publikovat a získat všechna pole kromě tajného pole.
Testování minimálního rozhraní API
Příklad testování minimální aplikace API najdete v této ukázce GitHubu.
Publikování do Azure
Informace o nasazení do Azure najdete v tématu Rychlý start: Nasazení webové aplikace ASP.NET.
Dodatečné zdroje
Minimální rozhraní API jsou navržena tak, aby vytvářela rozhraní HTTP API s minimálními závislostmi. Jsou ideální pro mikroslužby a aplikace, které chtějí do ASP.NET Core zahrnout jenom minimální soubory, funkce a závislosti.
V tomto kurzu se naučíte základy vytváření minimálního rozhraní API s ASP.NET Core. Dalším přístupem k vytváření rozhraní API v ASP.NET Core je použití kontrolerů. Nápovědu k výběru mezi minimálními rozhraními API a rozhraními API založenými na kontroleru najdete v přehledu rozhraní API. Kurz vytvoření projektu rozhraní API na základě kontrolerů , které obsahují další funkce, najdete v tématu Vytvoření webového rozhraní API.
Overview
Tento kurz vytvoří následující rozhraní API:
| API | Description | Obsah požadavku | Tělo odpovědi |
|---|---|---|---|
GET /todoitems |
Získání všech položek úkolů | None | Pole položek úkolů |
GET /todoitems/complete |
Získejte dokončené úkoly | None | Pole položek úkolů |
GET /todoitems/{id} |
Získejte položku podle ID | None | Položka úkolu |
POST /todoitems |
Přidání nové položky | Položka úkolu | Položka úkolu |
PUT /todoitems/{id} |
Aktualizace existující položky | Položka úkolu | None |
PATCH /todoitems/{id} |
Částečné aktualizace položky | Částečný úkol k dokončení | None |
DELETE /todoitems/{id} |
Odstranění položky | None | None |
Prerequisites
Visual Studio 2022 s pracovním zatížením ASP.NET a vývoje webu
Vytvoření projektu rozhraní API
Spusťte Visual Studio 2022 a vyberte Vytvořit nový projekt.
V dialogovém okně Vytvořit nový projekt :
- Zadejte
Emptydo vyhledávacího pole Hledat šablony . - Vyberte šablonu ASP.NET Core Empty a vyberte Další.
- Zadejte
Pojmenujte projekt TodoApi a vyberte Další.
V dialogovém okně Další informace :
- Vyberte .NET 8.0 (dlouhodobá podpora)
- Zrušte zaškrtnutí políčka Nepoužívat příkazy nejvyšší úrovně
- Vyberte Vytvořit
Kontrola kódu
Soubor Program.cs obsahuje následující kód:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Předchozí kód:
- Vytvoří WebApplicationBuilder a WebApplication s předkonfigurovanými výchozími nastaveními.
- Vytvoří koncový bod
/HTTP GET, který vrátíHello World!:
Spustit aplikaci
Stisknutím kombinace kláves Ctrl+F5 spusťte bez debuggeru.
Visual Studio zobrazí následující dialogové okno:
Pokud důvěřujete certifikátu SSL služby IIS Express, vyberte Ano .
Zobrazí se následující dialogové okno:
Pokud souhlasíte s tím, že se má důvěřovat vývojovému certifikátu, vyberte Ano.
Informace o tom, jak důvěřovat prohlížeči Firefox, naleznete v části k chybě certifikátu Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.
Visual Studio spustí Kestrel webový server a otevře okno prohlížeče.
Hello World! se zobrazí v prohlížeči. Soubor Program.cs obsahuje minimální, ale kompletní aplikaci.
Zavřete okno prohlížeče.
Přidání balíčků NuGet
Pro podporu databáze a diagnostiky používané v tomto kurzu je potřeba přidat balíčky NuGet.
- V nabídce Nástroje vyberte NuGet Správce balíčků > Spravovat balíčky NuGet pro řešení.
- Vyberte kartu Procházet.
- Do vyhledávacího pole zadejte Microsoft.EntityFrameworkCore.InMemory a pak vyberte
Microsoft.EntityFrameworkCore.InMemory. - V pravém podokně zaškrtněte políčko Projekt a pak vyberte Nainstalovat.
- Podle předchozích pokynů přidejte
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCorebalíček.
Třídy kontextu modelu a databáze
- Ve složce projektu vytvořte soubor s názvem
Todo.css následujícím kódem:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Předchozí kód vytvoří model pro tuto aplikaci. Model je třída, která představuje data, která aplikace spravuje.
- Vytvořte soubor s názvem
TodoDb.css následujícím kódem:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
Předchozí kód definuje kontext databáze, což je hlavní třída, která koordinuje funkce Entity Framework pro datový model. Tato třída je odvozena od Microsoft.EntityFrameworkCore.DbContext třídy.
Přidání kódu rozhraní API
- Obsah souboru
Program.csnahraďte následujícím kódem:
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();
Následující zvýrazněný kód přidá kontext databáze do kontejneru injektáže závislostí (DI) a povolí zobrazení výjimek souvisejících s databází:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
Kontejner DI poskytuje přístup k kontextu databáze a dalším službám.
V tomto kurzu se k otestování rozhraní API používá Průzkumník koncových bodů a soubory .http.
Testování zveřejňování dat
Následující kód v Program.cs vytvoří HTTP POST koncový bod /todoitems, který přidá data do paměťové databáze:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
Spustit aplikaci. Prohlížeč zobrazí chybu 404, protože / koncový bod již neexistuje.
Koncový bod POST se použije k přidání dat do aplikace.
Vyberte Zobrazit>Další okna>Průzkumník koncových bodů.
Klikněte pravým tlačítkem na koncový bod POST a vyberte Vygenerovat požadavek.
Ve složce projektu s názvem
TodoApi.httpse vytvoří nový soubor s podobným obsahem jako v následujícím příkladu:@TodoApi_HostAddress = https://localhost:7031 Post {{TodoApi_HostAddress}}/todoitems ###- První řádek vytvoří proměnnou, která se použije pro všechny koncové body.
- Další řádek definuje požadavek POST.
- Řádek s trojitým hashtagem (
###) je značka oddělující požadavek: co následuje za ní, je určené pro jiný požadavek.
Požadavek POST potřebuje hlavičky a text. Pokud chcete definovat tyto části požadavku, přidejte následující řádky bezprostředně za řádek požadavku POST:
Content-Type: application/json { "name":"walk dog", "isComplete":true }Předchozí kód přidá hlavičku Content-Type a text požadavku JSON. Soubor TodoApi.http by teď měl vypadat jako v následujícím příkladu, ale s číslem portu:
@TodoApi_HostAddress = https://localhost:7057 Post {{TodoApi_HostAddress}}/todoitems Content-Type: application/json { "name":"walk dog", "isComplete":true } ###Spustit aplikaci.
Vyberte odkaz Odeslat žádost, který je nad řádkem
POSTžádosti.
Požadavek POST se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi .
Prozkoumání koncových bodů GET
Ukázková aplikace implementuje několik koncových bodů GET voláním MapGet:
| API | Description | Obsah požadavku | Tělo odpovědi |
|---|---|---|---|
GET /todoitems |
Získání všech položek úkolů | None | Pole položek úkolů |
GET /todoitems/complete |
Získání všech dokončených položek úkolů | None | Pole položek úkolů |
GET /todoitems/{id} |
Získejte položku podle ID | None | Položka úkolu |
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());
Testování koncových bodů GET
Otestujte aplikaci voláním GET koncových bodů z prohlížeče nebo pomocí Průzkumníka koncových bodů. Následující kroky jsou určené pro Průzkumníka koncových bodů.
V Průzkumníku koncových bodů klikněte pravým tlačítkem na první koncový bod GET a vyberte Vygenerovat požadavek.
Do souboru
TodoApi.httpbyl přidán následující obsah:Get {{TodoApi_HostAddress}}/todoitems ###Vyberte odkaz Odeslat žádost, který je nad novým
GETřádkem žádosti.Požadavek GET se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi .
Text odpovědi je podobný následujícímu formátu JSON:
[ { "id": 1, "name": "walk dog", "isComplete": true } ]V Průzkumníku koncových bodů klikněte pravým tlačítkem na
/todoitems/{id}koncový bod GET a vyberte Vygenerovat požadavek. Do souboruTodoApi.httpbyl přidán následující obsah:GET {{TodoApi_HostAddress}}/todoitems/{id} ###Nahraďte
{id}s1.Vyberte odkaz Odeslat požadavek, který je nad novým řádkem požadavku GET.
Požadavek GET se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi .
Text odpovědi je podobný následujícímu formátu JSON:
{ "id": 1, "name": "walk dog", "isComplete": true }
Tato aplikace používá databázi v paměti. Pokud se aplikace restartuje, požadavek GET nevrací žádná data. Pokud se nevrátí žádná data, post data do aplikace a zkuste požadavek GET zopakovat.
Návratové hodnoty
ASP.NET Core automaticky serializuje objekt do formátu JSON a zapíše json do textu zprávy odpovědi. Kód odpovědi pro tento návratový typ je 200 OK, za předpokladu, že neexistují žádné neošetřené výjimky. Neošetřené výjimky se překládají do chyb 5xx.
Návratové typy mohou představovat širokou škálu stavových kódů HTTP. Může například GET /todoitems/{id} vrátit dvě různé hodnoty stavu:
- Pokud žádná položka neodpovídá požadovanému ID, vrátí metoda kód chyby stavuNotFound 404.
- V opačném případě metoda vrátí hodnotu 200 s textem odpovědi JSON. Vrácení
itemvede k odpovědi HTTP 200.
Prozkoumání koncového bodu PUT
Ukázková aplikace implementuje jeden koncový bod PUT pomocí 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();
});
Tato metoda je podobná MapPost metodě, s výjimkou použití HTTP PUT. Úspěšná odpověď vrátí hodnotu 204 (bez obsahu). Podle specifikace HTTP požadavek PUT vyžaduje, aby klient odeslal celou aktualizovanou entitu, nejen změny. Pokud chcete podporovat částečné aktualizace, použijte HTTP PATCH.
Testování koncového bodu PUT
Tato ukázka používá databázi v paměti, která se musí inicializovat při každém spuštění aplikace. Před voláním PUT musí být v databázi položka. Před voláním metody PUT zavolejte get a ujistěte se, že je v databázi položka.
Aktualizujte položku úkolu, která má Id = 1, a nastavte její název na "feed fish".
V Průzkumníku koncových bodů klikněte pravým tlačítkem myši na koncový bod PUT a vyberte Vygenerovat požadavek.
Do souboru
TodoApi.httpbyl přidán následující obsah:Put {{TodoApi_HostAddress}}/todoitems/{id} ###Na řádku požadavku PUT nahraďte
{id}s1.Přidejte následující řádky bezprostředně za řádek požadavku PUT:
Content-Type: application/json { "name": "feed fish", "isComplete": false }Předchozí kód přidá hlavičku Content-Type a text požadavku JSON.
Vyberte odkaz Odeslat žádost, který je nad novým řádkem požadavku PUT.
Požadavek PUT se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi . Tělo odpovědi je prázdné a stavový kód je 204.
Prozkoumání koncového bodu PATCH
Vytvořte soubor s názvem TodoPatchDto.cs s následujícím kódem:
public class TodoPatchDto
{
public string? Name { get; set; }
public bool? IsComplete { get; set; }
}
Třída TodoPatchDto používá vlastnosti s možnou hodnotou null (string? a bool?) k rozlišení mezi polem, které nebylo zadané v požadavku, a pole explicitně nastavené na hodnotu.
Ukázková aplikace implementuje jeden koncový bod PATCH pomocí MapPatch:
app.MapPatch("/todoitems/{id}", async (int id, TodoPatchDto inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
if (inputTodo.Name is not null) todo.Name = inputTodo.Name;
if (inputTodo.IsComplete is not null) todo.IsComplete = inputTodo.IsComplete.Value;
await db.SaveChangesAsync();
return Results.NoContent();
});
Tato metoda je podobná MapPut metodě, s výjimkou použití HTTP PATCH a aktualizuje pouze pole uvedená v požadavku. Úspěšná odpověď vrátí hodnotu 204 (bez obsahu). Podle specifikace HTTP požadavek PATCH umožňuje částečné aktualizace, což klientům umožňuje odesílat pouze pole, která je potřeba změnit.
Koncový bod PATCH používá třídu s vlastnostmi s možnou TodoPatchDto hodnotou null ke správnému zpracování částečných aktualizací. Použití nullable vlastností umožňuje koncovému bodu rozlišovat mezi polem, které nebylo zadáno (null), a polem, které je explicitně nastavené na určitou hodnotu (včetně false pro logická pole). Bez vlastností umožňujících null by se ve výchozím nastavení hodnota nepravdivého stavu nastavila na false, což by potenciálně přepsalo existující hodnotu true, pokud toto pole nebylo zahrnuto v požadavku.
Note
Operace PATCH umožňují částečné aktualizace prostředků. Pokročilejší částečné aktualizace pomocí dokumentů OPRAV JSON najdete v tématu JsonPatch ve webovém rozhraní API ASP.NET Core.
Otestujte koncový bod PATCH
Tato ukázka používá databázi v paměti, která se musí inicializovat při každém spuštění aplikace. Před voláním PATCH musí být v databázi položka. Před voláním funkce PATCH zavolejte metodu GET, abyste měli jistotu, že je v databázi položka.
Aktualizujte pouze name vlastnost to-do položky, která má Id = 1 a nastavte její název na "run errands".
V Průzkumníku koncových bodů klikněte pravým tlačítkem na koncový bod PATCH a vyberte Vygenerovat požadavek.
Do souboru
TodoApi.httpbyl přidán následující obsah:PATCH {{TodoApi_HostAddress}}/todoitems/{id} ###Na řádku požadavku PATCH nahraďte
{id}za1.Přidejte následující řádky bezprostředně za řádek žádosti PATCH:
Content-Type: application/json { "name": "run errands" }Předchozí kód přidá hlavičku Content-Type a text požadavku JSON pouze s polem, které se má aktualizovat.
Vyberte odkaz Odeslat požadavek , který je nad novým řádkem žádosti PATCH.
Požadavek PATCH se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi . Tělo odpovědi je prázdné a stavový kód je 204.
Prozkoumání a otestování koncového bodu DELETE
Ukázková aplikace implementuje jeden koncový bod DELETE pomocí 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();
});
V Průzkumníku koncových bodů klikněte pravým tlačítkem na koncový bod DELETE a vyberte Vygenerovat požadavek.
Přidá se DELETE požadavek k
TodoApi.http.Nahraďte
{id}v řádku žádosti DELETE textem1. Požadavek DELETE by měl vypadat jako v následujícím příkladu:DELETE {{TodoApi_HostAddress}}/todoitems/1 ###Vyberte odkaz Odeslat žádost pro požadavek DELETE.
Požadavek DELETE se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi . Tělo odpovědi je prázdné a stavový kód je 204.
Použití rozhraní MapGroup API
Vzorový kód aplikace opakuje předponu todoitems adresy URL pokaždé, když nastaví koncový bod. Rozhraní API často obsahují skupiny koncových bodů se společnou předponou adresy URL a metoda MapGroup je k dispozici k uspořádání takových skupin. Redukuje opakující se kód a umožňuje přizpůsobit celé skupiny koncových bodů jediným voláním metod, jako RequireAuthorization a WithMetadata.
Program.cs Obsah nahraďte následujícím kódem:
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();
Předchozí kód má následující změny:
- Přidá
var todoItems = app.MapGroup("/todoitems");k nastavení skupiny pomocí předpony adresy URL/todoitems. - Změní všechny
app.Map<HttpVerb>metody natodoItems.Map<HttpVerb>. - Odebere předponu
/todoitemsadresy URL zMap<HttpVerb>volání metod.
Otestujte koncové body a ověřte, že fungují stejně.
Použití rozhraní TypedResults API
Vracení TypedResults místo Results má několik výhod, včetně testovatelnosti a automatického vracení metadat typu odpovědi pro OpenAPI k popisu koncového bodu. Další informace naleznete v tématu TypedResults vs Results.
Metody Map<HttpVerb> mohou místo lambda volat metody obslužné rutiny trasy. Pokud chcete zobrazit příklad, aktualizujte Program.cs následujícím kódem:
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();
}
Kód Map<HttpVerb> teď volá metody místo 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);
Tyto metody vracejí objekty, které implementují IResult a jsou definovány 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();
}
Testy jednotek mohou volat tyto metody a testovat, že vracejí správný datový typ. Například pokud je metoda GetAllTodos:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Kód unit testu může ověřit, že objekt typu Ok<Todo[]> je vrácen z metody obslužné rutiny. Například:
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);
}
Prevence nadměrného zadávání
V současné době ukázková aplikace zveřejňuje celý Todo objekt. Produkční aplikace V produkčních aplikacích se podmnožina modelu často používá k omezení dat, která mohou být vstupní a vrácená. Z tohoto důvodu existuje několik důvodů a zabezpečení je hlavním důvodem. Podmnožina modelu se obvykle označuje jako objekt pro přenos dat (DTO), vstupní model nebo model zobrazení.
DTO se používá v tomto článku.
DTO lze použít k:
- Zabránit nadměrnému odesílání dat.
- Skryjte vlastnosti, které klienti nemají zobrazit.
- Vynechejte některé vlastnosti pro zmenšení velikosti datové části.
- Zplošťujte grafy objektů, které obsahují vnořené objekty. Grafy plochých objektů můžou být pro klienty pohodlnější.
Pokud chcete předvést přístup DTO, aktualizujte Todo třídu tak, aby obsahovala pole tajného kódu:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Pole tajného kódu musí být v této aplikaci skryté, ale aplikace pro správu by se mohla rozhodnout, že ho zveřejní.
Ověřte, že můžete publikovat a získat tajné pole.
Vytvořte soubor s názvem TodoItemDTO.cs s následujícím kódem:
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);
}
Program.cs Obsah souboru nahraďte následujícím kódem, který použije tento model DTO:
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();
}
Ověřte, že můžete publikovat a získat všechna pole kromě tajného pole.
Řešení potíží s dokončeným příkladem
Pokud narazíte na problém, který nemůžete vyřešit, porovnejte kód s dokončeným projektem. Zobrazit nebo stáhnout dokončený projekt (jak stáhnout)
Další kroky
- Nakonfigurujte možnosti serializace JSON.
- Zpracování chyb a výjimek: Ve výchozím nastavení je stránka výjimky vývojáře povolená v
Developmentprostředí pro minimální aplikace API. Informace o zpracování chyb a výjimek najdete v tématu Zpracování chyb v rozhraních ASP.NET Core API. - Příklad testování minimální aplikace API najdete v této ukázce GitHubu.
- Podpora OpenAPI v minimálních rozhraních API
- Rychlý start: Publikování do Azure
- Uspořádání minimalních rozhraní API ASP.NET Core
Learn more
Stručné referenční informace k minimálním rozhraním API
Minimální rozhraní API jsou navržena tak, aby vytvářela rozhraní HTTP API s minimálními závislostmi. Jsou ideální pro mikroslužby a aplikace, které chtějí do ASP.NET Core zahrnout jenom minimální soubory, funkce a závislosti.
V tomto kurzu se naučíte základy vytváření minimálního rozhraní API s ASP.NET Core. Dalším přístupem k vytváření rozhraní API v ASP.NET Core je použití kontrolerů. Nápovědu k výběru mezi minimálními rozhraními API a rozhraními API založenými na kontroleru najdete v přehledu rozhraní API. Kurz vytvoření projektu rozhraní API na základě kontrolerů , které obsahují další funkce, najdete v tématu Vytvoření webového rozhraní API.
Overview
Tento kurz vytvoří následující rozhraní API:
| API | Description | Obsah požadavku | Tělo odpovědi |
|---|---|---|---|
GET /todoitems |
Získání všech položek úkolů | None | Pole položek úkolů |
GET /todoitems/complete |
Získejte dokončené úkoly | None | Pole položek úkolů |
GET /todoitems/{id} |
Získejte položku podle ID | None | Položka úkolu |
POST /todoitems |
Přidání nové položky | Položka úkolu | Položka úkolu |
PUT /todoitems/{id} |
Aktualizace existující položky | Položka úkolu | None |
PATCH /todoitems/{id} |
Částečné aktualizace položky | Částečný úkol k dokončení | None |
DELETE /todoitems/{id} |
Odstranění položky | None | None |
Prerequisites
Visual Studio 2022 s pracovním zatížením ASP.NET a vývoje webu
Vytvoření projektu rozhraní API
Spusťte Visual Studio 2022 a vyberte Vytvořit nový projekt.
V dialogovém okně Vytvořit nový projekt :
- Zadejte
Emptydo vyhledávacího pole Hledat šablony . - Vyberte šablonu ASP.NET Core Empty a vyberte Další.
- Zadejte
Pojmenujte projekt TodoApi a vyberte Další.
V dialogovém okně Další informace :
- Vyberte .NET 9.0
- Zrušte zaškrtnutí políčka Nepoužívat příkazy nejvyšší úrovně
- Vyberte Vytvořit
Kontrola kódu
Soubor Program.cs obsahuje následující kód:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Předchozí kód:
- Vytvoří WebApplicationBuilder a WebApplication s předkonfigurovanými výchozími nastaveními.
- Vytvoří koncový bod
/HTTP GET, který vrátíHello World!.
Spustit aplikaci
Stisknutím kombinace kláves Ctrl+F5 spusťte bez debuggeru.
Visual Studio zobrazí následující dialogové okno:
Pokud důvěřujete certifikátu SSL služby IIS Express, vyberte Ano .
Zobrazí se následující dialogové okno:
Pokud souhlasíte s tím, že se má důvěřovat vývojovému certifikátu, vyberte Ano.
Informace o tom, jak důvěřovat prohlížeči Firefox, naleznete v části k chybě certifikátu Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.
Visual Studio spustí Kestrel webový server a otevře okno prohlížeče.
Hello World! se zobrazí v prohlížeči. Soubor Program.cs obsahuje minimální, ale kompletní aplikaci.
Zavřete okno prohlížeče.
Přidání balíčků NuGet
Pro podporu databáze a diagnostiky používané v tomto kurzu je potřeba přidat balíčky NuGet.
- V nabídce Nástroje vyberte NuGet Správce balíčků > Spravovat balíčky NuGet pro řešení.
- Vyberte kartu Procházet.
- Vyberte Zahrnout předběžné verze.
- Do vyhledávacího pole zadejte Microsoft.EntityFrameworkCore.InMemory a pak vyberte
Microsoft.EntityFrameworkCore.InMemory. - V pravém podokně zaškrtněte políčko Projekt a pak vyberte Nainstalovat.
- Podle předchozích pokynů přidejte
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCorebalíček.
Třídy kontextu modelu a databáze
- Ve složce projektu vytvořte soubor s názvem
Todo.css následujícím kódem:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Předchozí kód vytvoří model pro tuto aplikaci. Model je třída, která představuje data, která aplikace spravuje.
- Vytvořte soubor s názvem
TodoDb.css následujícím kódem:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
Předchozí kód definuje kontext databáze, což je hlavní třída, která koordinuje funkce Entity Framework pro datový model. Tato třída je odvozena od Microsoft.EntityFrameworkCore.DbContext třídy.
Přidání kódu rozhraní API
- Obsah souboru
Program.csnahraďte následujícím kódem:
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();
Následující zvýrazněný kód přidá kontext databáze do kontejneru injektáže závislostí (DI) a povolí zobrazení výjimek souvisejících s databází:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
Kontejner DI poskytuje přístup k kontextu databáze a dalším službám.
V tomto kurzu se k otestování rozhraní API používá Průzkumník koncových bodů a soubory .http.
Testování zveřejňování dat
Následující kód v Program.cs vytvoří HTTP POST koncový bod /todoitems, který přidá data do paměťové databáze:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
Spustit aplikaci. Prohlížeč zobrazí chybu 404, protože / koncový bod již neexistuje.
Koncový bod POST se použije k přidání dat do aplikace.
Vyberte Zobrazit>Další okna>Průzkumník koncových bodů.
Klikněte pravým tlačítkem na koncový bod POST a vyberte Vygenerovat požadavek.
Ve složce projektu s názvem
TodoApi.httpse vytvoří nový soubor s podobným obsahem jako v následujícím příkladu:@TodoApi_HostAddress = https://localhost:7031 POST {{TodoApi_HostAddress}}/todoitems ###- První řádek vytvoří proměnnou, která se použije pro všechny koncové body.
- Další řádek definuje požadavek POST.
- Řádek s trojitým hashtagem (
###) je značka oddělující požadavek: co následuje za ní, je určené pro jiný požadavek.
Požadavek POST potřebuje hlavičky a text. Pokud chcete definovat tyto části požadavku, přidejte následující řádky bezprostředně za řádek požadavku POST:
Content-Type: application/json { "name":"walk dog", "isComplete":true }Předchozí kód přidá hlavičku Content-Type a text požadavku JSON. Soubor TodoApi.http by teď měl vypadat jako v následujícím příkladu, ale s číslem portu:
@TodoApi_HostAddress = https://localhost:7057 POST {{TodoApi_HostAddress}}/todoitems Content-Type: application/json { "name":"walk dog", "isComplete":true } ###Spustit aplikaci.
Vyberte odkaz Odeslat žádost, který je nad řádkem
POSTžádosti.
Požadavek POST se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi .
Prozkoumání koncových bodů GET
Ukázková aplikace implementuje několik koncových bodů GET voláním MapGet:
| API | Description | Obsah požadavku | Tělo odpovědi |
|---|---|---|---|
GET /todoitems |
Získání všech položek úkolů | None | Pole položek úkolů |
GET /todoitems/complete |
Získání všech dokončených položek úkolů | None | Pole položek úkolů |
GET /todoitems/{id} |
Získejte položku podle ID | None | Položka úkolu |
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());
Testování koncových bodů GET
Otestujte aplikaci voláním GET koncových bodů z prohlížeče nebo pomocí Průzkumníka koncových bodů. Následující kroky jsou určené pro Průzkumníka koncových bodů.
V Průzkumníku koncových bodů klikněte pravým tlačítkem na první koncový bod GET a vyberte Vygenerovat požadavek.
Do souboru
TodoApi.httpbyl přidán následující obsah:GET {{TodoApi_HostAddress}}/todoitems ###Vyberte odkaz Odeslat žádost, který je nad novým
GETřádkem žádosti.Požadavek GET se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi .
Text odpovědi je podobný následujícímu formátu JSON:
[ { "id": 1, "name": "walk dog", "isComplete": true } ]V Průzkumníku koncových bodů klikněte pravým tlačítkem na
/todoitems/{id}koncový bod GET a vyberte Vygenerovat požadavek. Do souboruTodoApi.httpbyl přidán následující obsah:GET {{TodoApi_HostAddress}}/todoitems/{id} ###Nahraďte
{id}s1.Vyberte odkaz Odeslat požadavek, který je nad novým řádkem požadavku GET.
Požadavek GET se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi .
Text odpovědi je podobný následujícímu formátu JSON:
{ "id": 1, "name": "walk dog", "isComplete": true }
Tato aplikace používá databázi v paměti. Pokud se aplikace restartuje, požadavek GET nevrací žádná data. Pokud se nevrátí žádná data, post data do aplikace a zkuste požadavek GET zopakovat.
Návratové hodnoty
ASP.NET Core automaticky serializuje objekt do formátu JSON a zapíše json do textu zprávy odpovědi. Kód odpovědi pro tento návratový typ je 200 OK, za předpokladu, že neexistují žádné neošetřené výjimky. Neošetřené výjimky se překládají do chyb 5xx.
Návratové typy mohou představovat širokou škálu stavových kódů HTTP. Může například GET /todoitems/{id} vrátit dvě různé hodnoty stavu:
- Pokud žádná položka neodpovídá požadovanému ID, vrátí metoda kód chyby stavuNotFound 404.
- V opačném případě metoda vrátí hodnotu 200 s textem odpovědi JSON. Vrácení
itemvede k odpovědi HTTP 200.
Prozkoumání koncového bodu PUT
Ukázková aplikace implementuje jeden koncový bod PUT pomocí 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();
});
Tato metoda je podobná MapPost metodě, s výjimkou použití HTTP PUT. Úspěšná odpověď vrátí hodnotu 204 (bez obsahu). Podle specifikace HTTP požadavek PUT vyžaduje, aby klient odeslal celou aktualizovanou entitu, nejen změny. Pokud chcete podporovat částečné aktualizace, použijte HTTP PATCH.
Testování koncového bodu PUT
Tato ukázka používá databázi v paměti, která se musí inicializovat při každém spuštění aplikace. Před voláním PUT musí být v databázi položka. Před voláním metody PUT zavolejte get a ujistěte se, že je v databázi položka.
Aktualizujte položku úkolu, která má Id = 1, a nastavte její název na "feed fish".
V Průzkumníku koncových bodů klikněte pravým tlačítkem myši na koncový bod PUT a vyberte Vygenerovat požadavek.
Do souboru
TodoApi.httpbyl přidán následující obsah:PUT {{TodoApi_HostAddress}}/todoitems/{id} ###Na řádku požadavku PUT nahraďte
{id}s1.Přidejte následující řádky bezprostředně za řádek požadavku PUT:
Content-Type: application/json { "id": 1, "name": "feed fish", "isComplete": false }Předchozí kód přidá hlavičku Content-Type a text požadavku JSON.
Vyberte odkaz Odeslat žádost, který je nad novým řádkem požadavku PUT.
Požadavek PUT se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi . Tělo odpovědi je prázdné a stavový kód je 204.
Prozkoumání koncového bodu PATCH
Vytvořte soubor s názvem TodoPatchDto.cs s následujícím kódem:
public class TodoPatchDto
{
public string? Name { get; set; }
public bool? IsComplete { get; set; }
}
Třída TodoPatchDto používá vlastnosti s možnou hodnotou null (string? a bool?) k rozlišení mezi polem, které nebylo zadané v požadavku, a pole explicitně nastavené na hodnotu.
Ukázková aplikace implementuje jeden koncový bod PATCH pomocí MapPatch:
app.MapPatch("/todoitems/{id}", async (int id, TodoPatchDto inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
if (inputTodo.Name is not null) todo.Name = inputTodo.Name;
if (inputTodo.IsComplete is not null) todo.IsComplete = inputTodo.IsComplete.Value;
await db.SaveChangesAsync();
return Results.NoContent();
});
Tato metoda je podobná MapPut metodě, s výjimkou použití HTTP PATCH a aktualizuje pouze pole uvedená v požadavku. Úspěšná odpověď vrátí hodnotu 204 (bez obsahu). Podle specifikace HTTP požadavek PATCH umožňuje částečné aktualizace, což klientům umožňuje odesílat pouze pole, která je potřeba změnit.
Koncový bod PATCH používá třídu s vlastnostmi s možnou TodoPatchDto hodnotou null ke správnému zpracování částečných aktualizací. Použití nullable vlastností umožňuje koncovému bodu rozlišovat mezi polem, které nebylo zadáno (null), a polem, které je explicitně nastavené na určitou hodnotu (včetně false pro logická pole). Bez vlastností umožňujících null by se ve výchozím nastavení hodnota nepravdivého stavu nastavila na false, což by potenciálně přepsalo existující hodnotu true, pokud toto pole nebylo zahrnuto v požadavku.
Note
Operace PATCH umožňují částečné aktualizace prostředků. Pokročilejší částečné aktualizace pomocí dokumentů OPRAV JSON najdete v tématu JsonPatch ve webovém rozhraní API ASP.NET Core.
Otestujte koncový bod PATCH
Tato ukázka používá databázi v paměti, která se musí inicializovat při každém spuštění aplikace. Před voláním PATCH musí být v databázi položka. Před voláním funkce PATCH zavolejte metodu GET, abyste měli jistotu, že je v databázi položka.
Aktualizujte pouze name vlastnost to-do položky, která má Id = 1 a nastavte její název na "run errands".
V Průzkumníku koncových bodů klikněte pravým tlačítkem na koncový bod PATCH a vyberte Vygenerovat požadavek.
Do souboru
TodoApi.httpbyl přidán následující obsah:PATCH {{TodoApi_HostAddress}}/todoitems/{id} ###Na řádku požadavku PATCH nahraďte
{id}za1.Přidejte následující řádky bezprostředně za řádek žádosti PATCH:
Content-Type: application/json { "name": "run errands" }Předchozí kód přidá hlavičku Content-Type a text požadavku JSON pouze s polem, které se má aktualizovat.
Vyberte odkaz Odeslat požadavek , který je nad novým řádkem žádosti PATCH.
Požadavek PATCH se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi . Tělo odpovědi je prázdné a stavový kód je 204.
Prozkoumání a otestování koncového bodu DELETE
Ukázková aplikace implementuje jeden koncový bod DELETE pomocí 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();
});
V Průzkumníku koncových bodů klikněte pravým tlačítkem na koncový bod DELETE a vyberte Vygenerovat požadavek.
Přidá se DELETE požadavek k
TodoApi.http.Nahraďte
{id}v řádku žádosti DELETE textem1. Požadavek DELETE by měl vypadat jako v následujícím příkladu:DELETE {{TodoApi_HostAddress}}/todoitems/1 ###Vyberte odkaz Odeslat žádost pro požadavek DELETE.
Požadavek DELETE se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi . Tělo odpovědi je prázdné a stavový kód je 204.
Použití rozhraní MapGroup API
Vzorový kód aplikace opakuje předponu todoitems adresy URL pokaždé, když nastaví koncový bod. Rozhraní API často obsahují skupiny koncových bodů se společnou předponou adresy URL a metoda MapGroup je k dispozici k uspořádání takových skupin. Redukuje opakující se kód a umožňuje přizpůsobit celé skupiny koncových bodů jediným voláním metod, jako RequireAuthorization a WithMetadata.
Program.cs Obsah nahraďte následujícím kódem:
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();
Předchozí kód má následující změny:
- Přidá
var todoItems = app.MapGroup("/todoitems");k nastavení skupiny pomocí předpony adresy URL/todoitems. - Změní všechny
app.Map<HttpVerb>metody natodoItems.Map<HttpVerb>. - Odebere předponu
/todoitemsadresy URL zMap<HttpVerb>volání metod.
Otestujte koncové body a ověřte, že fungují stejně.
Použití rozhraní TypedResults API
Vracení TypedResults místo Results má několik výhod, včetně testovatelnosti a automatického vracení metadat typu odpovědi pro OpenAPI k popisu koncového bodu. Další informace naleznete v tématu TypedResults vs Results.
Metody Map<HttpVerb> mohou místo lambda volat metody obslužné rutiny trasy. Pokud chcete zobrazit příklad, aktualizujte Program.cs následujícím kódem:
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();
}
Kód Map<HttpVerb> teď volá metody místo 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);
Tyto metody vracejí objekty, které implementují IResult a jsou definovány 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();
}
Testy jednotek mohou volat tyto metody a testovat, že vracejí správný datový typ. Například pokud je metoda GetAllTodos:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Kód unit testu může ověřit, že objekt typu Ok<Todo[]> je vrácen z metody obslužné rutiny. Například:
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);
}
Prevence nadměrného zadávání
V současné době ukázková aplikace zveřejňuje celý Todo objekt. V produkčních aplikacích se podmnožina modelu často používá k omezení dat, která mohou být vstupní a vrácená. Z tohoto důvodu existuje několik důvodů a zabezpečení je hlavním důvodem. Podmnožina modelu se obvykle označuje jako objekt pro přenos dat (DTO), vstupní model nebo model zobrazení.
DTO se používá v tomto článku.
DTO lze použít k:
- Zabránit nadměrnému odesílání dat.
- Skryjte vlastnosti, které klienti nemají zobrazit.
- Vynechejte některé vlastnosti pro zmenšení velikosti datové části.
- Zplošťujte grafy objektů, které obsahují vnořené objekty. Grafy plochých objektů můžou být pro klienty pohodlnější.
Pokud chcete předvést přístup DTO, aktualizujte Todo třídu tak, aby obsahovala pole tajného kódu:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Pole tajného kódu musí být v této aplikaci skryté, ale aplikace pro správu by se mohla rozhodnout, že ho zveřejní.
Ověřte, že můžete publikovat a získat tajné pole.
Vytvořte soubor s názvem TodoItemDTO.cs s následujícím kódem:
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);
}
Program.cs Obsah souboru nahraďte následujícím kódem, který použije tento model DTO:
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();
}
Ověřte, že můžete publikovat a získat všechna pole kromě tajného pole.
Řešení potíží s dokončeným příkladem
Pokud narazíte na problém, který nemůžete vyřešit, porovnejte kód s dokončeným projektem. Zobrazit nebo stáhnout dokončený projekt (jak stáhnout)
Další kroky
- Nakonfigurujte možnosti serializace JSON.
- Zpracování chyb a výjimek: Ve výchozím nastavení je stránka výjimky vývojáře povolená v
Developmentprostředí pro minimální aplikace API. Informace o zpracování chyb a výjimek najdete v tématu Zpracování chyb v rozhraních ASP.NET Core API. - Příklad testování minimální aplikace API najdete v této ukázce GitHubu.
- Podpora OpenAPI v minimálních rozhraních API
- Rychlý start: Publikování do Azure
- Uspořádání minimalních rozhraní API ASP.NET Core
Learn more
Stručné referenční informace k minimálním rozhraním API