Megosztás a következőn keresztül:


Oktatóanyag: Minimális API létrehozása a ASP.NET Core használatával

Note

Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.

Warning

A ASP.NET Core ezen verziója már nem támogatott. További információ: .NET és .NET Core támogatási szabályzat. Az aktuális kiadást e cikk .NET 9-es verziójában találja.

Készítette: Rick Anderson és Tom Dykstra

A minimális API-k úgy vannak kialakítva, hogy minimális függőségekkel rendelkező HTTP API-kat hozzanak létre. Olyan mikroszolgáltatásokhoz és alkalmazásokhoz ideálisak, amelyek csak a minimális fájlokat, funkciókat és függőségeket szeretnék belefoglalni a ASP.NET Core-ba.

Ez az oktatóanyag a minimális API-k ASP.NET Core használatával történő létrehozásának alapjait mutatja be. Az API-k ASP.NET Core-ban való létrehozásának másik módszere a vezérlők használata. Ha segítségre van szüksége a minimális API-k és a vezérlőalapú API-k közötti választáshoz, tekintse meg API-k áttekintését. A további funkciókat tartalmazó vezérlők alapuló API-projektek létrehozásáról a Webes API-létrehozása című témakörben olvashat.

Overview

Ez az oktatóanyag a következő API-t hozza létre:

API Description A kérés tartalma Válasz törzse
GET /todoitems Az összes to-do elem lekérése None teendők tömbje
GET /todoitems/complete Kapj kész to-do elemeket None teendők tömbje
GET /todoitems/{id} Elem lekérése azonosító alapján None Teendő elem
POST /todoitems Új elem hozzáadása Teendő elem Teendő elem
PUT /todoitems/{id} Meglévő elem frissítése Teendő elem None
DELETE /todoitems/{id}     Elem törlése None None

Prerequisites

API-projekt létrehozása

  • Indítsa el a Visual Studio 2022-t, és válassza Új projekt létrehozásalehetőséget.

  • Az Új projekt létrehozása párbeszédpanelen:

    • Írja be Empty a Sablonok keresése keresőmezőbe.
    • Válassza a ASP.NET Core Empty sablont, majd válassza a Következőlehetőséget.

    Visual Studio Új projekt létrehozása

  • Nevezze el a projektet TodoApi, és válassza a Továbblehetőséget.

  • A További információk párbeszédpanelen:

    • Válassza .NET 9.0
    • Törölje a jelölést Ne használjon legfelső szintű utasításokat
    • Válassza a lehetőséget, hozza létre a lehetőséget

    További információk

A kód vizsgálata

A Program.cs fájl a következő kódot tartalmazza:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

Az előző kód:

  • Létrehoz egy WebApplicationBuilder és egy előre konfigurált alapértelmezett WebApplication.
  • Létrehoz egy HTTP GET-végpontot / , amely visszaadja a függvényt Hello World!.

Az alkalmazás futtatása

Nyomja le a Ctrl+F5 billentyűkombinációt a hibakereső nélküli futtatáshoz.

A Visual Studio a következő párbeszédpanelt jeleníti meg:

Ez a projekt SSL használatára van konfigurálva. Az SSL-figyelmeztetések elkerülése érdekében a böngészőben megadhatja, hogy megbízik-e az IIS Express által létrehozott önaláírt tanúsítványban. Megbízik az IIS Express SSL-tanúsítványban?

Válassza Igen lehetőséget, ha megbízik az IIS Express SSL-tanúsítványban.

A következő párbeszédpanel jelenik meg:

Biztonsági figyelmeztetés párbeszédpanel

Válassza Igen lehetőséget, ha elfogadja, hogy megbízik a fejlesztési tanúsítványban.

A Firefox böngésző megbízhatóságáról további információt a Firefox SEC_ERROR_INADEQUATE_KEY_USAGE tanúsítványhibacímű témakörben talál.

A Visual Studio elindítja a Kestrel webkiszolgáló, és megnyit egy böngészőablakot.

Hello World! megjelenik a böngészőben. A Program.cs fájl minimális, de teljes alkalmazást tartalmaz.

Zárja be a böngészőablakot.

NuGet-csomagok hozzáadása

Az oktatóanyagban használt adatbázis és diagnosztikák támogatásához NuGet-csomagokat kell hozzáadni.

  • Az Eszközök menüben válassza a NuGet Package Manager > Megoldáshoz készült NuGet-csomagok kezeléselehetőséget.
  • Válassza a Tallózás lapot.
  • Válassza a Előzetes verzió belefoglalásalehetőséget.
  • Írja be Microsoft.EntityFrameworkCore.InMemory a keresőmezőbe, majd válassza a Microsoft.EntityFrameworkCore.InMemory.
  • Jelölje be a Project jelölőnégyzetet a jobb oldali panelen, majd válassza a Telepítéslehetőséget.
  • A Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore csomag hozzáadásához kövesse az előző utasításokat.

A modell és az adatbázis környezeti osztályai

  • A projektmappában hozzon létre egy Todo.cs nevű fájlt a következő kóddal:
public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Az előző kód létrehozza az alkalmazás modelljét. A modell egy olyan osztály, amely az alkalmazás által kezelt adatokat jelöli.

  • Hozzon létre egy TodoDb.cs nevű fájlt a következő kóddal:
using Microsoft.EntityFrameworkCore;

class TodoDb : DbContext
{
    public TodoDb(DbContextOptions<TodoDb> options)
        : base(options) { }

    public DbSet<Todo> Todos => Set<Todo>();
}

Az előző kód határozza meg az adatbázis-környezetet, amely az adatmodell Entity Framework funkcióit koordináló fő osztály. Ez az osztály a Microsoft.EntityFrameworkCore.DbContext osztályból származik.

Az API-kód hozzáadása

  • Cserélje le a Program.cs fájl tartalmát a következő kódra:
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();

Az alábbi kiemelt kód hozzáadja az adatbázis-környezetet a függőséginjektálási (DI) tárolóhoz, és lehetővé teszi az adatbázissal kapcsolatos kivételek megjelenítését:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();

A DI-tároló hozzáférést biztosít az adatbázis-környezethez és más szolgáltatásokhoz.

Ez az oktatóanyag az Endpoints Explorer-t és a .http fájlokat használja az API teszteléséhez.

Közzétételi adatok tesztelése

A Program.cs következő kódja létrehoz egy HTTP POST-végpontot /todoitems, amely adatokat ad hozzá a memóriában lévő adatbázishoz:

app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
});

Futtassa az alkalmazást. A böngésző 404-et jelenít meg, mert már nincs / végpont.

A POST végpont használatával adatokat adhat hozzá az alkalmazáshoz.

  • Válassza a Nézet>Egyéb ablakok>Endpoints Explorer.

  • Kattintson a jobb gombbal a POST végpontra, és válassza a Kérés létrehozásalehetőséget.

    Endpoints Explorer helyi menüjében kiemelve a Kérés létrehozása menüpontot.

    Egy új fájl jön létre a TodoApi.httpnevű projektmappában, amely a következő példához hasonló tartalommal rendelkezik:

    @TodoApi_HostAddress = https://localhost:7031
    
    POST {{TodoApi_HostAddress}}/todoitems
    
    ###
    
    • Az első sor létrehoz egy változót, amelyet az összes végponthoz használnak.
    • A következő sor egy POST-kérést határoz meg.
    • A tripla hashtag (###) sor egy kérések közötti elválasztó jel: ami utána következik, az egy másik kéréshez tartozik.
  • A POST-kérelemhez fejlécekre és tartalomra van szükség. A kérelem ezen részeinek meghatározásához közvetlenül a POST kérelemsor után adja hozzá a következő sorokat:

    Content-Type: application/json
    
    {
      "name":"walk dog",
      "isComplete":true
    }
    

    Az előző kód hozzáad egy Tartalomtípus fejlécet és egy JSON-kérelemtörzset. A TodoApi.http fájlnak most az alábbi példához hasonlóan kell kinéznie, de a portszámmal:

    @TodoApi_HostAddress = https://localhost:7057
    
    POST {{TodoApi_HostAddress}}/todoitems
    Content-Type: application/json
    
    {
      "name":"walk dog",
      "isComplete":true
    }
    
    ###
    
  • Futtassa az alkalmazást.

  • Válassza a Kérés küldése hivatkozást, amely a POST kérelemsor felett található.

    .http fájlablak ki van emelve a futtatási hivatkozással.

    A POST kérést a rendszer elküldi az alkalmazásnak, és a válasz megjelenik a Válasz panelen.

    .http fájl ablak a POST-kérés válaszával.

A GET-végpontok vizsgálata

A mintaalkalmazás több GET-végpontot implementál MapGetmeghívásával:

API Description A kérés tartalma Válasz törzse
GET /todoitems Az összes to-do elem lekérése None teendők tömbje
GET /todoitems/complete Az összes kész to-do elem lekérése None teendők tömbje
GET /todoitems/{id} Elem lekérése azonosító alapján None Teendő elem
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());

A GET-végpontok tesztelése

