Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Warning
Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i supportpolicyn för .NET och .NET Core. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .
Important
Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.
För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .
Av Rick Anderson och Tom Dykstra
Minimala API:er har skapats för att skapa HTTP-API:er med minimala beroenden. De är idealiska för mikrotjänster och appar som bara vill inkludera de minsta filerna, funktionerna och beroendena i ASP.NET Core.
I den här självstudien lär du dig grunderna i att skapa ett minimalt API med ASP.NET Core. En annan metod för att skapa API:er i ASP.NET Core är att använda styrenheter. Hjälp med att välja mellan minimala API:er och kontrollantbaserade API:er finns i ÖVERSIKT över API:er. En självstudiekurs om hur du skapar ett API-projekt baserat på kontrollanter som innehåller fler funktioner finns i Skapa ett webb-API.
Overview
I den här handledningen skapas följande API:
| API | Description | begäranens innehåll | Svarskropp | 
|---|---|---|---|
| GET /todoitems | Hämta alla to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/complete | Hämta slutförda to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/{id} | Hämta ett objekt efter ID | None | Att göra-objekt | 
| POST /todoitems | Lägga till ett nytt objekt | Att göra-objekt | Att göra-objekt | 
| PUT /todoitems/{id} | Uppdatera ett befintligt objekt | Att göra-objekt | None | 
| DELETE /todoitems/{id} | Ta bort ett objekt | None | None | 
Prerequisites
- Visual Studio 2022 med arbetsbelastningen ASP.NET och webbutveckling .   
Skapa ett API-projekt
- Starta Visual Studio 2022 och välj Skapa ett nytt projekt. 
- I dialogrutan Skapa ett nytt projekt : - Ange Emptyi sökrutan Sök efter mallar .
- Välj mallen ASP.NET Core Empty och välj Nästa.
   
- Ange 
- Ge projektet namnet TodoApi och välj Nästa. 
- I dialogrutan Ytterligare information : - Välj .NET 9.0
- Avmarkera Använd inte toppnivåinstruktioner
- Välj Skapa
   
Granska koden
Filen Program.cs innehåller följande kod:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Föregående kod:
- Skapar en WebApplicationBuilder och en WebApplication med förkonfigurerade standardvärden.
- Skapar en HTTP GET-slutpunkt /som returnerarHello World!.
Kör appen
Tryck på Ctrl+F5 för att köra utan felsökningsprogrammet.
Visual Studio visar följande dialogruta:
               
              
            
Välj Ja om du litar på IIS Express SSL-certifikatet.
Följande dialogruta visas:
               
              
            
Välj Ja om du samtycker till att lita på utvecklingscertifikatet.
För information om hur du kan lita på Firefox-webbläsaren, se Firefox SEC_ERROR_INADEQUATE_KEY_USAGE certifikatfel.
Visual Studio startar Kestrel webbservern och öppnar ett webbläsarfönster.
              Hello World! visas i webbläsaren. Filen Program.cs innehåller en minimal men fullständig app.
