Sdílet prostřednictvím


Kurz: Vytvoření minimálního rozhraní API pomocí ASP.NET Core

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Upozorňující

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v tématu .NET a .NET Core Zásady podpory. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Autor: Rick Anderson a Tom Dykstra

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 pomocí 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.

Přehled

Tento kurz vytvoří následující rozhraní API:

API Popis Text požadavku Text odpovědi
GET /todoitems Získání všech položek úkolů Nic Pole položek úkolů
GET /todoitems/complete Dokončení položek úkolů Nic Pole položek úkolů
GET /todoitems/{id} Získání položky podle ID Nic 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 Nic
DELETE /todoitems/{id}     Odstranění položky Nic Nic

Požadavky

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 Empty do vyhledávacího pole Hledat šablony .
    • Vyberte šablonu ASP.NET Core Empty a vyberte Další.

    Vytvoření nového projektu v sadě Visual Studio

  • Pojmenujte projekt TodoApi a vyberte Další.

  • V dialogovém okně Další informace :

    • Vyberte .NET 9.0 (Preview)
    • Zrušení zaškrtnutí políčka Nepoužívat příkazy nejvyšší úrovně
    • Vyberte příkaz Vytvořit.

    Další informace

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:

Spustit aplikaci

Stisknutím kombinace kláves Ctrl+F5 spusťte bez ladicího programu.

Visual Studio zobrazí následující dialogové okno:

Tento projekt je nakonfigurovaný tak, aby používal PROTOKOL SSL. Abyste se vyhnuli upozorněním SSL v prohlížeči, můžete se rozhodnout důvěřovat certifikátu podepsanému svým držitelem, který služba IIS Express vygenerovala. Chcete důvěřovat certifikátu SSL služby IIS Express?

Pokud důvěřujete certifikátu SSL služby IIS Express, vyberte Ano .

Zobrazí se následující dialogové okno:

Dialogové okno upozornění zabezpečení

Pokud souhlasíte s tím, že se má důvěřovat vývojovému certifikátu, vyberte Ano.

Informace o důvěřování prohlížeči Firefox naleznete v článku o chybě certifikátu aplikace 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.EntityFrameworkCore balíček.

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.

V tomto kurzu se k otestování rozhraní API používá Průzkumník koncových bodů a soubory .http.

Testování odúčtovacích dat

Následující kód vytvoří Program.cs koncový bod /todoitems HTTP POST, který přidá data do databáze v paměti:

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 už / neexistuje koncový bod.