Tesztelje az alkalmazást a GET végpontok böngészőből való meghívásával vagy Endpoints Explorerhasználatával. Az alábbi lépések a Endpoints Explorer-hez tartoznak.

  • Az Endpoints Explorerterületen kattintson a jobb gombbal az első GET végpontra, és válassza a Kérés létrehozásalehetőséget.

    A következő tartalom lesz hozzáadva a TodoApi.http fájlhoz:

    GET {{TodoApi_HostAddress}}/todoitems
    
    ###
    
  • Válassza a Kérés küldése hivatkozást, amely az új GET kérelemsor felett található.

    A GET kérést a rendszer elküldi az alkalmazásnak, és a válasz megjelenik a Válasz panelen.

  • A válasz törzse a következő JSON-hoz hasonló:

    [
      {
        "id": 1,
        "name": "walk dog",
        "isComplete": true
      }
    ]
    
  • Az Endpoints Explorerterületen kattintson a jobb gombbal a /todoitems/{id}GET végpontra, és válassza a Kérés létrehozásalehetőséget. A következő tartalom lesz hozzáadva a TodoApi.http fájlhoz:

    GET {{TodoApi_HostAddress}}/todoitems/{id}
    
    ###
    
  • Cserélje le {id}1.

  • Válassza a Kérés küldése hivatkozást, amely az új GET kérelemsor felett található.

    A GET kérést a rendszer elküldi az alkalmazásnak, és a válasz megjelenik a Válasz panelen.

  • A válasz törzse a következő JSON-hoz hasonló:

    {
      "id": 1,
      "name": "walk dog",
      "isComplete": true
    }
    

Ez az alkalmazás memórián belüli adatbázist használ. Ha az alkalmazás újraindul, a GET kérés nem ad vissza adatokat. Ha nem ad vissza adatokat, POST adatokat az alkalmazásnak, és próbálkozzon újra a GET kéréssel.

Visszaadott értékek

ASP.NET Core automatikusan szerializálja az objektumot JSON formátumra, és beírja a JSON-t a válaszüzenet törzsébe. A visszatérési típus válaszkódja 200 OK, feltéve, hogy nincsenek kezeletlen kivételek. A nem kezelt kivételek 5xx-hibákká lesznek lefordítva.

A visszatérési típusok a HTTP-állapotkódok széles skáláját jelölhetik. A GET /todoitems/{id} például két különböző állapotértéket adhat vissza:

  • Ha egyetlen elem sem felel meg a kért azonosítónak, a metódus egy 404-NotFound hibakódot ad vissza.
  • Ellenkező esetben a metódus egy JSON-válasz törzsével adja vissza a 200-t. A item visszaadása HTTP 200-választ eredményez.

A PUT végpont vizsgálata

A mintaalkalmazás egyetlen PUT-végpontot valósít meg 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();
});

Ez a módszer hasonló a MapPost metódushoz, kivéve, ha HTTP PUT-t használ. A sikeres válasz a 204 (tartalom nélkül) ad vissza. A HTTP-specifikáció szerint a PUT-kéréshez az ügyfélnek a teljes frissített entitást kell elküldenie, nem csak a módosításokat. A részleges frissítések támogatásához használja HTTP PATCH.

A PUT végpont tesztelése

Ez a minta egy memórián belüli adatbázist használ, amelyet az alkalmazás minden indításakor inicializálni kell. Az adatbázisnak tartalmaznia kell egy elemet, mielőtt PUT-hívást kezdeményezne. A GET hívásával győződjön meg arról, hogy van egy elem az adatbázisban, mielőtt PUT-hívást kezdeményez.

Frissítse a to-do elemet, amely Id = 1-t tartalmaz, és állítsa be a nevét "feed fish".

  • Az Endpoints Explorer-ben kattintson a jobb gombbal a PUT végpontra, és válassza a Kérés létrehozásalehetőséget.

    A következő tartalom lesz hozzáadva a TodoApi.http fájlhoz:

    PUT {{TodoApi_HostAddress}}/todoitems/{id}
    
    ###
    
  • A PUT kérelemsorban cserélje le a {id}1.

  • Közvetlenül a PUT kérelemsor után adja hozzá a következő sorokat:

    Content-Type: application/json
    
    {
      "id": 1,
      "name": "feed fish",
      "isComplete": false
    }
    

    Az előző kód hozzáad egy Tartalomtípus fejlécet és egy JSON-kérelemtörzset.

  • Válassza a Kérés küldése hivatkozást, amely az új PUT kérelemsor felett található.

    A PUT kérést a rendszer elküldi az alkalmazásnak, és a válasz megjelenik a Válasz panelen. A válasz törzse üres, az állapotkód pedig 204.

A DELETE végpont vizsgálata és tesztelése

A mintaalkalmazás egyetlen DELETE-végpontot valósít meg 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();
});
  • Az Endpoints Explorerterületen kattintson a jobb gombbal a DELETE végpontra, és válassza a Kérés létrehozásalehetőséget.

    Hozzá van adva egy DELETE kérés a TodoApi.http-hoz.

  • A {id} helyett 1-et használjon a DELETE kérelemsorban. A DELETE kérésnek a következő példához hasonlóan kell kinéznie:

    DELETE {{TodoApi_HostAddress}}/todoitems/1
    
    ###
    
  • Válassza a Küldési kérelem hivatkozást a DELETE kéréshez.

    A DELETE kérést a rendszer elküldi az alkalmazásnak, és a válasz megjelenik a Válasz panelen. A válasz törzse üres, az állapotkód pedig 204.

A MapGroup API használata

A mintaalkalmazás kódja minden végpont beállításakor megismétli a todoitems URL-előtagot. Az API-k gyakran rendelkeznek közös URL-előtaggal rendelkező végpontcsoportokkal, és a MapGroup módszer segít az ilyen csoportok rendszerezésében. Csökkenti az ismétlődő kódot, és lehetővé teszi a végpontok teljes csoportjainak testreszabását egyetlen hívással az olyan metódusokhoz, mint a RequireAuthorization és a WithMetadata.

Cserélje le a Program.cs tartalmát a következő kódra:

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();

Az előző kód a következő módosításokat tartalmazza:

  • Hozzáad var todoItems = app.MapGroup("/todoitems"); a csoport beállításához az URL-előtag /todoitemshasználatával.
  • Módosítja az összes app.Map<HttpVerb> metódust todoItems.Map<HttpVerb>-re.
  • Eltávolítja a /todoitems URL-előtagot a Map<HttpVerb> metódushívásokból.

Tesztelje a végpontokat annak ellenőrzéséhez, hogy azonosak-e.

A TypedResults API használata

A TypedResults helyett a Results visszaadása számos előnnyel jár, beleértve a tesztelhetőséget, és automatikusan visszaadja a választípus metaadatait az OpenAPI-hoz a végpont leírásához. További információ: TypedResults vs Results.

A Map<HttpVerb> metódusok a lambdák használata helyett útvonalkezelő metódusokat is meghívhatnak. Egy példa megtekintéséhez frissítse a Program.cs a következő kóddal:

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();
}

A Map<HttpVerb> kód mostantól metódusokat hív meg a lambdas helyett:

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);

Ezek a metódusok olyan objektumokat adnak vissza, amelyek IResult implementálnak, és amelyeket a TypedResultshatároznak meg:

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();
}

Az egységtesztek meghívhatják ezeket a metódusokat, és tesztelhetik, hogy a megfelelő típust adja vissza. Ha például a módszer GetAllTodos:

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

Az egységtesztelési kód képes ellenőrizni, hogy a kezelőmetódus visszaad-e egy Ok<Todo[]> típusú objektumot. Például:

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);
}

Túlküldés megakadályozása

A mintaalkalmazás jelenleg a teljes Todo objektumot teszi elérhetővé. Éles alkalmazásokban a modell egy részhalmazát gyakran használják a bemeneti és visszaadható adatok korlátozására. Ennek több oka is van, és a biztonság a legfontosabb. A modell részhalmazát általában adatátviteli objektumnak (DTO), bemeneti modellnek vagy nézetmodellnek nevezzük. ebben a cikkben DTO- használunk.

A DTO a következőre használható:

  • A túlküldés megakadályozása.
  • Rejtse el azokat a tulajdonságokat, amelyeket az ügyfeleknek nem szabad megtekinteniük.
  • Hagyj ki néhány tulajdonságot, hogy csökkentsük a hasznos teher méretét.
  • Beágyazott objektumokat tartalmazó objektumdiagramok simítása. A laposabb objektumgráfok kényelmesebbek lehetnek az ügyfelek számára.

A DTO-megközelítés bemutatásához frissítse a Todo osztályt egy titkos mezőre:

public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
    public string? Secret { get; set; }
}

A titkos mezőt el kell rejteni az alkalmazás elől, de egy felügyeleti alkalmazás dönthet úgy, hogy közzéteszi.

Ellenőrizze, hogy közzéteheti és lekérheti-e a titkos kód mezőt.

Hozzon létre egy TodoItemDTO.cs nevű fájlt a következő kóddal:

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);
}

Cserélje le a Program.cs fájl tartalmát a következő kódra a DTO-modell használatához:

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();
}

Ellenőrizze, hogy a titkos kód mező kivételével az összes mezőt közzéteheti és lekérheti.