Stäng webbläsarfönstret.
Lägga till NuGet-paket
NuGet-paketen måste läggas till för att stödja databasen och diagnostiken som används i den här handledningen.
- På menyn Verktyg väljer du NuGet Package Manager > Hantera NuGet-paket för lösning.
- Välj fliken Bläddra.
- Välj Inkludera förhandsversion.
- Ange Microsoft.EntityFrameworkCore.InMemory i sökrutan och välj sedan Microsoft.EntityFrameworkCore.InMemory.
- Markera kryssrutan Projekt i den högra rutan och välj sedan Installera.
- Följ anvisningarna ovan för att lägga till Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore-paketet.
Modell- och databaskontextklasserna
- I projektmappen skapar du en fil med namnet Todo.csmed följande kod:
public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}
Föregående kod skapar modellen för den här appen. En modell är en klass som representerar data som appen hanterar.
- Skapa en fil med namnet TodoDb.csmed följande kod:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
    public TodoDb(DbContextOptions<TodoDb> options)
        : base(options) { }
    public DbSet<Todo> Todos => Set<Todo>();
}
Föregående kod definierar databaskontexten, som är huvudklassen som samordnar Entity Framework-funktioner för en datamodell. Den här klassen härleds från klassen Microsoft.EntityFrameworkCore.DbContext.
Lägg till API-koden
- Ersätt innehållet i Program.cs-filen med följande kod:
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();
Följande markerade kod lägger till databaskontexten i di-containern (dependency injection) och aktiverar visning av databasrelaterade undantag:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
DI-containern ger åtkomst till databaskontexten och andra tjänster.
I den här guiden används Endpoints Explorer och .http-filer för att testa API.
Testdata för publicering
Följande kod i Program.cs skapar en HTTP POST-slutpunkt /todoitems som lägger till data i den minnesinterna databasen:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();
    return Results.Created($"/todoitems/{todo.Id}", todo);
});
Kör appen. Webbläsaren visar ett 404-fel eftersom det inte längre finns någon / slutpunkt.
POST-slutpunkten används för att lägga till data i appen.
- Välj Visa>Andra fönster>Endpoints Explorer. 
- Högerklicka på POST-slutpunkten och välj Generera begäran.   - En ny fil skapas i projektmappen med namnet - TodoApi.http, med innehåll som liknar följande exempel:- @TodoApi_HostAddress = https://localhost:7031 POST {{TodoApi_HostAddress}}/todoitems ###- Den första raden skapar en variabel som används för alla slutpunkter.
- Nästa rad definierar en POST-begäran.
- Trippelhashtag-raden (###) är en begäranavgränsare: vad som följer efter är för en annan begäran.
 
- POST-begäran behöver rubriker och en brödtext. Om du vill definiera dessa delar av begäran lägger du till följande rader omedelbart efter POST-begäranderaden: - Content-Type: application/json { "name":"walk dog", "isComplete":true }- Föregående kod lägger till ett content-type-huvud och en JSON-begärandetext. Filen TodoApi.http bör nu se ut som i följande exempel, men med portnumret: - @TodoApi_HostAddress = https://localhost:7057 POST {{TodoApi_HostAddress}}/todoitems Content-Type: application/json { "name":"walk dog", "isComplete":true } ###
- Kör appen. 
- Välj länken Skicka begäran som ligger ovanför begäranderaden - POST.  - POST-begäran skickas till appen och svaret visas i fönstret Svar .   
Granska GET-slutpunkterna
Exempelappen implementerar flera GET-slutpunkter genom att anropa MapGet:
| API | Description | begäranens innehåll | Svarskropp | 
|---|---|---|---|
| GET /todoitems | Hämta alla to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/complete | Hämta alla slutförda to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/{id} | Hämta ett objekt efter ID | None | Att göra-objekt | 
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());
Testa GET-slutpunkterna
Testa appen genom att anropa GET slutpunkterna från en webbläsare eller med hjälp av Endpoints Explorer. Följande steg är för Endpoints Explorer.
- I Endpoints Explorer högerklickar du på den första GET-slutpunkten och väljer Generera begäran. - Följande innehåll läggs till i filen - TodoApi.http:- GET {{TodoApi_HostAddress}}/todoitems ###
- Välj länken Skicka begäran som ligger ovanför den nya - GETbegäranderaden.- GET-begäran skickas till appen och svaret visas i fönstret Svar . 
- Svarstexten liknar följande JSON: - [ { "id": 1, "name": "walk dog", "isComplete": true } ]
- I Endpoints Explorer högerklickar du på - /todoitems/{id}GET-slutpunkten och väljer Generera begäran. Följande innehåll läggs till i filen- TodoApi.http:- GET {{TodoApi_HostAddress}}/todoitems/{id} ###
- Ersätt - {id}med- 1.
- Välj länken Skicka begäran som ligger ovanför den nya GET-begäranderaden. - GET-begäran skickas till appen och svaret visas i fönstret Svar . 
- Svarstexten liknar följande JSON: - { "id": 1, "name": "walk dog", "isComplete": true }
Den här appen använder en minnesintern databas. Om appen startas om returnerar GET-begäran inte några data. Om inga data returneras skickar du POST-data till appen och provar GET-begäran igen.
Returnera värden
ASP.NET Core serialiserar automatiskt objektet till JSON och skriver JSON i brödtexten i svarsmeddelandet. Svarskoden för den här returtypen är 200 OK, förutsatt att det inte finns några ohanterade undantag. Ohanterade undantag översätts till 5xx-fel.
Returtyperna kan representera ett brett utbud av HTTP-statuskoder. Till exempel kan GET /todoitems/{id} returnera två olika statusvärden:
- Om inget objekt matchar det begärda ID:t returnerar metoden en 404-statusfelkodNotFound .
- Annars returnerar metoden 200 med en JSON-svarstext. Om du returnerar itemresulterar det i ett HTTP 200-svar.
Granska PUT-slutpunkten
Exempelappen implementerar en enda PUT-slutpunkt med hjälp av 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();
});
Den här metoden liknar metoden MapPost, förutom att den använder HTTP PUT. Ett lyckat svar returnerar 204 (inget innehåll). Enligt HTTP-specifikationen kräver en PUT-begäran att klienten skickar hela den uppdaterade entiteten, inte bara ändringarna. Om du vill stödja partiella uppdateringar använder du HTTP PATCH.
Testa PUT-slutpunkten
Det här exemplet använder en minnesintern databas som måste initieras varje gång appen startas. Det måste finnas ett objekt i databasen innan du gör ett PUT-anrop. Anropa GET för att se till att det finns ett objekt i databasen innan du gör ett PUT-anrop.
Uppdatera det to-do objekt som har Id = 1 och ange dess namn till "feed fish".
- I Endpoints Explorer högerklickar du på PUT-slutpunkten och väljer Generera begäran. - Följande innehåll läggs till i filen - TodoApi.http:- PUT {{TodoApi_HostAddress}}/todoitems/{id} ###
- Ersätt - {id}med- 1på PUT-begäranderaden.
- Lägg till följande rader omedelbart efter PUT-begäranderaden: - Content-Type: application/json { "id": 1, "name": "feed fish", "isComplete": false }- Föregående kod lägger till ett content-type-huvud och en JSON-begärandetext. 
- Välj länken Skicka begäran som ligger ovanför den nya PUT-begäranderaden. - PUT-begäran skickas till appen och svaret visas i fönstret Svar . Svarstexten är tom och statuskoden är 204. 
Granska och testa DELETE-slutpunkten
Exempelappen implementerar en enskild DELETE-slutpunkt med 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();
});
- I Endpoints Explorer högerklickar du på DELETE-slutpunkten och väljer Generera begäran. - En DELETE-begäran läggs till i - TodoApi.http.
- Byt ut - {id}i raden för DELETE-begäran mot- 1. DELETE-begäran bör se ut som i följande exempel:- DELETE {{TodoApi_HostAddress}}/todoitems/1 ###
- Välj länken Skicka begäran för DELETE-begäran. - DELETE-begäran skickas till appen och svaret visas i fönstret Svar . Svarstexten är tom och statuskoden är 204. 
Använda MapGroup-API:et
Exempelappkoden upprepar todoitems URL-prefixet varje gång den konfigurerar en slutpunkt. API:er har ofta grupper av slutpunkter med ett vanligt URL-prefix, och metoden MapGroup är tillgänglig för att organisera sådana grupper. Det minskar repetitiv kod och gör det möjligt att anpassa hela grupper av slutpunkter med ett enda anrop till metoder som RequireAuthorization och WithMetadata.
Ersätt innehållet i Program.cs med följande kod:
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();
Föregående kod har följande ändringar:
- Lägger till var todoItems = app.MapGroup("/todoitems");för att konfigurera gruppen med hjälp av URL-prefixet/todoitems.
- Ändrar alla app.Map<HttpVerb>metoder tilltodoItems.Map<HttpVerb>.
- Tar bort URL-prefixet /todoitemsfrånMap<HttpVerb>-metodanrop.
Testa slutpunkterna för att kontrollera att de fungerar på samma sätt.
Använda Api:et TypedResults
Att returnera TypedResults i stället för Results har flera fördelar, inklusive testbarhet och att automatiskt returnera svarstypmetadata för OpenAPI för att beskriva slutpunkten. Mer information finns i TypedResults vs Results.
De Map<HttpVerb> metoderna kan anropa routningshanterarmetoder i stället för att använda lambdas. Om du vill se ett exempel uppdaterar du Program.cs med följande kod:
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();
}
Den Map<HttpVerb> koden anropar nu metoder i stället för lambdas:
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);
Dessa metoder returnerar objekt som implementerar IResult och definieras av TypedResults:
static async Task<IResult> GetAllTodos(TodoDb db)
{
    return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
static async Task<IResult> GetCompleteTodos(TodoDb db)
{
    return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).ToListAsync());
}
static async Task<IResult> GetTodo(int id, TodoDb db)
{
    return await db.Todos.FindAsync(id)
        is Todo todo
            ? TypedResults.Ok(todo)
            : TypedResults.NotFound();
}
static async Task<IResult> CreateTodo(Todo todo, TodoDb db)
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();
    return TypedResults.Created($"/todoitems/{todo.Id}", todo);
}
static async Task<IResult> UpdateTodo(int id, Todo inputTodo, TodoDb db)
{
    var todo = await db.Todos.FindAsync(id);
    if (todo is null) return TypedResults.NotFound();
    todo.Name = inputTodo.Name;
    todo.IsComplete = inputTodo.IsComplete;
    await db.SaveChangesAsync();
    return TypedResults.NoContent();
}
static async Task<IResult> DeleteTodo(int id, TodoDb db)
{
    if (await db.Todos.FindAsync(id) is Todo todo)
    {
        db.Todos.Remove(todo);
        await db.SaveChangesAsync();
        return TypedResults.NoContent();
    }
    return TypedResults.NotFound();
}
Enhetstester kan anropa dessa metoder och testa att de returnerar rätt typ. Om metoden till exempel är GetAllTodos:
static async Task<IResult> GetAllTodos(TodoDb db)
{
    return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Enhetstestkoden kan kontrollera att ett objekt av typen Ok<Todo[]> returneras från hanteringsmetoden. Till exempel:
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);
}
Förhindra överpublicering
För närvarande exponerar exempelappen hela Todo objektet. I produktionsprogram används ofta en delmängd av modellen för att begränsa de data som kan matas in och returneras. Det finns flera orsaker till detta och säkerheten är viktig. Delmängden av en modell kallas vanligtvis för ett dataöverföringsobjekt (DTO), indatamodell eller vymodell. 
              DTO används i den här artikeln.
En DTO kan användas för att:
- Förhindra överpublicering.
- Dölj egenskaper som klienter inte ska visa.
- Utelämna vissa egenskaper för att minska nyttolaststorleken.
- Platta ut objektdiagram som innehåller kapslade objekt. Utplattade objektdiagram kan vara enklare för klienter.
Om du vill demonstrera DTO-metoden uppdaterar du klassen Todo så att den innehåller ett hemligt fält:
public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
    public string? Secret { get; set; }
}
Det hemliga fältet måste vara dolt från den här appen, men en administrativ app kan välja att exponera det.
Kontrollera att du kan skicka och hämta det hemliga fältet.
Skapa en fil med namnet TodoItemDTO.cs med följande kod:
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);
}
Ersätt innehållet i Program.cs-filen med följande kod för att använda den här DTO-modellen:
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();
}
Kontrollera att du kan publicera och hämta alla fält utom det hemliga fältet.
Felsökning med det slutförda exemplet
Om du stöter på ett problem som du inte kan lösa kan du jämföra koden med det slutförda projektet. Visa eller ladda ned slutfört projekt (ladda ned).
Nästa steg
- Konfigurera JSON-serialiseringsalternativ.
- Hantera fel och undantag: Undantagssidan för utvecklare är aktiverad som standard i utvecklingsmiljön för minimala API-appar. Information om hur du hanterar fel och undantag finns i Hantera fel i ASP.NET Core-API:er.
- Ett exempel på hur du testar en minimal API-app finns i det här GitHub-exemplet.
- OpenAPI-stöd i minimala API:er.
- Snabbstart: Publicera till Azure.
- Organisera ASP.NET grundläggande minimala API:er.
Learn more
Minimala API:er har skapats för att skapa HTTP-API:er med minimala beroenden. De är idealiska för mikrotjänster och appar som bara vill inkludera de minsta filerna, funktionerna och beroendena i ASP.NET Core.
I den här självstudien lär du dig grunderna i att skapa ett minimalt API med ASP.NET Core. En annan metod för att skapa API:er i ASP.NET Core är att använda styrenheter. Hjälp med att välja mellan minimala API:er och kontrollantbaserade API:er finns i ÖVERSIKT över API:er. En självstudiekurs om hur du skapar ett API-projekt baserat på kontrollanter som innehåller fler funktioner finns i Skapa ett webb-API.
Overview
I den här handledningen skapas följande API:
| API | Description | begäranens innehåll | Svarskropp | 
|---|---|---|---|
| GET /todoitems | Hämta alla to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/complete | Hämta slutförda to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/{id} | Hämta ett objekt efter ID | None | Att göra-objekt | 
| POST /todoitems | Lägga till ett nytt objekt | Att göra-objekt | Att göra-objekt | 
| PUT /todoitems/{id} | Uppdatera ett befintligt objekt | Att göra-objekt | None | 
| DELETE /todoitems/{id} | Ta bort ett objekt | None | None | 
Prerequisites
- Visual Studio 2022 med arbetsbelastningen ASP.NET och webbutveckling .   
Skapa ett API-projekt
- Starta Visual Studio 2022 och välj Skapa ett nytt projekt. 
- I dialogrutan Skapa ett nytt projekt : - Ange Emptyi sökrutan Sök efter mallar .
- Välj mallen ASP.NET Core Empty och välj Nästa.
   