Koncový bod POST se použije k přidání dat do aplikace.

  • Vyberte Zobrazit>další průzkumníka koncových bodů Windows.>

  • Klikněte pravým tlačítkem na koncový bod POST a vyberte Vygenerovat požadavek.

    Místní nabídka Průzkumníka koncových bodů se zvýrazněnou položkou nabídky Generovat žádost

    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.
    • Triple hashtag (###) line is a request delimiter: what comes after it is for a different request.
  • 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.

    Okno souboru .http se zvýrazněným odkazem pro spuštění

    Požadavek POST se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi .

    Okno souboru .http s odpovědí z požadavku POST.

Prozkoumání koncových bodů GET

Ukázková aplikace implementuje několik koncových bodů GET voláním MapGet:

API Popis Text požadavku Text odpovědi
GET /todoitems Získání všech položek úkolů Nic Pole položek úkolů
GET /todoitems/complete Získání všech dokončených položek úkolů Nic Pole položek úkolů
GET /todoitems/{id} Získání položky podle ID Nic 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 se přidá TodoApi.http 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 souboru se přidá TodoApi.http následující obsah:

    GET {{TodoApi_HostAddress}}/todoitems/{id}
    
    ###
    
  • Nahraďte {id} 1.

  • 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.

Vrácené 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 stavu NotFound 404.
  • V opačném případě metoda vrátí hodnotu 200 s textem odpovědi JSON. item Vrácení výsledků v 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 nastavila její název ."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 se přidá TodoApi.http následující obsah:

    Put {{TodoApi_HostAddress}}/todoitems/{id}
    
    ###
    
  • Na řádku požadavku PUT nahraďte {id} .1

  • 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í 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.

    Do souboru se přidá TodoApi.httppožadavek DELETE .

  • Nahraďte {id} v řádku žádosti DELETE textem 1. 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ů s běžnou předponou adresy URL a MapGroup tato metoda 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 /todoitemsadresy URL .
  • Změní všechny app.Map<HttpVerb> metody na todoItems.Map<HttpVerb>.
  • Odebere předponu /todoitems adresy URL z Map<HttpVerb> volání metody.

Otestujte koncové body a ověřte, že fungují stejně.

Použití rozhraní TypedResults API

Vrácení TypedResults místo Results několika výhod, včetně testovatelnosti a automatického vrácení metadat typu odpovědi pro OpenAPI k popisu koncového bodu. Další informace naleznete v tématu TypedResults vs Výsledky.

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 vrací správný typ. Pokud je GetAllTodosmetoda například:

static async Task<IResult> GetAllTodos(TodoDb db)
{
    return TypedResults.Ok(await db.Todos.ToArrayAsync());
}

Kód testu jednotek může ověřit, že objekt typu Ok<Todo[]> je vrácen z metody obslužné rutiny. Pří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 účtová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 účtování.
  • Skryjte vlastnosti, které klienti nemají zobrazit.
  • Vynecháte některé vlastnosti pro zmenšení velikosti datové části.
  • Zploštěné 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čenou ukázkou

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

Další informace

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 pomocí 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.

Přehled

Tento kurz vytvoří následující rozhraní API:

API Popis Text požadavku Text odpovědi
GET /todoitems Získání všech položek úkolů Nic Pole položek úkolů
GET /todoitems/complete Dokončení položek úkolů Nic Pole položek úkolů
GET /todoitems/{id} Získání položky podle ID Nic 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 Nic
DELETE /todoitems/{id}     Odstranění položky Nic Nic

Požadavky

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 Empty do vyhledávacího pole Hledat šablony .
    • Vyberte šablonu ASP.NET Core Empty a vyberte Další.

    Vytvoření nového projektu v sadě Visual Studio

  • Pojmenujte projekt TodoApi a vyberte Další.

  • V dialogovém okně Další informace :

    • Vyberte .NET 7.0.
    • Zrušení zaškrtnutí políčka Nepoužívat příkazy nejvyšší úrovně
    • Vyberte příkaz Vytvořit.

    Další informace

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:

Spustit aplikaci

Stisknutím kombinace kláves Ctrl+F5 spusťte bez ladicího programu.

Visual Studio zobrazí následující dialogové okno:

Tento projekt je nakonfigurovaný tak, aby používal PROTOKOL SSL. Abyste se vyhnuli upozorněním SSL v prohlížeči, můžete se rozhodnout důvěřovat certifikátu podepsanému svým držitelem, který služba IIS Express vygenerovala. Chcete důvěřovat certifikátu SSL služby IIS Express?

Pokud důvěřujete certifikátu SSL služby IIS Express, vyberte Ano .

Zobrazí se následující dialogové okno:

Dialogové okno upozornění zabezpečení

Pokud souhlasíte s tím, že se má důvěřovat vývojovému certifikátu, vyberte Ano.

Informace o důvěřování prohlížeči Firefox naleznete v článku o chybě certifikátu aplikace 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.EntityFrameworkCore verzí 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 definováním na řádku přidejte následující zvýrazněný kód app . var app = builder.Build();

    using Microsoft.EntityFrameworkCore;
    
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
    builder.Services.AddDatabaseDeveloperPageExceptionFilter();
    
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddOpenApiDocument(config =>
    {
        config.DocumentName = "TodoAPI";
        config.Title = "TodoAPI v1";
        config.Version = "v1";
    });
    var app = builder.Build();
    

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. Průzkumník rozhraní API používá Swagger 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 po app definování na řádku. var 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í odúčtovacích dat

Následující kód vytvoří Program.cs koncový bod /todoitems HTTP POST, který přidá data do databáze v paměti:

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 už / neexistuje koncový bod.

Koncový bod POST se použije k přidání dat do aplikace.

  • Když je aplikace stále spuštěná, přejděte v prohlížeči a https://localhost:<port>/swagger zobrazte stránku testování rozhraní API vygenerovanou Swaggerem.

    Stránka testování rozhraní API vygenerovaná pomocí Swaggeru

  • Na stránce testování rozhraní API Swagger vyberte Post /todoitems>Vyzkoušet.

  • 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 Provést.

    Swagger s Post

Swagger poskytuje pod tlačítkem Spustit podokno Odpovědi.

Swagger s odpovědí Post

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. Text odpovědi ukazuje, že id byla nastavena na 1hodnotu .
  • 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 Popis Text požadavku Text odpovědi
GET /todoitems Získání všech položek úkolů Nic Pole položek úkolů
GET /todoitems/complete Získání všech dokončených položek úkolů Nic Pole položek úkolů
GET /todoitems/{id} Získání položky podle ID Nic 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šet spustit.>

  • Alternativně volání GET /todoitems z prohlížeče zadáním identifikátoru URI http://localhost:<port>/todoitems. Například http://localhost:5001/todoitems

Volání, které GET /todoitems vytvoří odpověď podobnou následující:

[
  {
    "id": 1,
    "name": "walk dog",
    "isComplete": true
  }
]
  • Volání metody GET /todoitems/{id} ve Swaggeru pro vrácení dat z konkrétního ID:

    • Vyberte GET /todoitems>Vyzkoušet.
    • Nastavte pole ID na 1 a vyberte Spustit.
  • Alternativně volání GET /todoitems z prohlížeče zadáním identifikátoru URI https://localhost:<port>/todoitems/1. Například https://localhost:5001/todoitems/1

  • Odpověď 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.

Vrácené 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 stavu NotFound 404.
  • V opačném případě metoda vrátí hodnotu 200 s textem odpovědi JSON. item Vrácení výsledků v 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 nastavila její název ."feed fish"

Odeslání požadavku PUT pomocí Swaggeru:

  • Vyberte Put /todoitems/{id}>Vyzkoušet.

  • Nastavte pole ID na 1hodnotu .

  • Nastavte text požadavku na následující JSON:

    {
      "name": "feed fish",
      "isComplete": false
    }
    
  • Vyberte Provést.

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 1 a 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ů s běžnou předponou adresy URL a MapGroup tato metoda 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 /todoitemsadresy URL .
  • Změní všechny app.Map<HttpVerb> metody na todoItems.Map<HttpVerb>.
  • Odebere předponu /todoitems adresy URL z Map<HttpVerb> volání metody.

Otestujte koncové body a ověřte, že fungují stejně.

Použití rozhraní TypedResults API

Vrácení TypedResults místo Results několika výhod, včetně testovatelnosti a automatického vrácení metadat typu odpovědi pro OpenAPI k popisu koncového bodu. Další informace naleznete v tématu TypedResults vs Výsledky.

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 vrací správný typ. Pokud je GetAllTodosmetoda například:

static async Task<IResult> GetAllTodos(TodoDb db)
{
    return TypedResults.Ok(await db.Todos.ToArrayAsync());
}

Kód testu jednotek může ověřit, že objekt typu Ok<Todo[]> je vrácen z metody obslužné rutiny. Pří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 účtová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 účtování.
  • Skryjte vlastnosti, které klienti nemají zobrazit.
  • Vynecháte některé vlastnosti pro zmenšení velikosti datové části.
  • Zploštěné 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čenou ukázkou

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

Další informace

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 pomocí 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.

Přehled

Tento kurz vytvoří následující rozhraní API:

API Popis Text požadavku Text odpovědi
GET /todoitems Získání všech položek úkolů Nic Pole položek úkolů
GET /todoitems/complete Dokončení položek úkolů Nic Pole položek úkolů
GET /todoitems/{id} Získání položky podle ID Nic 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 Nic
DELETE /todoitems/{id}     Odstranění položky Nic Nic

Požadavky

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 Empty do vyhledávacího pole Hledat šablony .
    • Vyberte šablonu ASP.NET Core Empty a vyberte Další.

    Vytvoření nového projektu v sadě Visual Studio

  • Pojmenujte projekt TodoApi a vyberte Další.

  • V dialogovém okně Další informace :

    • Vyberte .NET 6.0
    • Zrušení zaškrtnutí políčka Nepoužívat příkazy nejvyšší úrovně
    • Vyberte příkaz 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:

Spustit aplikaci

Stisknutím kombinace kláves Ctrl+F5 spusťte bez ladicího programu.

Visual Studio zobrazí následující dialogové okno:

Tento projekt je nakonfigurovaný tak, aby používal PROTOKOL SSL. Abyste se vyhnuli upozorněním SSL v prohlížeči, můžete se rozhodnout důvěřovat certifikátu podepsanému svým držitelem, který služba IIS Express vygenerovala. Chcete důvěřovat certifikátu SSL služby IIS Express?

Pokud důvěřujete certifikátu SSL služby IIS Express, vyberte Ano .

Zobrazí se následující dialogové okno:

Dialogové okno upozornění zabezpečení

Pokud souhlasíte s tím, že se má důvěřovat vývojovému certifikátu, vyberte Ano.

Informace o důvěřování prohlížeči Firefox naleznete v článku o chybě certifikátu aplikace 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.EntityFrameworkCore verzí 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í using příkazy:

    using NSwag.AspNetCore;
    
  • Před definováním na řádku přidejte následující zvýrazněný kód app . var app = builder.Build();

    using NSwag.AspNetCore;
    using Microsoft.EntityFrameworkCore;
    
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
    builder.Services.AddDatabaseDeveloperPageExceptionFilter();
    
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddOpenApiDocument(config =>
    {
        config.DocumentName = "TodoAPI";
        config.Title = "TodoAPI v1";
        config.Version = "v1";
    });
    
    var app = builder.Build();
    

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. Průzkumník rozhraní API používá Swagger 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 po app definování na řádku. var 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í odúčtovacích dat

Následující kód vytvoří Program.cs koncový bod /todoitems HTTP POST, který přidá data do databáze v paměti:

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 už / neexistuje koncový bod.

Koncový bod POST se použije k přidání dat do aplikace.

  • Když je aplikace stále spuštěná, přejděte v prohlížeči a https://localhost:<port>/swagger zobrazte stránku testování rozhraní API vygenerovanou Swaggerem.

    Stránka testování rozhraní API vygenerovaná pomocí Swaggeru

  • Na stránce testování rozhraní API Swagger vyberte Post /todoitems>Vyzkoušet.

  • 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 Provést.

    Swagger s daty Post

Swagger poskytuje pod tlačítkem Spustit podokno Odpovědi.

Swagger s podoknem Post resonse

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. Text odpovědi ukazuje, že id byla nastavena na 1hodnotu .
  • 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 Popis Text požadavku Text odpovědi
GET /todoitems Získání všech položek úkolů Nic Pole položek úkolů
GET /todoitems/complete Získání všech dokončených položek úkolů Nic Pole položek úkolů
GET /todoitems/{id} Získání položky podle ID Nic 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šet spustit.>

  • Alternativně volání GET /todoitems z prohlížeče zadáním identifikátoru URI http://localhost:<port>/todoitems. Například http://localhost:5001/todoitems

Volání, které GET /todoitems vytvoří odpověď podobnou následující:

[
  {
    "id": 1,
    "name": "walk dog",
    "isComplete": true
  }
]
  • Volání metody GET /todoitems/{id} ve Swaggeru pro vrácení dat z konkrétního ID:

    • Vyberte GET /todoitems>Vyzkoušet.
    • Nastavte pole ID na 1 a vyberte Spustit.
  • Alternativně volání GET /todoitems z prohlížeče zadáním identifikátoru URI https://localhost:<port>/todoitems/1. Příklad: https://localhost:5001/todoitems/1

  • Odpověď 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.

Vrácené 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 stavu NotFound 404.
  • V opačném případě metoda vrátí hodnotu 200 s textem odpovědi JSON. item Vrácení výsledků v 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 nastavila její název ."feed fish"

Odeslání požadavku PUT pomocí Swaggeru:

  • Vyberte Put /todoitems/{id}>Vyzkoušet.

  • Nastavte pole ID na 1hodnotu .

  • Nastavte text požadavku na následující JSON:

    {
      "name": "feed fish",
      "isComplete": false
    }
    
  • Vyberte Provést.

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 1 a 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 účtová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 účtování.
  • Skryjte vlastnosti, které klienti nemají zobrazit.
  • Vynecháte některé vlastnosti pro zmenšení velikosti datové části.
  • Zploštěné 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.

Další materiály

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 pomocí 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.

Přehled

Tento kurz vytvoří následující rozhraní API:

API Popis Text požadavku Text odpovědi
GET /todoitems Získání všech položek úkolů Nic Pole položek úkolů
GET /todoitems/complete Dokončení položek úkolů Nic Pole položek úkolů
GET /todoitems/{id} Získání položky podle ID Nic 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 Nic
DELETE /todoitems/{id}     Odstranění položky Nic Nic

Požadavky

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 Empty do vyhledávacího pole Hledat šablony .
    • Vyberte šablonu ASP.NET Core Empty a vyberte Další.

    Vytvoření nového projektu v sadě Visual Studio

  • Pojmenujte projekt TodoApi a vyberte Další.

  • V dialogovém okně Další informace :

    • Vyberte .NET 8.0 (dlouhodobá podpora)
    • Zrušení zaškrtnutí políčka Nepoužívat příkazy nejvyšší úrovně
    • Vyberte příkaz Vytvořit.

    Další informace

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:

Spustit aplikaci

Stisknutím kombinace kláves Ctrl+F5 spusťte bez ladicího programu.

Visual Studio zobrazí následující dialogové okno:

Tento projekt je nakonfigurovaný tak, aby používal PROTOKOL SSL. Abyste se vyhnuli upozorněním SSL v prohlížeči, můžete se rozhodnout důvěřovat certifikátu podepsanému svým držitelem, který služba IIS Express vygenerovala. Chcete důvěřovat certifikátu SSL služby IIS Express?

Pokud důvěřujete certifikátu SSL služby IIS Express, vyberte Ano .

Zobrazí se následující dialogové okno:

Dialogové okno upozornění zabezpečení

Pokud souhlasíte s tím, že se má důvěřovat vývojovému certifikátu, vyberte Ano.

Informace o důvěřování prohlížeči Firefox naleznete v článku o chybě certifikátu aplikace 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.EntityFrameworkCore balíček.

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.

V tomto kurzu se k otestování rozhraní API používá Průzkumník koncových bodů a soubory .http.

Testování odúčtovacích dat

Následující kód vytvoří Program.cs koncový bod /todoitems HTTP POST, který přidá data do databáze v paměti:

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 už / neexistuje koncový bod.

Koncový bod POST se použije k přidání dat do aplikace.

  • Vyberte Zobrazit>další průzkumníka koncových bodů Windows.>

  • Klikněte pravým tlačítkem na koncový bod POST a vyberte Vygenerovat požadavek.

    Místní nabídka Průzkumníka koncových bodů se zvýrazněnou položkou nabídky Generovat žádost

    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.
    • Triple hashtag (###) line is a request delimiter: what comes after it is for a different request.
  • 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.

    Okno souboru .http se zvýrazněným odkazem pro spuštění

    Požadavek POST se odešle do aplikace a odpověď se zobrazí v podokně Odpovědi .

    Okno souboru .http s odpovědí z požadavku POST.

Prozkoumání koncových bodů GET

Ukázková aplikace implementuje několik koncových bodů GET voláním MapGet:

API Popis Text požadavku Text odpovědi
GET /todoitems Získání všech položek úkolů Nic Pole položek úkolů
GET /todoitems/complete Získání všech dokončených položek úkolů Nic Pole položek úkolů
GET /todoitems/{id} Získání položky podle ID Nic 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 se přidá TodoApi.http 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 souboru se přidá TodoApi.http následující obsah:

    GET {{TodoApi_HostAddress}}/todoitems/{id}
    
    ###
    
  • Nahraďte {id} 1.

  • 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.

Vrácené 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 stavu NotFound 404.
  • V opačném případě metoda vrátí hodnotu 200 s textem odpovědi JSON. item Vrácení výsledků v 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 nastavila její název ."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 se přidá TodoApi.http následující obsah:

    Put {{TodoApi_HostAddress}}/todoitems/{id}
    
    ###
    
  • Na řádku požadavku PUT nahraďte {id} .1

  • 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í 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.

    Do souboru se přidá TodoApi.httppožadavek DELETE .

  • Nahraďte {id} v řádku žádosti DELETE textem 1. 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ů s běžnou předponou adresy URL a MapGroup tato metoda 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 /todoitemsadresy URL .
  • Změní všechny app.Map<HttpVerb> metody na todoItems.Map<HttpVerb>.
  • Odebere předponu /todoitems adresy URL z Map<HttpVerb> volání metody.

Otestujte koncové body a ověřte, že fungují stejně.

Použití rozhraní TypedResults API

Vrácení TypedResults místo Results několika výhod, včetně testovatelnosti a automatického vrácení metadat typu odpovědi pro OpenAPI k popisu koncového bodu. Další informace naleznete v tématu TypedResults vs Výsledky.

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 vrací správný typ. Pokud je GetAllTodosmetoda například:

static async Task<IResult> GetAllTodos(TodoDb db)
{
    return TypedResults.Ok(await db.Todos.ToArrayAsync());
}

Kód testu jednotek může ověřit, že objekt typu Ok<Todo[]> je vrácen z metody obslužné rutiny. Pří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 účtová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 účtování.
  • Skryjte vlastnosti, které klienti nemají zobrazit.
  • Vynecháte některé vlastnosti pro zmenšení velikosti datové části.
  • Zploštěné 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čenou ukázkou

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

Další informace

Stručné referenční informace k minimálním rozhraním API