Hibaelhárítás a kész mintával

Ha olyan problémába ütközik, amely nem oldható meg, hasonlítsa össze a kódot a befejezett projektel. Befejezett projekt megtekintése vagy letöltése (miként tölthető le).

Következő lépések

Learn more

Tekintse meg Minimális API-k rövid útmutatója

A minimális API-k úgy vannak kialakítva, hogy minimális függőségekkel rendelkező HTTP API-kat hozzanak létre. Ideálisak olyan mikroszolgáltatásokhoz és alkalmazásokhoz, amelyek csak a minimális fájlokat, funkciókat és függőségeket szeretnék belefoglalni a ASP.NET Core-ba.

Ez az oktatóanyag a minimális API-k ASP.NET Core használatával történő létrehozásának alapjait mutatja be. Az API-k ASP.NET Core-ban való létrehozásának másik módszere a vezérlők használata. Ha segítségre van szüksége a minimális API-k és a vezérlőalapú API-k közötti választáshoz, tekintse meg API-k áttekintését. A további funkciókat tartalmazó vezérlők alapuló API-projektek létrehozásáról a Webes API-létrehozása című témakörben olvashat.

Overview

Ez az oktatóanyag a következő API-t hozza létre:

API Description A kérés tartalma Válasz törzse
GET /todoitems Az összes to-do elem lekérése None teendők tömbje
GET /todoitems/complete Kapj kész to-do elemeket None teendők tömbje
GET /todoitems/{id} Elem lekérése azonosító alapján None Teendő elem
POST /todoitems Új elem hozzáadása Teendő elem Teendő elem
PUT /todoitems/{id} Meglévő elem frissítése Teendő elem None
DELETE /todoitems/{id}     Elem törlése None None

Prerequisites

API-projekt létrehozása

  • Indítsa el a Visual Studio 2022-t, és válassza Új projekt létrehozásalehetőséget.

  • Az Új projekt létrehozása párbeszédpanelen:

    • Írja be Empty a Sablonok keresése keresőmezőbe.
    • Válassza a ASP.NET Core Empty sablont, majd válassza a Következőlehetőséget.

    Visual Studio Új projekt létrehozása

  • Nevezze el a projektet TodoApi, és válassza a Továbblehetőséget.

  • A További információk párbeszédpanelen:

    • Válassza .NET 7.0
    • Törölje a jelölést Ne használjon legfelső szintű utasításokat
    • Válassza a lehetőséget, hozza létre a lehetőséget

    További információk

A kód vizsgálata

A Program.cs fájl a következő kódot tartalmazza:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

Az előző kód:

Az alkalmazás futtatása

Nyomja le a Ctrl+F5 billentyűkombinációt a hibakereső nélküli futtatáshoz.

A Visual Studio a következő párbeszédpanelt jeleníti meg:

Ez a projekt SSL használatára van konfigurálva. Az SSL-figyelmeztetések elkerülése érdekében a böngészőben megadhatja, hogy megbízik-e az IIS Express által létrehozott önaláírt tanúsítványban. Megbízik az IIS Express SSL-tanúsítványban?

Válassza Igen lehetőséget, ha megbízik az IIS Express SSL-tanúsítványban.

A következő párbeszédpanel jelenik meg:

Biztonsági figyelmeztetés párbeszédpanel

Válassza Igen lehetőséget, ha elfogadja, hogy megbízik a fejlesztési tanúsítványban.

A Firefox böngésző megbízhatóságáról további információt a Firefox SEC_ERROR_INADEQUATE_KEY_USAGE tanúsítványhibacímű témakörben talál.

A Visual Studio elindítja a Kestrel webkiszolgáló, és megnyit egy böngészőablakot.

Hello World! megjelenik a böngészőben. A Program.cs fájl minimális, de teljes alkalmazást tartalmaz.

NuGet-csomagok hozzáadása

Az oktatóanyagban használt adatbázis és diagnosztikák támogatásához NuGet-csomagokat kell hozzáadni.

  • Az Eszközök menüben válassza a NuGet Package Manager > Megoldáshoz készült NuGet-csomagok kezeléselehetőséget.
  • Válassza a Tallózás lapot.
  • Írja be Microsoft.EntityFrameworkCore.InMemory a keresőmezőbe, majd válassza a Microsoft.EntityFrameworkCore.InMemory.
  • Jelölje be a Project jelölőnégyzetet a jobb oldali panelen.
  • A Verzió legördülő listában válassza ki a legújabb elérhető 7-es verziót, például 7.0.17, majd válassza a Telepítéslehetőséget.
  • Az előző utasításokat követve adja hozzá a Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore csomagot a legújabb, 7-es verzióval.

A modell és az adatbázis környezeti osztályai

A projektmappában hozzon létre egy Todo.cs nevű fájlt a következő kóddal:

public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Az előző kód létrehozza az alkalmazás modelljét. A modell egy olyan osztály, amely az alkalmazás által kezelt adatokat jelöli.

Hozzon létre egy TodoDb.cs nevű fájlt a következő kóddal:

using Microsoft.EntityFrameworkCore;

class TodoDb : DbContext
{
    public TodoDb(DbContextOptions<TodoDb> options)
        : base(options) { }

    public DbSet<Todo> Todos => Set<Todo>();
}

Az előző kód határozza meg az adatbázis-környezetet, amely az adatmodell Entity Framework funkcióit koordináló fő osztály. Ez az osztály a Microsoft.EntityFrameworkCore.DbContext osztályból származik.

Az API-kód hozzáadása

Cserélje le a Program.cs fájl tartalmát a következő kódra:

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();

Az alábbi kiemelt kód hozzáadja az adatbázis-környezetet a függőséginjektálási (DI) tárolóhoz, és lehetővé teszi az adatbázissal kapcsolatos kivételek megjelenítését:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();

A DI-tároló hozzáférést biztosít az adatbázis-környezethez és más szolgáltatásokhoz.

API-tesztelési felhasználói felület létrehozása a Swaggerrel

Számos elérhető webes API-tesztelési eszköz közül választhat, és az oktatóanyag bevezető API-tesztelési lépéseit saját előnyben részesített eszközével követheti.

Ez az oktatóanyag az NSwag.AspNetCore .NET-csomagot használja, amely integrálja a Swagger-eszközöket az OpenAPI-specifikációhoz tartozó tesztelési felhasználói felület létrehozásához:

  • NSwag: .NET-kódtár, amely közvetlenül integrálja a Swaggert ASP.NET Core-alkalmazásokba, köztes szoftvereket és konfigurációt biztosítva.
  • Swagger: Olyan nyílt forráskódú eszközök készlete, mint az OpenAPIGenerator és a SwaggerUI, amelyek az OpenAPI-specifikációt követő API-tesztoldalakat hoznak létre.
  • OpenAPI-specifikáció: Az API képességeit leíró dokumentum a vezérlők és modellek XML- és attribútumjegyzetei alapján.

Az OpenAPI és az NSwag ASP.NET-tel való használatával kapcsolatos további információkért tekintse meg az ASP.NET Core webes API-dokumentációját a Swagger/OpenAPI-val.

A Swagger eszközkészlet telepítése

  • Futtassa a következő parancsot:

    dotnet add package NSwag.AspNetCore
    

Az előző parancs hozzáadja az NSwag.AspNetCore csomagot, amely swagger-dokumentumok és felhasználói felület létrehozásához szükséges eszközöket tartalmaz.

Swagger köztes szoftver konfigurálása

  • Adja hozzá a következő kiemelt kódot, mielőtt a app meg van határozva a var app = builder.Build(); sorban.

    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();
    

Az előző kódban:

  • builder.Services.AddEndpointsApiExplorer();: Engedélyezi az API Explorert, amely egy szolgáltatás, amely metaadatokat biztosít a HTTP API-ról. Az API Explorert a Swagger használja a Swagger-dokumentum létrehozásához.

  • builder.Services.AddOpenApiDocument(config => {...});: Hozzáadja a Swagger OpenAPI-dokumentumgenerátort az alkalmazásszolgáltatásokhoz, és úgy konfigurálja, hogy további információt nyújtson az API-ról, például a címéről és a verziójáról. A robusztusabb API-részletekről további információt Az NSwag és ASP.NET Core használatának első lépéseit ismertető cikkben talál.

  • Adja hozzá a következő kiemelt kódot abba a sorba, amelyik a app után következik, miután a var app = builder.Build(); definiálva van.

    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";
        });
    }
    

    Az előző kód lehetővé teszi a Swagger köztes szoftver számára a létrehozott JSON-dokumentum és a Swagger felhasználói felület kiszolgálását. A Swagger csak fejlesztési környezetben engedélyezett. A Swagger éles környezetben való engedélyezése potenciálisan bizalmas részleteket tehet közzé az API szerkezetéről és implementációjáról.

Közzétételi adatok tesztelése

A Program.cs következő kódja létrehoz egy HTTP POST-végpontot /todoitems, amely adatokat ad hozzá a memóriában lévő adatbázishoz:

app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
});

Futtassa az alkalmazást. A böngésző 404-et jelenít meg, mert már nincs / végpont.