- Ange 
- Ge projektet namnet TodoApi och välj Nästa. 
- I dialogrutan Ytterligare information : - Välj .NET 7.0
- Avmarkera Använd inte toppnivåinstruktioner
- Välj Skapa
   
Granska koden
Filen Program.cs innehåller följande kod:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Föregående kod:
- Skapar en WebApplicationBuilder och en WebApplication med förkonfigurerade standardvärden.
- Skapar en HTTP GET-slutpunkt /som returnerarHello World!:
Kör appen
Tryck på Ctrl+F5 för att köra utan felsökningsprogrammet.
Visual Studio visar följande dialogruta:
               
              
            
Välj Ja om du litar på IIS Express SSL-certifikatet.
Följande dialogruta visas:
               
              
            
Välj Ja om du samtycker till att lita på utvecklingscertifikatet.
För information om hur du kan lita på Firefox-webbläsaren, se Firefox SEC_ERROR_INADEQUATE_KEY_USAGE certifikatfel.
Visual Studio startar Kestrel webbservern och öppnar ett webbläsarfönster.
              Hello World! visas i webbläsaren. Filen Program.cs innehåller en minimal men fullständig app.
Lägga till NuGet-paket
NuGet-paketen måste läggas till för att stödja databasen och diagnostiken som används i den här handledningen.
- På menyn Verktyg väljer du NuGet Package Manager > Hantera NuGet-paket för lösning.
- Välj fliken Bläddra.
- Ange Microsoft.EntityFrameworkCore.InMemory i sökrutan och välj sedan Microsoft.EntityFrameworkCore.InMemory.
- Markera kryssrutan Projekt i den högra rutan.
- I listrutan Version väljer du den senaste version 7 som är tillgänglig, till exempel 7.0.17och väljer sedan Installera.
- Följ anvisningarna ovan för att lägga till Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore-paketet med den senaste versionen 7 tillgänglig.
Modell- och databaskontextklasserna
I projektmappen skapar du en fil med namnet Todo.cs med följande kod:
public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}
Föregående kod skapar modellen för den här appen. En modell är en klass som representerar data som appen hanterar.
Skapa en fil med namnet TodoDb.cs med följande kod:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
    public TodoDb(DbContextOptions<TodoDb> options)
        : base(options) { }
    public DbSet<Todo> Todos => Set<Todo>();
}
Föregående kod definierar databaskontexten, som är huvudklassen som samordnar Entity Framework-funktioner för en datamodell. Den här klassen härleds från klassen Microsoft.EntityFrameworkCore.DbContext.
Lägg till API-koden
Ersätt innehållet i Program.cs-filen med följande kod:
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();
Följande markerade kod lägger till databaskontexten i di-containern (dependency injection) och aktiverar visning av databasrelaterade undantag:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
DI-containern ger åtkomst till databaskontexten och andra tjänster.
Skapa API-testgränssnitt med Swagger
Det finns många tillgängliga verktyg för webb-API-testning att välja mellan, och du kan följa den här självstudiekursens inledande API-teststeg med ditt eget önskade verktyg.
I den här självstudien används .NET-paketet NSwag.AspNetCore, som integrerar Swagger-verktyg för att generera ett testgränssnitt som följer OpenAPI-specifikationen:
- NSwag: Ett .NET-bibliotek som integrerar Swagger direkt i ASP.NET Core-program, vilket ger mellanprogram och konfiguration.
- Swagger: En uppsättning verktyg med öppen källkod som OpenAPIGenerator och SwaggerUI som genererar API-testsidor som följer OpenAPI-specifikationen.
- OpenAPI-specifikation: Ett dokument som beskriver funktionerna i API:et, baserat på XML- och attributanteckningarna i kontrollanterna och modellerna.
Mer information om hur du använder OpenAPI och NSwag med ASP.NET finns i ASP.NET Core web API-dokumentation med Swagger/OpenAPI.
Installera Swagger-verktyg
- Kör följande kommando: - dotnet add package NSwag.AspNetCore
Föregående kommando lägger till paketet NSwag.AspNetCore, som innehåller verktyg för att generera Swagger-dokument och användargränssnitt.
Konfigurera Swagger-mellanprogram
- Lägg till följande markerade kod innan - appdefinieras i rad- var app = builder.Build();- using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList")); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddOpenApiDocument(config => { config.DocumentName = "TodoAPI"; config.Title = "TodoAPI v1"; config.Version = "v1"; }); var app = builder.Build();
I föregående kod:
- builder.Services.AddEndpointsApiExplorer();: Aktiverar API Explorer, som är en tjänst som tillhandahåller metadata om HTTP-API:et. API Explorer används av Swagger för att generera Swagger-dokumentet.
- builder.Services.AddOpenApiDocument(config => {...});: Lägger till Swagger OpenAPI-dokumentgeneratorn i programtjänsterna och konfigurerar den för att ge mer information om API:et, till exempel dess titel och version. Information om hur du tillhandahåller mer robust API-information finns i Kom igång med NSwag och ASP.NET Core
- Lägg till följande markerade kod på nästa rad när - apphar definierats i rad- var app = builder.Build();- var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseOpenApi(); app.UseSwaggerUi(config => { config.DocumentTitle = "TodoAPI"; config.Path = "/swagger"; config.DocumentPath = "/swagger/{documentName}/swagger.json"; config.DocExpansion = "list"; }); }- Den tidigare koden aktiverar Swagger-mellanprogrammet för att hantera det genererade JSON-dokumentet och Swagger-användargränssnittet. Swagger är endast aktiverat i en utvecklingsmiljö. Om du aktiverar Swagger i en produktionsmiljö kan potentiellt känslig information om API:ets struktur och implementering exponeras. 
Testdata för publicering
Följande kod i Program.cs skapar en HTTP POST-slutpunkt /todoitems som lägger till data i den minnesinterna databasen:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();
    return Results.Created($"/todoitems/{todo.Id}", todo);
});
Kör appen. Webbläsaren visar ett 404-fel eftersom det inte längre finns någon / slutpunkt.
POST-slutpunkten används för att lägga till data i appen.
- När appen fortfarande körs går du till - https://localhost:<port>/swaggeri webbläsaren för att visa den API-testsida som genererats av Swagger.  
- På testsidan för Swagger API väljer du Publicera /todoitems>Prova. 
- Observera att fältet Begärandetext innehåller ett genererat exempelformat som återspeglar parametrarna för API:et. 
- I begärandetexten anger du JSON för ett to-do objekt, utan att ange den valfria - id:- { "name":"walk dog", "isComplete":true }
- Välj Kör.   
Swagger innehåller ett svarsfönster under knappen Kör .
               
              
            
Observera några av de användbara detaljerna:
- cURL: Swagger innehåller ett exempel på ett cURL-kommando i Unix/Linux-syntaxen, som kan köras på kommandoraden med alla bash-gränssnitt som använder Unix/Linux-syntax, inklusive Git Bash från Git för Windows.
- Begärande-URL: En förenklad representation av HTTP-begäran som görs av Swagger UI:s JavaScript-kod för API-anropet. Faktiska begäranden kan innehålla information som rubriker och frågeparametrar och en begärandetext.
- Serversvar: Innehåller svarstexten och rubrikerna. Svarstexten visar att idhar angetts till1.
- Svarskod: En statuskod för 201 HTTPreturnerades, vilket indikerar att begäran har bearbetats och resulterat i skapandet av en ny resurs.
Granska GET-slutpunkterna
Exempelappen implementerar flera GET-slutpunkter genom att anropa MapGet:
| API | Description | begäranens innehåll | Svarskropp | 
|---|---|---|---|
| GET /todoitems | Hämta alla to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/complete | Hämta alla slutförda to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/{id} | Hämta ett objekt efter ID | None | Att göra-objekt | 
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());
Testa GET-slutpunkterna
Testa appen genom att anropa slutpunkterna från en webbläsare eller Swagger.
- I Swagger väljer du GET /todoitems>Prova>Kör. 
- Du kan också anropa GET /todoitems från en webbläsare genom att ange URI - http://localhost:<port>/todoitems:n . Till exempel- http://localhost:5001/todoitems
Anropet till GET /todoitems genererar ett svar som liknar följande:
[
  {
    "id": 1,
    "name": "walk dog",
    "isComplete": true
  }
]
- Anropa GET /todoitems/{id} i Swagger för att returnera data från ett specifikt ID: - Välj GET /todoitems>Prova.
- Ange id-fältet till 1och välj Kör.
 
- Du kan också anropa GET /todoitems från en webbläsare genom att ange URI - https://localhost:<port>/todoitems/1:n . Till exempel- https://localhost:5001/todoitems/1
- Svaret liknar följande: - { "id": 1, "name": "walk dog", "isComplete": true }
Den här appen använder en minnesintern databas. Om appen startas om returnerar GET-begäran inte några data. Om inga data returneras skickar du POST-data till appen och provar GET-begäran igen.
Returnera värden
ASP.NET Core serialiserar automatiskt objektet till JSON och skriver JSON i brödtexten i svarsmeddelandet. Svarskoden för den här returtypen är 200 OK, förutsatt att det inte finns några ohanterade undantag. Ohanterade undantag översätts till 5xx-fel.
Returtyperna kan representera ett brett utbud av HTTP-statuskoder. Till exempel kan GET /todoitems/{id} returnera två olika statusvärden:
- Om inget objekt matchar det begärda ID:t returnerar metoden en 404-statusfelkodNotFound .
- Annars returnerar metoden 200 med en JSON-svarstext. Om du returnerar itemresulterar det i ett HTTP 200-svar.
Granska PUT-slutpunkten
Exempelappen implementerar en enda PUT-slutpunkt med hjälp av 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();
});
Den här metoden liknar metoden MapPost, förutom att den använder HTTP PUT. Ett lyckat svar returnerar 204 (inget innehåll). Enligt HTTP-specifikationen kräver en PUT-begäran att klienten skickar hela den uppdaterade entiteten, inte bara ändringarna. Om du vill stödja partiella uppdateringar använder du HTTP PATCH.
Testa PUT-slutpunkten
Det här exemplet använder en minnesintern databas som måste initieras varje gång appen startas. Det måste finnas ett objekt i databasen innan du gör ett PUT-anrop. Anropa GET för att se till att det finns ett objekt i databasen innan du gör ett PUT-anrop.
Uppdatera det to-do objekt som har Id = 1 och ange dess namn till "feed fish".
Använd Swagger för att skicka en PUT-begäran:
- Välj Lägg till /todoitems/{id}>Prova. 
- Ange ID-fältet till - 1.
- Ställ in förfrågningskroppen som följande JSON: - { "name": "feed fish", "isComplete": false }
- Välj Kör. 
Granska och testa DELETE-slutpunkten
Exempelappen implementerar en enskild DELETE-slutpunkt med 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();
});
Använd Swagger för att skicka en DELETE-begäran:
- Välj DELETE /todoitems/{id}>Prova. 
- Ange ID-fältet till - 1och välj Kör.- DELETE-begäran skickas till appen och svaret visas i fönstret Svar . Svarstexten är tom och statuskoden för serversvar är 204. 
Använda MapGroup-API:et
Exempelappkoden upprepar todoitems URL-prefixet varje gång den konfigurerar en slutpunkt. API:er har ofta grupper av slutpunkter med ett vanligt URL-prefix, och metoden MapGroup är tillgänglig för att organisera sådana grupper. Det minskar repetitiv kod och gör det möjligt att anpassa hela grupper av slutpunkter med ett enda anrop till metoder som RequireAuthorization och WithMetadata.
Ersätt innehållet i Program.cs med följande kod:
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();
Föregående kod har följande ändringar:
- Lägger till var todoItems = app.MapGroup("/todoitems");för att konfigurera gruppen med hjälp av URL-prefixet/todoitems.
- Ändrar alla app.Map<HttpVerb>metoder tilltodoItems.Map<HttpVerb>.
- Tar bort URL-prefixet /todoitemsfrånMap<HttpVerb>-metodanrop.
Testa slutpunkterna för att kontrollera att de fungerar på samma sätt.
Använda Api:et TypedResults
Att returnera TypedResults i stället för Results har flera fördelar, inklusive testbarhet och att automatiskt returnera svarstypmetadata för OpenAPI för att beskriva slutpunkten. Mer information finns i TypedResults vs Results.
De Map<HttpVerb> metoderna kan anropa routningshanterarmetoder i stället för att använda lambdas. Om du vill se ett exempel uppdaterar du Program.cs med följande kod:
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();
}
Den Map<HttpVerb> koden anropar nu metoder i stället för lambdas:
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);
Dessa metoder returnerar objekt som implementerar IResult och definieras av TypedResults:
static async Task<IResult> GetAllTodos(TodoDb db)
{
    return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
static async Task<IResult> GetCompleteTodos(TodoDb db)
{
    return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).ToListAsync());
}
static async Task<IResult> GetTodo(int id, TodoDb db)
{
    return await db.Todos.FindAsync(id)
        is Todo todo
            ? TypedResults.Ok(todo)
            : TypedResults.NotFound();
}
static async Task<IResult> CreateTodo(Todo todo, TodoDb db)
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();
    return TypedResults.Created($"/todoitems/{todo.Id}", todo);
}
static async Task<IResult> UpdateTodo(int id, Todo inputTodo, TodoDb db)
{
    var todo = await db.Todos.FindAsync(id);
    if (todo is null) return TypedResults.NotFound();
    todo.Name = inputTodo.Name;
    todo.IsComplete = inputTodo.IsComplete;
    await db.SaveChangesAsync();
    return TypedResults.NoContent();
}
static async Task<IResult> DeleteTodo(int id, TodoDb db)
{
    if (await db.Todos.FindAsync(id) is Todo todo)
    {
        db.Todos.Remove(todo);
        await db.SaveChangesAsync();
        return TypedResults.NoContent();
    }
    return TypedResults.NotFound();
}
Enhetstester kan anropa dessa metoder och testa att de returnerar rätt typ. Om metoden till exempel är GetAllTodos:
static async Task<IResult> GetAllTodos(TodoDb db)
{
    return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Enhetstestkoden kan kontrollera att ett objekt av typen Ok<Todo[]> returneras från hanteringsmetoden. Till exempel:
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);
}
Förhindra överpublicering
För närvarande exponerar exempelappen hela Todo objektet. Produktionsappar I produktionsprogram används ofta en delmängd av modellen för att begränsa de data som kan matas in och returneras. Det finns flera orsaker till detta och säkerheten är viktig. Delmängden av en modell kallas vanligtvis för ett dataöverföringsobjekt (DTO), indatamodell eller vymodell. 
              DTO används i den här artikeln.