A POST végpont használatával adatokat adhat hozzá az alkalmazáshoz.

  • Ha az alkalmazás még mindig fut, a böngészőben navigáljon a https://localhost:<port>/swagger oldalra, hogy megjelenítse a Swagger által létrehozott API-tesztelő oldalt.

    Swagger által létrehozott API-tesztelési oldal

  • A Swagger API tesztelési oldalán válassza a Post /todoitems>Próbálja ki.

  • Vegye figyelembe, hogy a Kérelem törzs mező egy létrehozott példaformátumot tartalmaz, amely az API paramétereit tükrözi.

  • A kérelem törzsében adja meg a JSON-t egy to-do elemhez az opcionális idmegadása nélkül:

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • Válassza a Végrehajtás lehetőséget.

    Swagger és Post

A Swagger Válaszok panelt biztosít a Végrehajtás gomb alatt.

Swagger és Post válasz

Jegyezze fel néhány hasznos részletet:

  • cURL: A Swagger egy példa cURL-parancsot biztosít a Unix/Linux szintaxisban, amely futtatható a parancssorban bármilyen Unix/Linux szintaxist használó Bash-felülettel, beleértve a Git Basht Git for Windows.
  • Kérelem URL-címe: A Swagger UI JavaScript-kódja által az API-híváshoz küldött HTTP-kérés egyszerűsített ábrázolása. A tényleges kérések tartalmazhatnak olyan részleteket, mint a fejlécek és a lekérdezési paraméterek, valamint a kérelem törzse.
  • Kiszolgáló válasza: Tartalmazza a válasz törzsét és fejléceit. A válasz törzse azt mutatja, hogy a id1 értékre lett állítva.
  • Válaszkód: A rendszer egy 201-HTTP állapotkódot adott vissza, amely azt jelzi, hogy a kérés feldolgozása sikeresen megtörtént, és egy új erőforrás létrehozását eredményezte.

A GET-végpontok vizsgálata

A mintaalkalmazás több GET-végpontot implementál MapGetmeghívásával:

API Description A kérés tartalma Válasz törzse
GET /todoitems Az összes to-do elem lekérése None teendők tömbje
GET /todoitems/complete Az összes kész to-do elem lekérése None teendők tömbje
GET /todoitems/{id} Elem lekérése azonosító alapján None Teendő elem
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());

A GET-végpontok tesztelése

Tesztelje az alkalmazást úgy, hogy meghívja a végpontokat egy böngészőből vagy egy Swaggerből.

  • A Swaggerben válassza GET /todoitems>Próbálja ki>Hajtson végre.

  • Másik lehetőségként a GET /todoitems meghívása böngészőből az URI http://localhost:<port>/todoitemsbeírásával. Például http://localhost:5001/todoitems

A GET /todoitems hívása a következőhöz hasonló választ ad:

[
  {
    "id": 1,
    "name": "walk dog",
    "isComplete": true
  }
]
  • Hívja meg GET /todoitems/{id} a Swaggerben egy adott azonosító adatainak visszaadásához:

    • Válassza GET /todoitems>Próbálja ki.
    • Állítsa be a azonosító mezőt 1-re, és válassza a Végrehajtáslehetőséget.
  • Másik lehetőségként a GET /todoitems meghívása böngészőből az URI https://localhost:<port>/todoitems/1beírásával. Például https://localhost:5001/todoitems/1

  • A válasz a következőhöz hasonló:

    {
      "id": 1,
      "name": "walk dog",
      "isComplete": true
    }
    

Ez az alkalmazás memórián belüli adatbázist használ. Ha az alkalmazás újraindul, a GET kérés nem ad vissza adatokat. Ha nem ad vissza adatokat, POST adatokat az alkalmazásnak, és próbálkozzon újra a GET kéréssel.

Visszaadott értékek

ASP.NET Core automatikusan szerializálja az objektumot JSON formátumra, és beírja a JSON-t a válaszüzenet törzsébe. A visszatérési típus válaszkódja 200 OK, feltéve, hogy nincsenek kezeletlen kivételek. A nem kezelt kivételek 5xx-hibákká lesznek lefordítva.

A visszatérési típusok a HTTP-állapotkódok széles skáláját jelölhetik. A GET /todoitems/{id} például két különböző állapotértéket adhat vissza:

  • Ha egyetlen elem sem felel meg a kért azonosítónak, a metódus egy 404-NotFound hibakódot ad vissza.
  • Ellenkező esetben a metódus egy JSON-válasz törzsével adja vissza a 200-t. A item visszaadása HTTP 200-választ eredményez.

A PUT végpont vizsgálata

A mintaalkalmazás egyetlen PUT-végpontot valósít meg 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();
});

Ez a módszer hasonló a MapPost metódushoz, kivéve, ha HTTP PUT-t használ. A sikeres válasz a 204 (tartalom nélkül) ad vissza. A HTTP-specifikáció szerint a PUT-kéréshez az ügyfélnek a teljes frissített entitást kell elküldenie, nem csak a módosításokat. A részleges frissítések támogatásához használja HTTP PATCH.

A PUT végpont tesztelése

Ez a minta egy memórián belüli adatbázist használ, amelyet az alkalmazás minden indításakor inicializálni kell. Az adatbázisnak tartalmaznia kell egy elemet, mielőtt PUT-hívást kezdeményezne. A GET hívásával győződjön meg arról, hogy van egy elem az adatbázisban, mielőtt PUT-hívást kezdeményez.

Frissítse a to-do elemet, amely Id = 1-t tartalmaz, és állítsa be a nevét "feed fish".

PUT-kérés küldése a Swagger használatával:

  • Válassza Put /todoitems/{id}>Próbálja ki.

  • Állítsa be a azonosító mezőjét 1értékre.

  • Állítsa a kérelem törzsét a következő JSON-ra:

    {
      "name": "feed fish",
      "isComplete": false
    }
    
  • Válassza a Végrehajtás lehetőséget.

A DELETE végpont vizsgálata és tesztelése

A mintaalkalmazás egyetlen DELETE-végpontot valósít meg 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();
});

DELETE-kérés küldése a Swagger használatával:

  • Válassza DELETE /todoitems/{id}>Próbálja ki.

  • Állítsa be a azonosító mezőt 1, és válassza a Végrehajtáslehetőséget.

    A DELETE kérést elküldik az alkalmazásnak, és a válasz megjelenik a Válaszok panelen. A válasz törzse üres, és a kiszolgáló válasza állapotkódja 204.

A MapGroup API használata

A mintaalkalmazás kódja minden végpont beállításakor megismétli a todoitems URL-előtagot. Az API-k gyakran rendelkeznek közös URL-előtaggal rendelkező végpontcsoportokkal, és a MapGroup módszer segít az ilyen csoportok rendszerezésében. Csökkenti az ismétlődő kódot, és lehetővé teszi a végpontok teljes csoportjainak testreszabását egyetlen hívással az olyan metódusokhoz, mint a RequireAuthorization és a WithMetadata.

Cserélje le a Program.cs tartalmát a következő kódra:

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();

Az előző kód a következő módosításokat tartalmazza:

  • Hozzáad var todoItems = app.MapGroup("/todoitems"); a csoport beállításához az URL-előtag /todoitemshasználatával.
  • Módosítja az összes app.Map<HttpVerb> metódust todoItems.Map<HttpVerb>-re.
  • Eltávolítja a /todoitems URL-előtagot a Map<HttpVerb> metódushívásokból.

Tesztelje a végpontokat annak ellenőrzéséhez, hogy azonosak-e.

A TypedResults API használata

A TypedResults helyett a Results visszaadása számos előnnyel jár, beleértve a tesztelhetőséget, és automatikusan visszaadja a választípus metaadatait az OpenAPI-hoz a végpont leírásához. További információ: TypedResults vs Results.

A Map<HttpVerb> metódusok a lambdák használata helyett útvonalkezelő metódusokat is meghívhatnak. Egy példa megtekintéséhez frissítse a Program.cs a következő kóddal:

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();
}

A Map<HttpVerb> kód mostantól metódusokat hív meg a lambdas helyett:

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);

Ezek a metódusok olyan objektumokat adnak vissza, amelyek IResult implementálnak, és amelyeket a TypedResultshatároznak meg:

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();
}

Az egységtesztek meghívhatják ezeket a metódusokat, és tesztelhetik, hogy a megfelelő típust adja vissza. Ha például a módszer GetAllTodos:

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

Az egységtesztelési kód képes ellenőrizni, hogy a kezelőmetódus visszaad-e egy Ok<Todo[]> típusú objektumot. Például:

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);
}

Túlküldés megakadályozása

A mintaalkalmazás jelenleg a teljes Todo objektumot teszi elérhetővé. Éles alkalmazások Éles alkalmazásokban a modell egy részhalmazát gyakran használják a bemeneti és visszaadható adatok korlátozására. Ennek több oka is van, és a biztonság a legfontosabb. A modell részhalmazát általában adatátviteli objektumnak (DTO), bemeneti modellnek vagy nézetmodellnek nevezzük. ebben a cikkben DTO- használunk.