En DTO kan användas för att:
- Förhindra överpublicering.
- Dölj egenskaper som klienter inte ska visa.
- Utelämna vissa egenskaper för att minska nyttolaststorleken.
- Platta ut objektdiagram som innehåller kapslade objekt. Utplattade objektdiagram kan vara enklare för klienter.
Om du vill demonstrera DTO-metoden uppdaterar du klassen Todo så att den innehåller ett hemligt fält:
public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
    public string? Secret { get; set; }
}
Det hemliga fältet måste vara dolt från den här appen, men en administrativ app kan välja att exponera det.
Kontrollera att du kan skicka och hämta det hemliga fältet.
Skapa en fil med namnet TodoItemDTO.cs med följande kod:
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);
}
Ersätt innehållet i Program.cs-filen med följande kod för att använda den här DTO-modellen:
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>();
}
Kontrollera att du kan publicera och hämta alla fält utom det hemliga fältet.
Felsökning med det slutförda exemplet
Om du stöter på ett problem som du inte kan lösa kan du jämföra koden med det slutförda projektet. Visa eller ladda ned slutfört projekt (ladda ned).
Nästa steg
- Konfigurera JSON-serialiseringsalternativ.
- Hantera fel och undantag: Undantagssidan för utvecklare är aktiverad som standard i utvecklingsmiljön för minimala API-appar. Information om hur du hanterar fel och undantag finns i Hantera fel i ASP.NET Core-API:er.
- Ett exempel på hur du testar en minimal API-app finns i det här GitHub-exemplet.
- OpenAPI-stöd i minimala API:er.
- Snabbstart: Publicera till Azure.
- Organisera ASP.NET grundläggande minimala API:er.
Learn more
Minimala API:er har skapats för att skapa HTTP-API:er med minimala beroenden. De är idealiska för mikrotjänster och appar som bara vill inkludera de minsta filerna, funktionerna och beroendena i ASP.NET Core.
I den här självstudien lär du dig grunderna i att skapa ett minimalt API med ASP.NET Core. En annan metod för att skapa API:er i ASP.NET Core är att använda styrenheter. Hjälp med att välja mellan minimala API:er och kontrollantbaserade API:er finns i ÖVERSIKT över API:er. En självstudiekurs om hur du skapar ett API-projekt baserat på kontrollanter som innehåller fler funktioner finns i Skapa ett webb-API.
Overview
I den här handledningen skapas följande API:
| API | Description | begäranens innehåll | Svarskropp | 
|---|---|---|---|
| GET /todoitems | Hämta alla to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/complete | Hämta slutförda to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/{id} | Hämta ett objekt efter ID | None | Att göra-objekt | 
| POST /todoitems | Lägga till ett nytt objekt | Att göra-objekt | Att göra-objekt | 
| PUT /todoitems/{id} | Uppdatera ett befintligt objekt | Att göra-objekt | None | 
| DELETE /todoitems/{id} | Ta bort ett objekt | None | None | 
Prerequisites
- Visual Studio 2022 med arbetsbelastningen ASP.NET och webbutveckling .
- .NET 6 SDK
Skapa ett API-projekt
- Starta Visual Studio 2022 och välj Skapa ett nytt projekt. 
- I dialogrutan Skapa ett nytt projekt : - Ange Emptyi sökrutan Sök efter mallar .
- Välj mallen ASP.NET Core Empty och välj Nästa.
   
- Ange 
- Ge projektet namnet TodoApi och välj Nästa. 
- I dialogrutan Ytterligare information : - Välj .NET 6.0
- Avmarkera Använd inte toppnivåinstruktioner
- Välj Skapa
 
Granska koden
Filen Program.cs innehåller följande kod:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Föregående kod:
- Skapar en WebApplicationBuilder och en WebApplication med förkonfigurerade standardvärden.
- Skapar en HTTP GET-slutpunkt /som returnerarHello World!:
Kör appen
Tryck på Ctrl+F5 för att köra utan felsökningsprogrammet.
Visual Studio visar följande dialogruta:
               
              
            
Välj Ja om du litar på IIS Express SSL-certifikatet.
Följande dialogruta visas:
               
              
            
Välj Ja om du samtycker till att lita på utvecklingscertifikatet.
För information om hur du kan lita på Firefox-webbläsaren, se Firefox SEC_ERROR_INADEQUATE_KEY_USAGE certifikatfel.
Visual Studio startar Kestrel webbservern och öppnar ett webbläsarfönster.
              Hello World! visas i webbläsaren. Filen Program.cs innehåller en minimal men fullständig app.
Lägga till NuGet-paket
NuGet-paketen måste läggas till för att stödja databasen och diagnostiken som används i den här handledningen.
- På menyn Verktyg väljer du NuGet Package Manager > Hantera NuGet-paket för lösning.
- Välj fliken Bläddra.
- Ange Microsoft.EntityFrameworkCore.InMemory i sökrutan och välj sedan Microsoft.EntityFrameworkCore.InMemory.
- Markera kryssrutan Projekt i den högra rutan.
- I listrutan Version väljer du den senaste version 7 som är tillgänglig, till exempel 6.0.28och väljer sedan Installera.
- Följ anvisningarna ovan för att lägga till Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore-paketet med den senaste versionen 7 tillgänglig.
Modell- och databaskontextklasserna
I projektmappen skapar du en fil med namnet Todo.cs med följande kod:
public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}
Föregående kod skapar modellen för den här appen. En modell är en klass som representerar data som appen hanterar.
Skapa en fil med namnet TodoDb.cs med följande kod:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
    public TodoDb(DbContextOptions<TodoDb> options)
        : base(options) { }
    public DbSet<Todo> Todos => Set<Todo>();
}
Föregående kod definierar databaskontexten, som är huvudklassen som samordnar Entity Framework-funktioner för en datamodell. Den här klassen härleds från klassen Microsoft.EntityFrameworkCore.DbContext.
Lägg till API-koden
Ersätt innehållet i Program.cs-filen med följande kod:
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();
Följande markerade kod lägger till databaskontexten i di-containern (dependency injection) och aktiverar visning av databasrelaterade undantag:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
DI-containern ger åtkomst till databaskontexten och andra tjänster.
Skapa API-testgränssnitt med Swagger
Det finns många tillgängliga verktyg för webb-API-testning att välja mellan, och du kan följa den här självstudiekursens inledande API-teststeg med ditt eget önskade verktyg.
I den här självstudien används .NET-paketet NSwag.AspNetCore, som integrerar Swagger-verktyg för att generera ett testgränssnitt som följer OpenAPI-specifikationen:
- NSwag: Ett .NET-bibliotek som integrerar Swagger direkt i ASP.NET Core-program, vilket ger mellanprogram och konfiguration.
- Swagger: En uppsättning verktyg med öppen källkod som OpenAPIGenerator och SwaggerUI som genererar API-testsidor som följer OpenAPI-specifikationen.
- OpenAPI-specifikation: Ett dokument som beskriver funktionerna i API:et, baserat på XML- och attributanteckningarna i kontrollanterna och modellerna.
Mer information om hur du använder OpenAPI och NSwag med ASP.NET finns i ASP.NET Core web API-dokumentation med Swagger/OpenAPI.
Installera Swagger-verktyg
- Kör följande kommando: - dotnet add package NSwag.AspNetCore
Föregående kommando lägger till paketet NSwag.AspNetCore, som innehåller verktyg för att generera Swagger-dokument och användargränssnitt.
Konfigurera Swagger-mellanprogram
- Lägg till följande - using-instruktioner överst i Program.cs:- using NSwag.AspNetCore;
- Lägg till följande markerade kod innan - appdefinieras i rad- var app = builder.Build();- using NSwag.AspNetCore; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList")); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddOpenApiDocument(config => { config.DocumentName = "TodoAPI"; config.Title = "TodoAPI v1"; config.Version = "v1"; }); var app = builder.Build();
I föregående kod:
- builder.Services.AddEndpointsApiExplorer();: Aktiverar API Explorer, som är en tjänst som tillhandahåller metadata om HTTP-API:et. API Explorer används av Swagger för att generera Swagger-dokumentet.
- builder.Services.AddOpenApiDocument(config => {...});: Lägger till Swagger OpenAPI-dokumentgeneratorn i programtjänsterna och konfigurerar den för att ge mer information om API:et, till exempel dess titel och version. Information om hur du tillhandahåller mer robust API-information finns i Kom igång med NSwag och ASP.NET Core
- Lägg till följande markerade kod på nästa rad när - apphar definierats i rad- var app = builder.Build();- var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseOpenApi(); app.UseSwaggerUi(config => { config.DocumentTitle = "TodoAPI"; config.Path = "/swagger"; config.DocumentPath = "/swagger/{documentName}/swagger.json"; config.DocExpansion = "list"; }); }- Den tidigare koden aktiverar Swagger-mellanprogrammet för att hantera det genererade JSON-dokumentet och Swagger-användargränssnittet. Swagger är endast aktiverat i en utvecklingsmiljö. Om du aktiverar Swagger i en produktionsmiljö kan potentiellt känslig information om API:ets struktur och implementering exponeras. 
Testdata för publicering
Följande kod i Program.cs skapar en HTTP POST-slutpunkt /todoitems som lägger till data i den minnesinterna databasen:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();
    return Results.Created($"/todoitems/{todo.Id}", todo);
});
Kör appen. Webbläsaren visar ett 404-fel eftersom det inte längre finns någon / slutpunkt.
POST-slutpunkten används för att lägga till data i appen.
- När appen fortfarande körs går du till - https://localhost:<port>/swaggeri webbläsaren för att visa den API-testsida som genererats av Swagger.  
- På testsidan för Swagger API väljer du Publicera /todoitems>Prova. 
- Observera att fältet Begärandetext innehåller ett genererat exempelformat som återspeglar parametrarna för API:et. 
- I begärandetexten anger du JSON för ett to-do objekt, utan att ange den valfria - id:- { "name":"walk dog", "isComplete":true }
- Välj Kör.   
Swagger innehåller ett svarsfönster under knappen Kör .
               
              
            
Observera några av de användbara detaljerna:
- cURL: Swagger innehåller ett exempel på ett cURL-kommando i Unix/Linux-syntaxen, som kan köras på kommandoraden med alla bash-gränssnitt som använder Unix/Linux-syntax, inklusive Git Bash från Git för Windows.
- Begärande-URL: En förenklad representation av HTTP-begäran som görs av Swagger UI:s JavaScript-kod för API-anropet. Faktiska begäranden kan innehålla information som rubriker och frågeparametrar och en begärandetext.
- Serversvar: Innehåller svarstexten och rubrikerna. Svarstexten visar att idhar angetts till1.
- Svarskod: En statuskod för 201 HTTPreturnerades, vilket indikerar att begäran har bearbetats och resulterat i skapandet av en ny resurs.
Granska GET-slutpunkterna
Exempelappen implementerar flera GET-slutpunkter genom att anropa MapGet:
| API | Description | begäranens innehåll | Svarskropp | 
|---|---|---|---|
| GET /todoitems | Hämta alla to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/complete | Hämta alla slutförda to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/{id} | Hämta ett objekt efter ID | None | Att göra-objekt | 
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());
Testa GET-slutpunkterna
Testa appen genom att anropa slutpunkterna från en webbläsare eller Swagger.
- I Swagger väljer du GET /todoitems>Prova>Kör. 
- Du kan också anropa GET /todoitems från en webbläsare genom att ange URI - http://localhost:<port>/todoitems:n . Till exempel- http://localhost:5001/todoitems
Anropet till GET /todoitems genererar ett svar som liknar följande:
[
  {
    "id": 1,
    "name": "walk dog",
    "isComplete": true
  }
]
- Anropa GET /todoitems/{id} i Swagger för att returnera data från ett specifikt ID: - Välj GET /todoitems>Prova.
- Ange id-fältet till 1och välj Kör.
 
- Du kan också anropa GET /todoitems från en webbläsare genom att ange URI - https://localhost:<port>/todoitems/1:n . Till exempel, till exempel- https://localhost:5001/todoitems/1
- Svaret liknar följande: - { "id": 1, "name": "walk dog", "isComplete": true }
Den här appen använder en minnesintern databas. Om appen startas om returnerar GET-begäran inte några data. Om inga data returneras skickar du POST-data till appen och provar GET-begäran igen.
Returnera värden
ASP.NET Core serialiserar automatiskt objektet till JSON och skriver JSON i brödtexten i svarsmeddelandet. Svarskoden för den här returtypen är 200 OK, förutsatt att det inte finns några ohanterade undantag. Ohanterade undantag översätts till 5xx-fel.
Returtyperna kan representera ett brett utbud av HTTP-statuskoder. Till exempel kan GET /todoitems/{id} returnera två olika statusvärden:
- Om inget objekt matchar det begärda ID:t returnerar metoden en 404-statusfelkodNotFound .
- Annars returnerar metoden 200 med en JSON-svarstext. Om du returnerar itemresulterar det i ett HTTP 200-svar.
Granska PUT-slutpunkten
Exempelappen implementerar en enda PUT-slutpunkt med hjälp av 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();
});
Den här metoden liknar metoden MapPost, förutom att den använder HTTP PUT. Ett lyckat svar returnerar 204 (inget innehåll). Enligt HTTP-specifikationen kräver en PUT-begäran att klienten skickar hela den uppdaterade entiteten, inte bara ändringarna. Om du vill stödja partiella uppdateringar använder du HTTP PATCH.
Testa PUT-slutpunkten
Det här exemplet använder en minnesintern databas som måste initieras varje gång appen startas. Det måste finnas ett objekt i databasen innan du gör ett PUT-anrop. Anropa GET för att se till att det finns ett objekt i databasen innan du gör ett PUT-anrop.
Uppdatera det to-do objekt som har Id = 1 och ange dess namn till "feed fish".
Använd Swagger för att skicka en PUT-begäran:
- Välj Lägg till /todoitems/{id}>Prova. 
- Ange ID-fältet till - 1.
- Ställ in förfrågningskroppen som följande JSON: - { "name": "feed fish", "isComplete": false }
- Välj Kör. 
Granska och testa DELETE-slutpunkten
Exempelappen implementerar en enskild DELETE-slutpunkt med 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();
});
Använd Swagger för att skicka en DELETE-begäran:
- Välj DELETE /todoitems/{id}>Prova. 
- Ange ID-fältet till - 1och välj Kör.- DELETE-begäran skickas till appen och svaret visas i fönstret Svar . Svarstexten är tom och statuskoden för serversvar är 204. 
Förhindra överpublicering
För närvarande exponerar exempelappen hela Todo objektet. Produktionsappar I produktionsprogram används ofta en delmängd av modellen för att begränsa de data som kan matas in och returneras. Det finns flera orsaker till detta och säkerheten är viktig. Delmängden av en modell kallas vanligtvis för ett dataöverföringsobjekt (DTO), indatamodell eller vymodell. 
              DTO används i den här artikeln.