A DTO a következőre használható:

  • A túlküldés megakadályozása.
  • Rejtse el azokat a tulajdonságokat, amelyeket az ügyfeleknek nem szabad megtekinteniük.
  • Hagyj ki néhány tulajdonságot, hogy csökkentsük a hasznos teher méretét.
  • Beágyazott objektumokat tartalmazó objektumdiagramok simítása. A laposabb objektumgráfok kényelmesebbek lehetnek az ügyfelek számára.

A DTO-megközelítés bemutatásához frissítse a Todo osztályt egy titkos mezőre:

public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
    public string? Secret { get; set; }
}

A titkos mezőt el kell rejteni az alkalmazás elől, de egy felügyeleti alkalmazás dönthet úgy, hogy közzéteszi.

Ellenőrizze, hogy közzéteheti és lekérheti-e a titkos kód mezőt.

Hozzon létre egy TodoItemDTO.cs nevű fájlt a következő kóddal:

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);
}

Cserélje le a Program.cs fájl tartalmát a következő kódra a DTO-modell használatához:

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>();
}

Ellenőrizze, hogy a titkos kód mező kivételével az összes mezőt közzéteheti és lekérheti.

Hibaelhárítás a kész mintával

Ha olyan problémába ütközik, amely nem oldható meg, hasonlítsa össze a kódot a befejezett projektel. Befejezett projekt megtekintése vagy letöltése (miként tölthető le).

Következő lépések

Learn more

Tekintse meg Minimális API-k rövid útmutatója

A minimális API-k úgy vannak kialakítva, hogy minimális függőségekkel rendelkező HTTP API-kat hozzanak létre. Ideálisak olyan mikroszolgáltatásokhoz és alkalmazásokhoz, amelyek csak a minimális fájlokat, funkciókat és függőségeket szeretnék belefoglalni a ASP.NET Core-ba.

Ez az oktatóanyag a minimális API-k ASP.NET Core használatával történő létrehozásának alapjait mutatja be. Az API-k ASP.NET Core-ban való létrehozásának másik módszere a vezérlők használata. Ha segítségre van szüksége a minimális API-k és a vezérlőalapú API-k közötti választáshoz, tekintse meg API-k áttekintését. A további funkciókat tartalmazó vezérlők alapuló API-projektek létrehozásáról a Webes API-létrehozása című témakörben olvashat.

Overview

Ez az oktatóanyag a következő API-t hozza létre:

API Description A kérés tartalma Válasz törzse
GET /todoitems Az összes to-do elem lekérése None teendők tömbje
GET /todoitems/complete Kapj kész to-do elemeket None teendők tömbje
GET /todoitems/{id} Elem lekérése azonosító alapján None Teendő elem
POST /todoitems Új elem hozzáadása Teendő elem Teendő elem
PUT /todoitems/{id} Meglévő elem frissítése Teendő elem None
DELETE /todoitems/{id}     Elem törlése None None

Prerequisites

API-projekt létrehozása

  • Indítsa el a Visual Studio 2022-t, és válassza Új projekt létrehozásalehetőséget.

  • Az Új projekt létrehozása párbeszédpanelen:

    • Írja be Empty a Sablonok keresése keresőmezőbe.
    • Válassza a ASP.NET Core Empty sablont, majd válassza a Következőlehetőséget.

    Visual Studio Új projekt létrehozása

  • Nevezze el a projektet TodoApi, és válassza a Továbblehetőséget.

  • A További információk párbeszédpanelen:

    • Válassza .NET 6.0
    • Törölje a jelölést Ne használjon legfelső szintű utasításokat
    • Válassza a lehetőséget, hozza létre a lehetőséget

A kód vizsgálata

A Program.cs fájl a következő kódot tartalmazza:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

Az előző kód:

Az alkalmazás futtatása

Nyomja le a Ctrl+F5 billentyűkombinációt a hibakereső nélküli futtatáshoz.

A Visual Studio a következő párbeszédpanelt jeleníti meg:

Ez a projekt SSL használatára van konfigurálva. Az SSL-figyelmeztetések elkerülése érdekében a böngészőben megadhatja, hogy megbízik-e az IIS Express által létrehozott önaláírt tanúsítványban. Megbízik az IIS Express SSL-tanúsítványban?

Válassza Igen lehetőséget, ha megbízik az IIS Express SSL-tanúsítványban.

A következő párbeszédpanel jelenik meg:

Biztonsági figyelmeztetés párbeszédpanel

Válassza Igen lehetőséget, ha elfogadja, hogy megbízik a fejlesztési tanúsítványban.

A Firefox böngésző megbízhatóságáról további információt a Firefox SEC_ERROR_INADEQUATE_KEY_USAGE tanúsítványhibacímű témakörben talál.

A Visual Studio elindítja a Kestrel webkiszolgáló, és megnyit egy böngészőablakot.

Hello World! megjelenik a böngészőben. A Program.cs fájl minimális, de teljes alkalmazást tartalmaz.

NuGet-csomagok hozzáadása

Az oktatóanyagban használt adatbázis és diagnosztikák támogatásához NuGet-csomagokat kell hozzáadni.

  • Az Eszközök menüben válassza a NuGet Package Manager > Megoldáshoz készült NuGet-csomagok kezeléselehetőséget.
  • Válassza a Tallózás lapot.
  • Írja be Microsoft.EntityFrameworkCore.InMemory a keresőmezőbe, majd válassza a Microsoft.EntityFrameworkCore.InMemory.
  • Jelölje be a Project jelölőnégyzetet a jobb oldali panelen.
  • A Verzió legördülő listában válassza ki a legújabb elérhető 7-es verziót, például 6.0.28, majd válassza a Telepítéslehetőséget.
  • Az előző utasításokat követve adja hozzá a Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore csomagot a legújabb, 7-es verzióval.

A modell és az adatbázis környezeti osztályai

A projektmappában hozzon létre egy Todo.cs nevű fájlt a következő kóddal:

public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Az előző kód létrehozza az alkalmazás modelljét. A modell egy olyan osztály, amely az alkalmazás által kezelt adatokat jelöli.

Hozzon létre egy TodoDb.cs nevű fájlt a következő kóddal:

using Microsoft.EntityFrameworkCore;

class TodoDb : DbContext
{
    public TodoDb(DbContextOptions<TodoDb> options)
        : base(options) { }

    public DbSet<Todo> Todos => Set<Todo>();
}

Az előző kód határozza meg az adatbázis-környezetet, amely az adatmodell Entity Framework funkcióit koordináló fő osztály. Ez az osztály a Microsoft.EntityFrameworkCore.DbContext osztályból származik.

Az API-kód hozzáadása

Cserélje le a Program.cs fájl tartalmát a következő kódra:

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();

Az alábbi kiemelt kód hozzáadja az adatbázis-környezetet a függőséginjektálási (DI) tárolóhoz, és lehetővé teszi az adatbázissal kapcsolatos kivételek megjelenítését:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();

A DI-tároló hozzáférést biztosít az adatbázis-környezethez és más szolgáltatásokhoz.

API-tesztelési felhasználói felület létrehozása a Swaggerrel

Számos elérhető webes API-tesztelési eszköz közül választhat, és az oktatóanyag bevezető API-tesztelési lépéseit saját előnyben részesített eszközével követheti.

Ez az oktatóanyag az NSwag.AspNetCore .NET-csomagot használja, amely integrálja a Swagger-eszközöket az OpenAPI-specifikációhoz tartozó tesztelési felhasználói felület létrehozásához:

  • NSwag: .NET-kódtár, amely közvetlenül integrálja a Swaggert ASP.NET Core-alkalmazásokba, köztes szoftvereket és konfigurációt biztosítva.
  • Swagger: Olyan nyílt forráskódú eszközök készlete, mint az OpenAPIGenerator és a SwaggerUI, amelyek az OpenAPI-specifikációt követő API-tesztoldalakat hoznak létre.
  • OpenAPI-specifikáció: Az API képességeit leíró dokumentum a vezérlők és modellek XML- és attribútumjegyzetei alapján.

Az OpenAPI és az NSwag ASP.NET-tel való használatával kapcsolatos további információkért tekintse meg az ASP.NET Core webes API-dokumentációját a Swagger/OpenAPI-val.

A Swagger eszközkészlet telepítése

  • Futtassa a következő parancsot:

    dotnet add package NSwag.AspNetCore
    

Az előző parancs hozzáadja az NSwag.AspNetCore csomagot, amely swagger-dokumentumok és felhasználói felület létrehozásához szükséges eszközöket tartalmaz.

Swagger köztes szoftver konfigurálása

  • A Program.cs fájlban adja hozzá a következő using utasításokat az elején:

    using NSwag.AspNetCore;
    
  • Adja hozzá a következő kiemelt kódot, mielőtt a app meg van határozva a var app = builder.Build(); sorban.

    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();
    