En DTO kan användas för att:
- Förhindra överpublicering.
- Dölj egenskaper som klienter inte ska visa.
- Utelämna vissa egenskaper för att minska nyttolaststorleken.
- Platta ut objektdiagram som innehåller kapslade objekt. Utplattade objektdiagram kan vara enklare för klienter.
Om du vill demonstrera DTO-metoden uppdaterar du klassen Todo så att den innehåller ett hemligt fält:
public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
    public string? Secret { get; set; }
}
Det hemliga fältet måste vara dolt från den här appen, men en administrativ app kan välja att exponera det.
Kontrollera att du kan skicka och hämta det hemliga fältet.
Skapa en fil med namnet TodoItemDTO.cs med följande kod:
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);
}
Ersätt innehållet i Program.cs-filen med följande kod för att använda den här DTO-modellen:
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>();
}
Kontrollera att du kan publicera och hämta alla fält utom det hemliga fältet.
Testa minimalt API
Ett exempel på hur du testar en minimal API-app finns i det här GitHub-exemplet.
Publicera till Azure
Information om hur du distribuerar till Azure finns i Snabbstart: Distribuera en ASP.NET webbapp.
Ytterligare resurser
Minimala API:er har skapats för att skapa HTTP-API:er med minimala beroenden. De är idealiska för mikrotjänster och appar som bara vill inkludera de minsta filerna, funktionerna och beroendena i ASP.NET Core.
I den här självstudien lär du dig grunderna i att skapa ett minimalt API med ASP.NET Core. En annan metod för att skapa API:er i ASP.NET Core är att använda styrenheter. Hjälp med att välja mellan minimala API:er och kontrollantbaserade API:er finns i ÖVERSIKT över API:er. En självstudiekurs om hur du skapar ett API-projekt baserat på kontrollanter som innehåller fler funktioner finns i Skapa ett webb-API.
Overview
I den här handledningen skapas följande API:
| API | Description | begäranens innehåll | Svarskropp | 
|---|---|---|---|
| GET /todoitems | Hämta alla to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/complete | Hämta slutförda to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/{id} | Hämta ett objekt efter ID | None | Att göra-objekt | 
| POST /todoitems | Lägga till ett nytt objekt | Att göra-objekt | Att göra-objekt | 
| PUT /todoitems/{id} | Uppdatera ett befintligt objekt | Att göra-objekt | None | 
| DELETE /todoitems/{id} | Ta bort ett objekt | None | None | 
Prerequisites
- Visual Studio 2022 med arbetsbelastningen ASP.NET och webbutveckling .   
Skapa ett API-projekt
- Starta Visual Studio 2022 och välj Skapa ett nytt projekt. 
- I dialogrutan Skapa ett nytt projekt : - Ange Emptyi sökrutan Sök efter mallar .
- Välj mallen ASP.NET Core Empty och välj Nästa.
   
- Ange 
- Ge projektet namnet TodoApi och välj Nästa. 
- I dialogrutan Ytterligare information : - Välj .NET 8.0 (långsiktig support)
- Avmarkera Använd inte toppnivåinstruktioner
- Välj Skapa
   
Granska koden
Filen Program.cs innehåller följande kod:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Föregående kod:
- Skapar en WebApplicationBuilder och en WebApplication med förkonfigurerade standardvärden.
- Skapar en HTTP GET-slutpunkt /som returnerarHello World!:
Kör appen
Tryck på Ctrl+F5 för att köra utan felsökningsprogrammet.
Visual Studio visar följande dialogruta:
               
              
            
Välj Ja om du litar på IIS Express SSL-certifikatet.
Följande dialogruta visas:
               
              
            
Välj Ja om du samtycker till att lita på utvecklingscertifikatet.
För information om hur du kan lita på Firefox-webbläsaren, se Firefox SEC_ERROR_INADEQUATE_KEY_USAGE certifikatfel.
Visual Studio startar Kestrel webbservern och öppnar ett webbläsarfönster.
              Hello World! visas i webbläsaren. Filen Program.cs innehåller en minimal men fullständig app.
Stäng webbläsarfönstret.
Lägga till NuGet-paket
NuGet-paketen måste läggas till för att stödja databasen och diagnostiken som används i den här handledningen.
- På menyn Verktyg väljer du NuGet Package Manager > Hantera NuGet-paket för lösning.
- Välj fliken Bläddra.
- Ange Microsoft.EntityFrameworkCore.InMemory i sökrutan och välj sedan Microsoft.EntityFrameworkCore.InMemory.
- Markera kryssrutan Projekt i den högra rutan och välj sedan Installera.
- Följ anvisningarna ovan för att lägga till Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore-paketet.
Modell- och databaskontextklasserna
- I projektmappen skapar du en fil med namnet Todo.csmed följande kod:
public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}
Föregående kod skapar modellen för den här appen. En modell är en klass som representerar data som appen hanterar.
- Skapa en fil med namnet TodoDb.csmed följande kod:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
    public TodoDb(DbContextOptions<TodoDb> options)
        : base(options) { }
    public DbSet<Todo> Todos => Set<Todo>();
}
Föregående kod definierar databaskontexten, som är huvudklassen som samordnar Entity Framework-funktioner för en datamodell. Den här klassen härleds från klassen Microsoft.EntityFrameworkCore.DbContext.
Lägg till API-koden
- Ersätt innehållet i Program.cs-filen med följande kod:
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();
Följande markerade kod lägger till databaskontexten i di-containern (dependency injection) och aktiverar visning av databasrelaterade undantag:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
DI-containern ger åtkomst till databaskontexten och andra tjänster.
I den här guiden används Endpoints Explorer och .http-filer för att testa API.
Testdata för publicering
Följande kod i Program.cs skapar en HTTP POST-slutpunkt /todoitems som lägger till data i den minnesinterna databasen:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();
    return Results.Created($"/todoitems/{todo.Id}", todo);
});
Kör appen. Webbläsaren visar ett 404-fel eftersom det inte längre finns någon / slutpunkt.
POST-slutpunkten används för att lägga till data i appen.
- Välj Visa>Andra fönster>Endpoints Explorer. 
- Högerklicka på POST-slutpunkten och välj Generera begäran.   - En ny fil skapas i projektmappen med namnet - TodoApi.http, med innehåll som liknar följande exempel:- @TodoApi_HostAddress = https://localhost:7031 Post {{TodoApi_HostAddress}}/todoitems ###- Den första raden skapar en variabel som används för alla slutpunkter.
- Nästa rad definierar en POST-begäran.
- Trippelhashtag-raden (###) är en begäranavgränsare: vad som följer efter är för en annan begäran.
 
- POST-begäran behöver rubriker och en brödtext. Om du vill definiera dessa delar av begäran lägger du till följande rader omedelbart efter POST-begäranderaden: - Content-Type: application/json { "name":"walk dog", "isComplete":true }- Föregående kod lägger till ett content-type-huvud och en JSON-begärandetext. Filen TodoApi.http bör nu se ut som i följande exempel, men med portnumret: - @TodoApi_HostAddress = https://localhost:7057 Post {{TodoApi_HostAddress}}/todoitems Content-Type: application/json { "name":"walk dog", "isComplete":true } ###
- Kör appen. 
- Välj länken Skicka begäran som ligger ovanför begäranderaden - POST.  - POST-begäran skickas till appen och svaret visas i fönstret Svar .   
Granska GET-slutpunkterna
Exempelappen implementerar flera GET-slutpunkter genom att anropa MapGet:
| API | Description | begäranens innehåll | Svarskropp | 
|---|---|---|---|
| GET /todoitems | Hämta alla to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/complete | Hämta alla slutförda to-do objekt | None | Matris med to-do objekt | 
| GET /todoitems/{id} | Hämta ett objekt efter ID | None | Att göra-objekt | 
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());
Testa GET-slutpunkterna
Testa appen genom att anropa GET slutpunkterna från en webbläsare eller med hjälp av Endpoints Explorer. Följande steg är för Endpoints Explorer.
- I Endpoints Explorer högerklickar du på den första GET-slutpunkten och väljer Generera begäran. - Följande innehåll läggs till i filen - TodoApi.http:- Get {{TodoApi_HostAddress}}/todoitems ###
- Välj länken Skicka begäran som ligger ovanför den nya - GETbegäranderaden.- GET-begäran skickas till appen och svaret visas i fönstret Svar . 
- Svarstexten liknar följande JSON: - [ { "id": 1, "name": "walk dog", "isComplete": true } ]
- I Endpoints Explorer högerklickar du på - /todoitems/{id}GET-slutpunkten och väljer Generera begäran. Följande innehåll läggs till i filen- TodoApi.http:- GET {{TodoApi_HostAddress}}/todoitems/{id} ###
- Ersätt - {id}med- 1.
- Välj länken Skicka begäran som ligger ovanför den nya GET-begäranderaden. - GET-begäran skickas till appen och svaret visas i fönstret Svar . 
- Svarstexten liknar följande JSON: - { "id": 1, "name": "walk dog", "isComplete": true }
Den här appen använder en minnesintern databas. Om appen startas om returnerar GET-begäran inte några data. Om inga data returneras skickar du POST-data till appen och provar GET-begäran igen.
Returnera värden
ASP.NET Core serialiserar automatiskt objektet till JSON och skriver JSON i brödtexten i svarsmeddelandet. Svarskoden för den här returtypen är 200 OK, förutsatt att det inte finns några ohanterade undantag. Ohanterade undantag översätts till 5xx-fel.
Returtyperna kan representera ett brett utbud av HTTP-statuskoder. Till exempel kan GET /todoitems/{id} returnera två olika statusvärden:
- Om inget objekt matchar det begärda ID:t returnerar metoden en 404-statusfelkodNotFound .
- Annars returnerar metoden 200 med en JSON-svarstext. Om du returnerar itemresulterar det i ett HTTP 200-svar.
Granska PUT-slutpunkten
Exempelappen implementerar en enda PUT-slutpunkt med hjälp av 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();
});
Den här metoden liknar metoden MapPost, förutom att den använder HTTP PUT. Ett lyckat svar returnerar 204 (inget innehåll). Enligt HTTP-specifikationen kräver en PUT-begäran att klienten skickar hela den uppdaterade entiteten, inte bara ändringarna. Om du vill stödja partiella uppdateringar använder du HTTP PATCH.
Testa PUT-slutpunkten
Det här exemplet använder en minnesintern databas som måste initieras varje gång appen startas. Det måste finnas ett objekt i databasen innan du gör ett PUT-anrop. Anropa GET för att se till att det finns ett objekt i databasen innan du gör ett PUT-anrop.
Uppdatera det to-do objekt som har Id = 1 och ange dess namn till "feed fish".
- I Endpoints Explorer högerklickar du på PUT-slutpunkten och väljer Generera begäran. - Följande innehåll läggs till i filen - TodoApi.http:- Put {{TodoApi_HostAddress}}/todoitems/{id} ###
- Ersätt - {id}med- 1på PUT-begäranderaden.
- Lägg till följande rader omedelbart efter PUT-begäranderaden: - Content-Type: application/json { "name": "feed fish", "isComplete": false }- Föregående kod lägger till ett content-type-huvud och en JSON-begärandetext. 
- Välj länken Skicka begäran som ligger ovanför den nya PUT-begäranderaden. - PUT-begäran skickas till appen och svaret visas i fönstret Svar . Svarstexten är tom och statuskoden är 204. 
Granska och testa DELETE-slutpunkten
Exempelappen implementerar en enskild DELETE-slutpunkt med 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();
});
- I Endpoints Explorer högerklickar du på DELETE-slutpunkten och väljer Generera begäran. - En DELETE-begäran läggs till i - TodoApi.http.
- Byt ut - {id}i raden för DELETE-begäran mot- 1. DELETE-begäran bör se ut som i följande exempel:- DELETE {{TodoApi_HostAddress}}/todoitems/1 ###
- Välj länken Skicka begäran för DELETE-begäran. - DELETE-begäran skickas till appen och svaret visas i fönstret Svar . Svarstexten är tom och statuskoden är 204. 
Använda MapGroup-API:et
Exempelappkoden upprepar todoitems URL-prefixet varje gång den konfigurerar en slutpunkt. API:er har ofta grupper av slutpunkter med ett vanligt URL-prefix, och metoden MapGroup är tillgänglig för att organisera sådana grupper. Det minskar repetitiv kod och gör det möjligt att anpassa hela grupper av slutpunkter med ett enda anrop till metoder som RequireAuthorization och WithMetadata.
Ersätt innehållet i Program.cs med följande kod:
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();
Föregående kod har följande ändringar:
- Lägger till var todoItems = app.MapGroup("/todoitems");för att konfigurera gruppen med hjälp av URL-prefixet/todoitems.
- Ändrar alla app.Map<HttpVerb>metoder tilltodoItems.Map<HttpVerb>.
- Tar bort URL-prefixet /todoitemsfrånMap<HttpVerb>-metodanrop.
Testa slutpunkterna för att kontrollera att de fungerar på samma sätt.
Använda Api:et TypedResults
Att returnera TypedResults i stället för Results har flera fördelar, inklusive testbarhet och att automatiskt returnera svarstypmetadata för OpenAPI för att beskriva slutpunkten. Mer information finns i TypedResults vs Results.
De Map<HttpVerb> metoderna kan anropa routningshanterarmetoder i stället för att använda lambdas. Om du vill se ett exempel uppdaterar du Program.cs med följande kod:
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();
}
Den Map<HttpVerb> koden anropar nu metoder i stället för lambdas:
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);
Dessa metoder returnerar objekt som implementerar IResult och definieras av TypedResults:
static async Task<IResult> GetAllTodos(TodoDb db)
{
    return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
static async Task<IResult> GetCompleteTodos(TodoDb db)
{
    return TypedResults.Ok(await db.Todos.Where(t => t.IsComplete).ToListAsync());
}
static async Task<IResult> GetTodo(int id, TodoDb db)
{
    return await db.Todos.FindAsync(id)
        is Todo todo
            ? TypedResults.Ok(todo)
            : TypedResults.NotFound();
}
static async Task<IResult> CreateTodo(Todo todo, TodoDb db)
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();
    return TypedResults.Created($"/todoitems/{todo.Id}", todo);
}
static async Task<IResult> UpdateTodo(int id, Todo inputTodo, TodoDb db)
{
    var todo = await db.Todos.FindAsync(id);
    if (todo is null) return TypedResults.NotFound();
    todo.Name = inputTodo.Name;
    todo.IsComplete = inputTodo.IsComplete;
    await db.SaveChangesAsync();
    return TypedResults.NoContent();
}
static async Task<IResult> DeleteTodo(int id, TodoDb db)
{
    if (await db.Todos.FindAsync(id) is Todo todo)
    {
        db.Todos.Remove(todo);
        await db.SaveChangesAsync();
        return TypedResults.NoContent();
    }
    return TypedResults.NotFound();
}
Enhetstester kan anropa dessa metoder och testa att de returnerar rätt typ. Om metoden till exempel är GetAllTodos:
static async Task<IResult> GetAllTodos(TodoDb db)
{
    return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Enhetstestkoden kan kontrollera att ett objekt av typen Ok<Todo[]> returneras från hanteringsmetoden. Till exempel:
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);
}
Förhindra överpublicering
För närvarande exponerar exempelappen hela Todo objektet. Produktionsappar I produktionsprogram används ofta en delmängd av modellen för att begränsa de data som kan matas in och returneras. Det finns flera orsaker till detta och säkerheten är viktig. Delmängden av en modell kallas vanligtvis för ett dataöverföringsobjekt (DTO), indatamodell eller vymodell. 
              DTO används i den här artikeln.
En DTO kan användas för att:
- Förhindra överpublicering.
- Dölj egenskaper som klienter inte ska visa.
- Utelämna vissa egenskaper för att minska nyttolaststorleken.
- Platta ut objektdiagram som innehåller kapslade objekt. Utplattade objektdiagram kan vara enklare för klienter.
Om du vill demonstrera DTO-metoden uppdaterar du klassen Todo så att den innehåller ett hemligt fält:
public class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
    public string? Secret { get; set; }
}
Det hemliga fältet måste vara dolt från den här appen, men en administrativ app kan välja att exponera det.
Kontrollera att du kan skicka och hämta det hemliga fältet.
Skapa en fil med namnet TodoItemDTO.cs med följande kod:
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);
}
Ersätt innehållet i Program.cs-filen med följande kod för att använda den här DTO-modellen:
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();
}
Kontrollera att du kan publicera och hämta alla fält utom det hemliga fältet.
Felsökning med det slutförda exemplet
Om du stöter på ett problem som du inte kan lösa kan du jämföra koden med det slutförda projektet. Visa eller ladda ned slutfört projekt (ladda ned).
Nästa steg
- Konfigurera JSON-serialiseringsalternativ.
- Hantera fel och undantag: Undantagssidan för utvecklare är aktiverad som standard i utvecklingsmiljön för minimala API-appar. Information om hur du hanterar fel och undantag finns i Hantera fel i ASP.NET Core-API:er.
- Ett exempel på hur du testar en minimal API-app finns i det här GitHub-exemplet.
- OpenAPI-stöd i minimala API:er.
- Snabbstart: Publicera till Azure.
- Organisera ASP.NET grundläggande minimala API:er.
Learn more
ASP.NET Core
 
              
             
              
            