Az előző kódban:

  • builder.Services.AddEndpointsApiExplorer();: Engedélyezi az API Explorert, amely egy szolgáltatás, amely metaadatokat biztosít a HTTP API-ról. Az API Explorert a Swagger használja a Swagger-dokumentum létrehozásához.

  • builder.Services.AddOpenApiDocument(config => {...});: Hozzáadja a Swagger OpenAPI-dokumentumgenerátort az alkalmazásszolgáltatásokhoz, és úgy konfigurálja, hogy további információt nyújtson az API-ról, például a címéről és a verziójáról. A robusztusabb API-részletekről további információt Az NSwag és ASP.NET Core használatának első lépéseit ismertető cikkben talál.

  • Adja hozzá a következő kiemelt kódot abba a sorba, amelyik a app után következik, miután a var app = builder.Build(); definiálva van.

    
    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";
        });
    }
    
    

    Az előző kód lehetővé teszi a Swagger köztes szoftver számára a létrehozott JSON-dokumentum és a Swagger felhasználói felület kiszolgálását. A Swagger csak fejlesztési környezetben engedélyezett. A Swagger éles környezetben való engedélyezése potenciálisan bizalmas részleteket tehet közzé az API szerkezetéről és implementációjáról.

Közzétételi adatok tesztelése

A Program.cs következő kódja létrehoz egy HTTP POST-végpontot /todoitems, amely adatokat ad hozzá a memóriában lévő adatbázishoz:

app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
});

Futtassa az alkalmazást. A böngésző 404-et jelenít meg, mert már nincs / végpont.

A POST végpont használatával adatokat adhat hozzá az alkalmazáshoz.

  • Ha az alkalmazás még mindig fut, a böngészőben navigáljon a https://localhost:<port>/swagger oldalra, hogy megjelenítse a Swagger által létrehozott API-tesztelő oldalt.

    Swagger által létrehozott API-tesztelési oldal

  • A Swagger API tesztelési oldalán válassza a Post /todoitems>Próbálja ki.

  • Vegye figyelembe, hogy a Kérelem törzs mező egy létrehozott példaformátumot tartalmaz, amely az API paramétereit tükrözi.

  • A kérelem törzsében adja meg a JSON-t egy to-do elemhez az opcionális idmegadása nélkül:

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • Válassza a Végrehajtás lehetőséget.

    Swagger posta adat

A Swagger Válaszok panelt biztosít a Végrehajtás gomb alatt.

Swagger és Post resonse panel

Jegyezze fel néhány hasznos részletet:

  • cURL: A Swagger egy példa cURL-parancsot biztosít a Unix/Linux szintaxisban, amely futtatható a parancssorban bármilyen Unix/Linux szintaxist használó Bash-felülettel, beleértve a Git Basht Git for Windows.
  • Kérelem URL-címe: A Swagger UI JavaScript-kódja által az API-híváshoz küldött HTTP-kérés egyszerűsített ábrázolása. A tényleges kérések tartalmazhatnak olyan részleteket, mint a fejlécek és a lekérdezési paraméterek, valamint a kérelem törzse.
  • Kiszolgáló válasza: Tartalmazza a válasz törzsét és fejléceit. A válasz törzse azt mutatja, hogy a id1 értékre lett állítva.
  • Válaszkód: A rendszer egy 201-HTTP állapotkódot adott vissza, amely azt jelzi, hogy a kérés feldolgozása sikeresen megtörtént, és egy új erőforrás létrehozását eredményezte.

A GET-végpontok vizsgálata

A mintaalkalmazás több GET-végpontot implementál MapGetmeghívásával:

API Description A kérés tartalma Válasz törzse
GET /todoitems Az összes to-do elem lekérése None teendők tömbje
GET /todoitems/complete Az összes kész to-do elem lekérése None teendők tömbje
GET /todoitems/{id} Elem lekérése azonosító alapján None Teendő elem
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());

A GET-végpontok tesztelése

Tesztelje az alkalmazást úgy, hogy meghívja a végpontokat egy böngészőből vagy egy Swaggerből.

  • A Swaggerben válassza GET /todoitems>Próbálja ki>Hajtson végre.

  • Másik lehetőségként a GET /todoitems meghívása böngészőből az URI http://localhost:<port>/todoitemsbeírásával. Például http://localhost:5001/todoitems

A GET /todoitems hívása a következőhöz hasonló választ ad:

[
  {
    "id": 1,
    "name": "walk dog",
    "isComplete": true
  }
]
  • Hívja meg GET /todoitems/{id} a Swaggerben egy adott azonosító adatainak visszaadásához:

    • Válassza GET /todoitems>Próbálja ki.
    • Állítsa be a azonosító mezőt 1-re, és válassza a Végrehajtáslehetőséget.
  • Másik lehetőségként a GET /todoitems meghívása böngészőből az URI https://localhost:<port>/todoitems/1beírásával. Például: https://localhost:5001/todoitems/1

  • A válasz a következőhöz hasonló:

    {
      "id": 1,
      "name": "walk dog",
      "isComplete": true
    }
    

Ez az alkalmazás memórián belüli adatbázist használ. Ha az alkalmazás újraindul, a GET kérés nem ad vissza adatokat. Ha nem ad vissza adatokat, POST adatokat az alkalmazásnak, és próbálkozzon újra a GET kéréssel.

Visszaadott értékek

ASP.NET Core automatikusan szerializálja az objektumot JSON formátumra, és beírja a JSON-t a válaszüzenet törzsébe. A visszatérési típus válaszkódja 200 OK, feltéve, hogy nincsenek kezeletlen kivételek. A nem kezelt kivételek 5xx-hibákká lesznek lefordítva.

A visszatérési típusok a HTTP-állapotkódok széles skáláját jelölhetik. A GET /todoitems/{id} például két különböző állapotértéket adhat vissza:

  • Ha egyetlen elem sem felel meg a kért azonosítónak, a metódus egy 404-NotFound hibakódot ad vissza.
  • Ellenkező esetben a metódus egy JSON-válasz törzsével adja vissza a 200-t. A item visszaadása HTTP 200-választ eredményez.

A PUT végpont vizsgálata

A mintaalkalmazás egyetlen PUT-végpontot valósít meg 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();
});

Ez a módszer hasonló a MapPost metódushoz, kivéve, ha HTTP PUT-t használ. A sikeres válasz a 204 (tartalom nélkül) ad vissza. A HTTP-specifikáció szerint a PUT-kéréshez az ügyfélnek a teljes frissített entitást kell elküldenie, nem csak a módosításokat. A részleges frissítések támogatásához használja HTTP PATCH.

A PUT végpont tesztelése

Ez a minta egy memórián belüli adatbázist használ, amelyet az alkalmazás minden indításakor inicializálni kell. Az adatbázisnak tartalmaznia kell egy elemet, mielőtt PUT-hívást kezdeményezne. A GET hívásával győződjön meg arról, hogy van egy elem az adatbázisban, mielőtt PUT-hívást kezdeményez.

Frissítse a to-do elemet, amely Id = 1-t tartalmaz, és állítsa be a nevét "feed fish".

PUT-kérés küldése a Swagger használatával:

  • Válassza Put /todoitems/{id}>Próbálja ki.

  • Állítsa be a azonosító mezőjét 1értékre.

  • Állítsa a kérelem törzsét a következő JSON-ra:

    {
      "name": "feed fish",
      "isComplete": false
    }
    
  • Válassza a Végrehajtás lehetőséget.

A DELETE végpont vizsgálata és tesztelése

A mintaalkalmazás egyetlen DELETE-végpontot valósít meg 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();
});

DELETE-kérés küldése a Swagger használatával:

  • Válassza DELETE /todoitems/{id}>Próbálja ki.

  • Állítsa be a azonosító mezőt 1, és válassza a Végrehajtáslehetőséget.

    A DELETE kérést elküldik az alkalmazásnak, és a válasz megjelenik a Válaszok panelen. A válasz törzse üres, és a kiszolgáló válasza állapotkódja 204.

Túlküldés megakadályozása

A mintaalkalmazás jelenleg a teljes Todo objektumot teszi elérhetővé. Éles alkalmazások Éles alkalmazásokban a modell egy részhalmazát gyakran használják a bemeneti és visszaadható adatok korlátozására. Ennek több oka is van, és a biztonság a legfontosabb. A modell részhalmazát általában adatátviteli objektumnak (DTO), bemeneti modellnek vagy nézetmodellnek nevezzük. ebben a cikkben DTO- használunk.

A DTO a következőre használható:

  • A túlküldés megakadályozása.
  • Rejtse el azokat a tulajdonságokat, amelyeket az ügyfeleknek nem szabad megtekinteniük.
  • Hagyj ki néhány tulajdonságot, hogy csökkentsük a hasznos teher méretét.
  • Beágyazott objektumokat tartalmazó objektumdiagramok simítása. A laposabb objektumgráfok kényelmesebbek lehetnek az ügyfelek számára.

A DTO-megközelítés bemutatásához frissítse a Todo osztályt egy titkos mezőre:

public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
    public string? Secret { get; set; }
}

A titkos mezőt el kell rejteni az alkalmazás elől, de egy felügyeleti alkalmazás dönthet úgy, hogy közzéteszi.

Ellenőrizze, hogy közzéteheti és lekérheti-e a titkos kód mezőt.

Hozzon létre egy TodoItemDTO.cs nevű fájlt a következő kóddal:

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);
}

Cserélje le a Program.cs fájl tartalmát a következő kódra a DTO-modell használatához:

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>();
}

Ellenőrizze, hogy a titkos kód mező kivételével az összes mezőt közzéteheti és lekérheti.

Minimális API tesztelése

Egy minimális API-alkalmazás tesztelésére példa: ez a GitHub-minta.

Közzététel az Azure-ban

Az Azure-ban való üzembe helyezésről további információért lásd: Gyorsútmutató: ASP.NET webalkalmazás üzembe helyezése.

További erőforrások

A minimális API-k úgy vannak kialakítva, hogy minimális függőségekkel rendelkező HTTP API-kat hozzanak létre. Olyan mikroszolgáltatásokhoz és alkalmazásokhoz ideálisak, amelyek csak a minimális fájlokat, funkciókat és függőségeket szeretnék belefoglalni a ASP.NET Core-ba.

Ez az oktatóanyag a minimális API-k ASP.NET Core használatával történő létrehozásának alapjait mutatja be. Az API-k ASP.NET Core-ban való létrehozásának másik módszere a vezérlők használata. Ha segítségre van szüksége a minimális API-k és a vezérlőalapú API-k közötti választáshoz, tekintse meg API-k áttekintését. A további funkciókat tartalmazó vezérlők alapuló API-projektek létrehozásáról a Webes API-létrehozása című témakörben olvashat.

Overview

Ez az oktatóanyag a következő API-t hozza létre:

API Description A kérés tartalma Válasz törzse
GET /todoitems Az összes to-do elem lekérése None teendők tömbje
GET /todoitems/complete Kapj kész to-do elemeket None teendők tömbje
GET /todoitems/{id} Elem lekérése azonosító alapján None Teendő elem
POST /todoitems Új elem hozzáadása Teendő elem Teendő elem
PUT /todoitems/{id} Meglévő elem frissítése Teendő elem None
DELETE /todoitems/{id}     Elem törlése None None

Prerequisites

API-projekt létrehozása

  • Indítsa el a Visual Studio 2022-t, és válassza Új projekt létrehozásalehetőséget.

  • Az Új projekt létrehozása párbeszédpanelen:

    • Írja be Empty a Sablonok keresése keresőmezőbe.
    • Válassza a ASP.NET Core Empty sablont, majd válassza a Következőlehetőséget.

    Visual Studio Új projekt létrehozása

  • Nevezze el a projektet TodoApi, és válassza a Továbblehetőséget.

  • A További információk párbeszédpanelen:

    • Válassza .NET 8.0 (hosszú távú támogatás)
    • Törölje a jelölést Ne használjon legfelső szintű utasításokat
    • Válassza a lehetőséget, hozza létre a lehetőséget

    További információk

A kód vizsgálata

A Program.cs fájl a következő kódot tartalmazza:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

Az előző kód:

Az alkalmazás futtatása

Nyomja le a Ctrl+F5 billentyűkombinációt a hibakereső nélküli futtatáshoz.

A Visual Studio a következő párbeszédpanelt jeleníti meg:

Ez a projekt SSL használatára van konfigurálva. Az SSL-figyelmeztetések elkerülése érdekében a böngészőben megadhatja, hogy megbízik-e az IIS Express által létrehozott önaláírt tanúsítványban. Megbízik az IIS Express SSL-tanúsítványban?

Válassza Igen lehetőséget, ha megbízik az IIS Express SSL-tanúsítványban.

A következő párbeszédpanel jelenik meg:

Biztonsági figyelmeztetés párbeszédpanel

Válassza Igen lehetőséget, ha elfogadja, hogy megbízik a fejlesztési tanúsítványban.

A Firefox böngésző megbízhatóságáról további információt a Firefox SEC_ERROR_INADEQUATE_KEY_USAGE tanúsítványhibacímű témakörben talál.

A Visual Studio elindítja a Kestrel webkiszolgáló, és megnyit egy böngészőablakot.

Hello World! megjelenik a böngészőben. A Program.cs fájl minimális, de teljes alkalmazást tartalmaz.

Zárja be a böngészőablakot.

NuGet-csomagok hozzáadása

Az oktatóanyagban használt adatbázis és diagnosztikák támogatásához NuGet-csomagokat kell hozzáadni.

  • Az Eszközök menüben válassza a NuGet Package Manager > Megoldáshoz készült NuGet-csomagok kezeléselehetőséget.
  • Válassza a Tallózás lapot.
  • Írja be Microsoft.EntityFrameworkCore.InMemory a keresőmezőbe, majd válassza a Microsoft.EntityFrameworkCore.InMemory.
  • Jelölje be a Project jelölőnégyzetet a jobb oldali panelen, majd válassza a Telepítéslehetőséget.
  • A Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore csomag hozzáadásához kövesse az előző utasításokat.

A modell és az adatbázis környezeti osztályai

  • A projektmappában hozzon létre egy Todo.cs nevű fájlt a következő kóddal:
public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Az előző kód létrehozza az alkalmazás modelljét. A modell egy olyan osztály, amely az alkalmazás által kezelt adatokat jelöli.

  • Hozzon létre egy TodoDb.cs nevű fájlt a következő kóddal:
using Microsoft.EntityFrameworkCore;

class TodoDb : DbContext
{
    public TodoDb(DbContextOptions<TodoDb> options)
        : base(options) { }

    public DbSet<Todo> Todos => Set<Todo>();
}

Az előző kód határozza meg az adatbázis-környezetet, amely az adatmodell Entity Framework funkcióit koordináló fő osztály. Ez az osztály a Microsoft.EntityFrameworkCore.DbContext osztályból származik.

Az API-kód hozzáadása

  • Cserélje le a Program.cs fájl tartalmát a következő kódra:
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();

Az alábbi kiemelt kód hozzáadja az adatbázis-környezetet a függőséginjektálási (DI) tárolóhoz, és lehetővé teszi az adatbázissal kapcsolatos kivételek megjelenítését:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();

A DI-tároló hozzáférést biztosít az adatbázis-környezethez és más szolgáltatásokhoz.

Ez az oktatóanyag az Endpoints Explorer-t és a .http fájlokat használja az API teszteléséhez.

Közzétételi adatok tesztelése

A Program.cs következő kódja létrehoz egy HTTP POST-végpontot /todoitems, amely adatokat ad hozzá a memóriában lévő adatbázishoz:

app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
});

Futtassa az alkalmazást. A böngésző 404-et jelenít meg, mert már nincs / végpont.

A POST végpont használatával adatokat adhat hozzá az alkalmazáshoz.

  • Válassza a Nézet>Egyéb ablakok>Endpoints Explorer.

  • Kattintson a jobb gombbal a POST végpontra, és válassza a Kérés létrehozásalehetőséget.

    Endpoints Explorer helyi menüjében kiemelve a Kérés létrehozása menüpontot.

    Egy új fájl jön létre a TodoApi.httpnevű projektmappában, amely a következő példához hasonló tartalommal rendelkezik:

    @TodoApi_HostAddress = https://localhost:7031
    
    Post {{TodoApi_HostAddress}}/todoitems
    
    ###
    
    • Az első sor létrehoz egy változót, amelyet az összes végponthoz használnak.
    • A következő sor egy POST-kérést határoz meg.
    • A tripla hashtag (###) sor egy kérések közötti elválasztó jel: ami utána következik, az egy másik kéréshez tartozik.
  • A POST-kérelemhez fejlécekre és tartalomra van szükség. A kérelem ezen részeinek meghatározásához közvetlenül a POST kérelemsor után adja hozzá a következő sorokat:

    Content-Type: application/json
    
    {
      "name":"walk dog",
      "isComplete":true
    }
    

    Az előző kód hozzáad egy Tartalomtípus fejlécet és egy JSON-kérelemtörzset. A TodoApi.http fájlnak most az alábbi példához hasonlóan kell kinéznie, de a portszámmal:

    @TodoApi_HostAddress = https://localhost:7057
    
    Post {{TodoApi_HostAddress}}/todoitems
    Content-Type: application/json
    
    {
      "name":"walk dog",
      "isComplete":true
    }
    
    ###
    
  • Futtassa az alkalmazást.

  • Válassza a Kérés küldése hivatkozást, amely a POST kérelemsor felett található.

    .http fájlablak ki van emelve a futtatási hivatkozással.

    A POST kérést a rendszer elküldi az alkalmazásnak, és a válasz megjelenik a Válasz panelen.

    .http fájl ablak a POST-kérés válaszával.

A GET-végpontok vizsgálata

A mintaalkalmazás több GET-végpontot implementál MapGetmeghívásával:

API Description A kérés tartalma Válasz törzse
GET /todoitems Az összes to-do elem lekérése None teendők tömbje
GET /todoitems/complete Az összes kész to-do elem lekérése None teendők tömbje
GET /todoitems/{id} Elem lekérése azonosító alapján None Teendő elem
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());

A GET-végpontok tesztelése

Tesztelje az alkalmazást a GET végpontok böngészőből való meghívásával vagy Endpoints Explorerhasználatával. Az alábbi lépések a Endpoints Explorer-hez tartoznak.

  • Az Endpoints Explorerterületen kattintson a jobb gombbal az első GET végpontra, és válassza a Kérés létrehozásalehetőséget.

    A következő tartalom lesz hozzáadva a TodoApi.http fájlhoz:

    Get {{TodoApi_HostAddress}}/todoitems
    
    ###
    
  • Válassza a Kérés küldése hivatkozást, amely az új GET kérelemsor felett található.

    A GET kérést a rendszer elküldi az alkalmazásnak, és a válasz megjelenik a Válasz panelen.

  • A válasz törzse a következő JSON-hoz hasonló:

    [
      {
        "id": 1,
        "name": "walk dog",
        "isComplete": true
      }
    ]
    
  • Az Endpoints Explorerterületen kattintson a jobb gombbal a /todoitems/{id}GET végpontra, és válassza a Kérés létrehozásalehetőséget. A következő tartalom lesz hozzáadva a TodoApi.http fájlhoz:

    GET {{TodoApi_HostAddress}}/todoitems/{id}
    
    ###
    
  • Cserélje le {id}1.

  • Válassza a Kérés küldése hivatkozást, amely az új GET kérelemsor felett található.

    A GET kérést a rendszer elküldi az alkalmazásnak, és a válasz megjelenik a Válasz panelen.

  • A válasz törzse a következő JSON-hoz hasonló:

    {
      "id": 1,
      "name": "walk dog",
      "isComplete": true
    }
    

Ez az alkalmazás memórián belüli adatbázist használ. Ha az alkalmazás újraindul, a GET kérés nem ad vissza adatokat. Ha nem ad vissza adatokat, POST adatokat az alkalmazásnak, és próbálkozzon újra a GET kéréssel.

Visszaadott értékek

ASP.NET Core automatikusan szerializálja az objektumot JSON formátumra, és beírja a JSON-t a válaszüzenet törzsébe. A visszatérési típus válaszkódja 200 OK, feltéve, hogy nincsenek kezeletlen kivételek. A nem kezelt kivételek 5xx-hibákká lesznek lefordítva.

A visszatérési típusok a HTTP-állapotkódok széles skáláját jelölhetik. A GET /todoitems/{id} például két különböző állapotértéket adhat vissza:

  • Ha egyetlen elem sem felel meg a kért azonosítónak, a metódus egy 404-NotFound hibakódot ad vissza.
  • Ellenkező esetben a metódus egy JSON-válasz törzsével adja vissza a 200-t. A item visszaadása HTTP 200-választ eredményez.

A PUT végpont vizsgálata

A mintaalkalmazás egyetlen PUT-végpontot valósít meg 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();
});

Ez a módszer hasonló a MapPost metódushoz, kivéve, ha HTTP PUT-t használ. A sikeres válasz a 204 (tartalom nélkül) ad vissza. A HTTP-specifikáció szerint a PUT-kéréshez az ügyfélnek a teljes frissített entitást kell elküldenie, nem csak a módosításokat. A részleges frissítések támogatásához használja HTTP PATCH.

A PUT végpont tesztelése

Ez a minta egy memórián belüli adatbázist használ, amelyet az alkalmazás minden indításakor inicializálni kell. Az adatbázisnak tartalmaznia kell egy elemet, mielőtt PUT-hívást kezdeményezne. A GET hívásával győződjön meg arról, hogy van egy elem az adatbázisban, mielőtt PUT-hívást kezdeményez.

Frissítse a to-do elemet, amely Id = 1-t tartalmaz, és állítsa be a nevét "feed fish".

  • Az Endpoints Explorer-ben kattintson a jobb gombbal a PUT végpontra, és válassza a Kérés létrehozásalehetőséget.

    A következő tartalom lesz hozzáadva a TodoApi.http fájlhoz:

    Put {{TodoApi_HostAddress}}/todoitems/{id}
    
    ###
    
  • A PUT kérelemsorban cserélje le a {id}1.

  • Közvetlenül a PUT kérelemsor után adja hozzá a következő sorokat:

    Content-Type: application/json
    
    {
      "name": "feed fish",
      "isComplete": false
    }
    

    Az előző kód hozzáad egy Tartalomtípus fejlécet és egy JSON-kérelemtörzset.

  • Válassza a Kérés küldése hivatkozást, amely az új PUT kérelemsor felett található.

    A PUT kérést a rendszer elküldi az alkalmazásnak, és a válasz megjelenik a Válasz panelen. A válasz törzse üres, az állapotkód pedig 204.

A DELETE végpont vizsgálata és tesztelése

A mintaalkalmazás egyetlen DELETE-végpontot valósít meg 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();
});
  • Az Endpoints Explorerterületen kattintson a jobb gombbal a DELETE végpontra, és válassza a Kérés létrehozásalehetőséget.

    Hozzá van adva egy DELETE kérés a TodoApi.http-hoz.

  • A {id} helyett 1-et használjon a DELETE kérelemsorban. A DELETE kérésnek a következő példához hasonlóan kell kinéznie:

    DELETE {{TodoApi_HostAddress}}/todoitems/1
    
    ###
    
  • Válassza a Küldési kérelem hivatkozást a DELETE kéréshez.

    A DELETE kérést a rendszer elküldi az alkalmazásnak, és a válasz megjelenik a Válasz panelen. A válasz törzse üres, az állapotkód pedig 204.

A MapGroup API használata

A mintaalkalmazás kódja minden végpont beállításakor megismétli a todoitems URL-előtagot. Az API-k gyakran rendelkeznek közös URL-előtaggal rendelkező végpontcsoportokkal, és a MapGroup módszer segít az ilyen csoportok rendszerezésében. Csökkenti az ismétlődő kódot, és lehetővé teszi a végpontok teljes csoportjainak testreszabását egyetlen hívással az olyan metódusokhoz, mint a RequireAuthorization és a WithMetadata.

Cserélje le a Program.cs tartalmát a következő kódra:

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();

Az előző kód a következő módosításokat tartalmazza:

  • Hozzáad var todoItems = app.MapGroup("/todoitems"); a csoport beállításához az URL-előtag /todoitemshasználatával.
  • Módosítja az összes app.Map<HttpVerb> metódust todoItems.Map<HttpVerb>-re.
  • Eltávolítja a /todoitems URL-előtagot a Map<HttpVerb> metódushívásokból.

Tesztelje a végpontokat annak ellenőrzéséhez, hogy azonosak-e.

A TypedResults API használata

A TypedResults helyett a Results visszaadása számos előnnyel jár, beleértve a tesztelhetőséget, és automatikusan visszaadja a választípus metaadatait az OpenAPI-hoz a végpont leírásához. További információ: TypedResults vs Results.

A Map<HttpVerb> metódusok a lambdák használata helyett útvonalkezelő metódusokat is meghívhatnak. Egy példa megtekintéséhez frissítse a Program.cs a következő kóddal:

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();
}

A Map<HttpVerb> kód mostantól metódusokat hív meg a lambdas helyett:

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);

Ezek a metódusok olyan objektumokat adnak vissza, amelyek IResult implementálnak, és amelyeket a TypedResultshatároznak meg:

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();
}

Az egységtesztek meghívhatják ezeket a metódusokat, és tesztelhetik, hogy a megfelelő típust adja vissza. Ha például a módszer GetAllTodos:

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

Az egységtesztelési kód képes ellenőrizni, hogy a kezelőmetódus visszaad-e egy Ok<Todo[]> típusú objektumot. Például:

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);
}

Túlküldés megakadályozása

A mintaalkalmazás jelenleg a teljes Todo objektumot teszi elérhetővé. Éles alkalmazások Éles alkalmazásokban a modell egy részhalmazát gyakran használják a bemeneti és visszaadható adatok korlátozására. Ennek több oka is van, és a biztonság a legfontosabb. A modell részhalmazát általában adatátviteli objektumnak (DTO), bemeneti modellnek vagy nézetmodellnek nevezzük. ebben a cikkben DTO- használunk.

A DTO a következőre használható:

  • A túlküldés megakadályozása.
  • Rejtse el azokat a tulajdonságokat, amelyeket az ügyfeleknek nem szabad megtekinteniük.
  • Hagyj ki néhány tulajdonságot, hogy csökkentsük a hasznos teher méretét.
  • Beágyazott objektumokat tartalmazó objektumdiagramok simítása. A laposabb objektumgráfok kényelmesebbek lehetnek az ügyfelek számára.

A DTO-megközelítés bemutatásához frissítse a Todo osztályt egy titkos mezőre:

public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
    public string? Secret { get; set; }
}

A titkos mezőt el kell rejteni az alkalmazás elől, de egy felügyeleti alkalmazás dönthet úgy, hogy közzéteszi.

Ellenőrizze, hogy közzéteheti és lekérheti-e a titkos kód mezőt.

Hozzon létre egy TodoItemDTO.cs nevű fájlt a következő kóddal:

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);
}

Cserélje le a Program.cs fájl tartalmát a következő kódra a DTO-modell használatához:

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();
}

Ellenőrizze, hogy a titkos kód mező kivételével az összes mezőt közzéteheti és lekérheti.

Hibaelhárítás a kész mintával

Ha olyan problémába ütközik, amely nem oldható meg, hasonlítsa össze a kódot a befejezett projektel. Befejezett projekt megtekintése vagy letöltése (miként tölthető le).

Következő lépések

Learn more

Tekintse meg Minimális API-k rövid útmutatója