Zelfstudie: Een minimale API maken met ASP.NET Core
Notitie
Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Waarschuwing
Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Belangrijk
Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.
Zie de .NET 9-versie van dit artikelvoor de huidige release.
Door Rick Anderson en Tom Dykstra
Minimale API's zijn ontworpen om HTTP-API's te maken met minimale afhankelijkheden. Ze zijn ideaal voor microservices en apps die alleen de minimale bestanden, functies en afhankelijkheden in ASP.NET Core willen opnemen.
In deze zelfstudie leert u de basisbeginselen van het bouwen van een minimale API met ASP.NET Core. Een andere benadering voor het maken van API's in ASP.NET Core is het gebruik van controllers. Zie het overzicht van API'svoor hulp bij het kiezen tussen minimale API's en api's op basis van een controller. Zie Een web-API-maken voor een tutorial over het opstellen van een API-project met meer functies, gebaseerd op controllers.
Overzicht
Bij deze handleiding maken we de volgende API:
API | Beschrijving | Aanvraaginhoud | Hoofdtekst van antwoord |
---|---|---|---|
GET /todoitems |
Alle to-do items ophalen | Geen | Matrix van to-do items |
GET /todoitems/complete |
Voltooide to-do items verkrijgen | Geen | Matrix van to-do items |
GET /todoitems/{id} |
Een item ophalen met ID | Geen | Actiepunt |
POST /todoitems |
Een nieuw item toevoegen | Takenitem | Te doen taak |
PUT /todoitems/{id} |
Een bestaand item bijwerken | Actiepunt | Geen |
DELETE /todoitems/{id} |
Een item verwijderen | Geen | Geen |
Voorwaarden
Visual Studio 2022 met de ASP.NET- en webontwikkelingsworkload.
Een API-project maken
Start Visual Studio 2022 en selecteer Een nieuw project maken.
In het dialoogvenster Een Nieuw Project Maken:
- Typ
Empty
in het zoekvak Zoeken naar sjablonen. - Selecteer de sjabloon ASP.NET Core Empty en klik op Volgende.
- Typ
Geef het project de naam TodoApi- en selecteer Volgende.
In het dialoogvenster Aanvullende informatie:
- Selecteer .NET 9.0
- Schakel Gebruik geen instructies op het hoogste niveau uit
- Selecteer Creëren
De code onderzoeken
Het bestand Program.cs
bevat de volgende code:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
De voorgaande code:
- Hiermee maakt u een WebApplicationBuilder en een WebApplication met vooraf geconfigureerde standaardinstellingen.
- Hiermee maakt u een HTTP GET-eindpunt
/
datHello World!
retourneert:
De app uitvoeren
Druk op Ctrl+F5 om uit te voeren zonder het foutopsporingsprogramma.
Visual Studio geeft het volgende dialoogvenster weer:
Selecteer Ja als u het IIS Express SSL-certificaat vertrouwt.
Het volgende dialoogvenster wordt weergegeven:
Selecteer Ja als u akkoord gaat met het vertrouwen van het ontwikkelingscertificaat.
Zie Firefox SEC_ERROR_INADEQUATE_KEY_USAGE certificaatfoutvoor meer informatie over het vertrouwen van de Firefox-browser.
Visual Studio start de Kestrel webserver en opent een browservenster.
Hello World!
wordt weergegeven in de browser. Het bestand Program.cs
bevat een minimale maar volledige app.
Sluit het browservenster.
NuGet-pakketten toevoegen
NuGet-pakketten moeten worden toegevoegd ter ondersteuning van de database en diagnostische gegevens die in deze zelfstudie worden gebruikt.
- Selecteer in het menu ToolsNuGet Package Manager > NuGet-pakketten beheren voor Solution.
- Selecteer het tabblad Bladeren.
- Selecteer Inclusief Prelease.
- Voer Microsoft.EntityFrameworkCore.InMemory- in het zoekvak in en selecteer vervolgens
Microsoft.EntityFrameworkCore.InMemory
. - Vink het selectievakje Project in het rechterpaneel aan en selecteer vervolgens Installeren.
- Volg de voorgaande instructies om het
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
-pakket toe te voegen.
De contextklassen van het model en de database
- Maak in de projectmap een bestand met de naam
Todo.cs
met de volgende code:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Met de voorgaande code wordt het model voor deze app gemaakt. Een model is een klasse die gegevens vertegenwoordigt die door de app worden beheerd.
- Maak een bestand met de naam
TodoDb.cs
met de volgende code:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
De voorgaande code definieert de databasecontext, de belangrijkste klasse die de functionaliteit van Entity Framework coördineert voor een gegevensmodel. Deze klasse is afgeleid van de klasse Microsoft.EntityFrameworkCore.DbContext.
De API-code toevoegen
- Vervang de inhoud van het bestand
Program.cs
door de volgende code:
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();
Met de volgende gemarkeerde code wordt de databasecontext toegevoegd aan de afhankelijkheidsinjectie (DI) container en worden databasegerelateerde uitzonderingen weergegeven:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
De DI-container biedt toegang tot de databasecontext en andere services.
In deze zelfstudie worden Endpoints Explorer en .http-bestanden gebruikt om de API te testen.
Gegevens voor plaatsing testen
Met de volgende code in Program.cs
wordt een HTTP POST-eindpunt gemaakt /todoitems
waarmee gegevens worden toegevoegd aan de in-memory database:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
Voer de app uit. In de browser wordt een 404-fout weergegeven omdat er geen /
eindpunt meer is.
Het POST-eindpunt wordt gebruikt om gegevens toe te voegen aan de app.
Selecteer Weergave>Andere vensters>Endpoints Explorer.
Klik met de rechtermuisknop op het eindpunt POST en selecteer Aanvraag genereren.
Er wordt een nieuw bestand gemaakt in de projectmap met de naam
TodoApi.http
, met inhoud die vergelijkbaar is met het volgende voorbeeld:@TodoApi_HostAddress = https://localhost:7031 Post {{TodoApi_HostAddress}}/todoitems ###
- Met de eerste regel maakt u een variabele die wordt gebruikt voor alle eindpunten.
- De volgende regel definieert een POST-aanvraag.
- De regel met de drievoudige hashtag (
###
) is een scheidingsteken voor aanvragen: wat erna komt, is voor een andere aanvraag.
De POST-aanvraag heeft headers en een hoofdtekst nodig. Als u deze onderdelen van de aanvraag wilt definiëren, voegt u de volgende regels toe direct na de POST-aanvraagregel:
Content-Type: application/json { "name":"walk dog", "isComplete":true }
Met de voorgaande code worden een header voor het inhoudstype en een hoofdtekst van een JSON-aanvraag toegevoegd. Het todoApi.http-bestand moet er nu uitzien zoals in het volgende voorbeeld, maar met uw poortnummer:
@TodoApi_HostAddress = https://localhost:7057 Post {{TodoApi_HostAddress}}/todoitems Content-Type: application/json { "name":"walk dog", "isComplete":true } ###
Voer de app uit.
Selecteer de koppeling Aanvraag verzenden boven de
POST
aanvraagregel.De POST-aanvraag wordt verzonden naar de app en het antwoord wordt weergegeven in het deelvenster Antwoord.
De GET-eindpunten onderzoeken
De voorbeeld-app implementeert verschillende GET-eindpunten door MapGet
aan te roepen:
API | Beschrijving | Aanvraaginhoud | Hoofdtekst van antwoord |
---|---|---|---|
GET /todoitems |
Alle to-do items verkrijgen | Geen | Matrix van to-do items |
GET /todoitems/complete |
Alle voltooide to-do items ophalen | Geen | Matrix van to-do items |
GET /todoitems/{id} |
Een item ophalen met ID | Geen | Takenitem |
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());
De GET-eindpunten testen
Test de app door de GET
-eindpunten aan te roepen vanuit een browser of met behulp van Endpoints Explorer. De volgende stappen zijn bedoeld voor Endpoints Explorer.
Klik in Endpoints Explorermet de rechtermuisknop op het eerste GET--eindpunt en selecteer Aanvraag genereren.
De volgende inhoud wordt toegevoegd aan het
TodoApi.http
-bestand:Get {{TodoApi_HostAddress}}/todoitems ###
Selecteer de koppeling Aanvraag verzenden boven de nieuwe
GET
aanvraagregel.De GET-aanvraag wordt verzonden naar de app en het antwoord wordt weergegeven in het deelvenster Antwoord.
De hoofdtekst van het antwoord is vergelijkbaar met de volgende JSON:
[ { "id": 1, "name": "walk dog", "isComplete": true } ]
Klik in Endpoints Explorermet de rechtermuisknop op het eindpunt
/todoitems/{id}
GET en selecteer Aanvraag genereren. De volgende inhoud wordt toegevoegd aan hetTodoApi.http
-bestand:GET {{TodoApi_HostAddress}}/todoitems/{id} ###
Vervang
{id}
door1
.Selecteer de aanvraag verzenden koppeling die zich boven de nieuwe GET-aanvraagregel bevindt.
De GET-aanvraag wordt verzonden naar de app en het antwoord wordt weergegeven in het deelvenster Antwoord.
De hoofdtekst van het antwoord is vergelijkbaar met de volgende JSON:
{ "id": 1, "name": "walk dog", "isComplete": true }
Deze app maakt gebruik van een in-memory database. Als de app opnieuw wordt opgestart, retourneert de GET-aanvraag geen gegevens. Als er geen gegevens worden geretourneerd, POST gegevens naar de app en probeer de GET-aanvraag opnieuw.
Retourwaarden
ASP.NET Core het object automatisch serialiseert naar JSON- en schrijft de JSON naar de hoofdtekst van het antwoordbericht. De antwoordcode voor dit retourtype is 200 OK, ervan uitgaande dat er geen onverwerkte uitzonderingen zijn. Niet-verwerkte uitzonderingen worden omgezet in 5xx-fouten.
De retourtypen kunnen een breed scala aan HTTP-statuscodes vertegenwoordigen.
GET /todoitems/{id}
kan bijvoorbeeld twee verschillende statuswaarden retourneren:
- Als er geen item overeenkomt met de aangevraagde id, retourneert de methode een 404-statusNotFound foutcode.
- Anders retourneert de methode 200 met een JSON-antwoordtekst. Het retourneren van
item
resulteert in een HTTP 200-antwoord.
Het PUT-eindpunt onderzoeken
De voorbeeld-app implementeert één PUT-eindpunt met behulp van 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();
});
Deze methode is vergelijkbaar met de methode MapPost
, met uitzondering van HTTP PUT. Een geslaagd antwoord retourneert 204 (Geen inhoud). Volgens de HTTP-specificatie vereist een PUT-aanvraag dat de client de volledige bijgewerkte entiteit verzendt, niet alleen de wijzigingen. Gebruik HTTP PATCH-om gedeeltelijke updates te ondersteunen.
Het PUT-eindpunt testen
In dit voorbeeld wordt een in-memory database gebruikt die telkens wanneer de app wordt gestart, moet worden geïnitialiseerd. Er moet een item in de database staan voordat u een PUT-aanroep uitvoert. Roep GET aan om ervoor te zorgen dat er een item in de database staat voordat u een PUT-aanroep doet.
Werk het to-do item met Id = 1
bij en stel de naam ervan in op "feed fish"
.
Klik in Endpoints Explorermet de rechtermuisknop op het PUT-eindpunt en selecteer Aanvraag genereren.
De volgende inhoud wordt toegevoegd aan het
TodoApi.http
-bestand:Put {{TodoApi_HostAddress}}/todoitems/{id} ###
Vervang
{id}
in de PUT-aanvraagregel door1
.Voeg de volgende regels toe direct na de PUT-aanvraagregel:
Content-Type: application/json { "name": "feed fish", "isComplete": false }
Met de voorgaande code worden een header voor het inhoudstype en een hoofdtekst van een JSON-aanvraag toegevoegd.
Selecteer de koppeling Aanvraag verzenden boven de nieuwe PUT-aanvraagregel.
De PUT-aanvraag wordt verzonden naar de app en het antwoord wordt weergegeven in het deelvenster Antwoord. De hoofdtekst van het antwoord is leeg en de statuscode is 204.
Het DELETE-eindpunt onderzoeken en testen
De voorbeeld-app implementeert één DELETE-eindpunt met behulp van 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();
});
Klik in Endpoints Explorermet de rechtermuisknop op het eindpunt DELETE en selecteer Aanvraag genereren.
Er wordt een DELETE-aanvraag toegevoegd aan
TodoApi.http
.Vervang
{id}
in de regel van de DELETE-aanvraag door1
. De DELETE-aanvraag moet eruitzien als in het volgende voorbeeld:DELETE {{TodoApi_HostAddress}}/todoitems/1 ###
Selecteer de koppeling Aanvraag verzenden voor de DELETE-aanvraag.
De DELETE-aanvraag wordt verzonden naar de app en het antwoord wordt weergegeven in het deelvenster Antwoord. De hoofdtekst van het antwoord is leeg en de statuscode is 204.
De MapGroup-API gebruiken
De code van de voorbeeld-app herhaalt het todoitems
URL-voorvoegsel telkens wanneer er een eindpunt wordt ingesteld. API's hebben vaak groepen eindpunten met een gemeenschappelijk URL-voorvoegsel en de MapGroup methode is beschikbaar om dergelijke groepen te organiseren. Het vermindert terugkerende code en maakt het mogelijk om hele groepen eindpunten aan te passen met één aanroep naar methoden zoals RequireAuthorization en WithMetadata.
Vervang de inhoud van Program.cs
door de volgende code:
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();
De voorgaande code heeft de volgende wijzigingen:
- Voegt
var todoItems = app.MapGroup("/todoitems");
toe om de groep in te stellen met behulp van het URL-voorvoegsel/todoitems
. - Hiermee worden alle
app.Map<HttpVerb>
methoden gewijzigd intodoItems.Map<HttpVerb>
. - Hiermee verwijdert u het URL-voorvoegsel
/todoitems
uit de aanroepen van deMap<HttpVerb>
methode.
Test de eindpunten om te controleren of ze hetzelfde werken.
De TypedResults-API gebruiken
Het retourneren van TypedResults in plaats van Results heeft verschillende voordelen, waaronder testbaarheid en het automatisch retourneren van de metagegevens van het antwoordtype voor OpenAPI om het eindpunt te beschrijven. Voor meer informatie, zie TypedResults versus Results.
De Map<HttpVerb>
methoden kunnen methoden voor routehandlers aanroepen in plaats van lambdas te gebruiken. Als u een voorbeeld wilt zien, werkt u Program.cs bij met de volgende code:
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();
}
De Map<HttpVerb>
-code roept nu methoden aan in plaats van 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);
Deze methoden retourneren objecten die IResult implementeren en worden gedefinieerd door 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();
}
Eenheidstests kunnen deze methoden aanroepen en testen of ze het juiste type retourneren. Als de methode bijvoorbeeld is GetAllTodos
:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Code voor eenheidstests kan controleren of een object van het type OK<Todo[]> wordt geretourneerd door de handlermethode. Bijvoorbeeld:
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);
}
Overposten voorkomen
Op dit moment wordt in de voorbeeld-app het hele Todo
-object weergegeven. Productie-apps In productietoepassingen wordt vaak een subset van het model gebruikt om de gegevens te beperken die kunnen worden ingevoerd en geretourneerd. Er zijn meerdere redenen achter deze en beveiliging is een belangrijke. De subset van een model wordt meestal aangeduid als een DTO (Data Transfer Object), invoermodel of weergavemodel.
DTO- wordt in dit artikel gebruikt.
Een DTO kan worden gebruikt voor het volgende:
- Voorkom overboeking.
- Eigenschappen verbergen die klanten niet zouden moeten zien.
- Laat sommige eigenschappen weg om de nettolading te verkleinen.
- Platte objectgrafieken die geneste objecten bevatten. Platgemaakte objectgrafieken kunnen handiger zijn voor clients.
Als u de DTO-benadering wilt demonstreren, werkt u de Todo
klasse bij om een geheim veld op te nemen:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Het geheime veld moet worden verborgen voor deze app, maar een beheer-app kan ervoor kiezen om het beschikbaar te maken.
Controleer of u het geheime veld kunt posten en ophalen.
Maak een bestand met de naam TodoItemDTO.cs
met de volgende code:
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);
}
Vervang de inhoud van het Program.cs
-bestand door de volgende code om dit DTO-model te gebruiken:
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();
}
Controleer of u alle velden kunt posten en ophalen, behalve het geheime veld.
Problemen oplossen met het voltooide voorbeeld
Als u een probleem ondervindt dat u niet kunt oplossen, vergelijkt u de code met het voltooide project. Bekijk of download het voltooide project (hoe te downloaden).
Volgende stappen
- JSON-serialisatieopties configureren.
- Fouten en uitzonderingen afhandelen: de uitzonderingspagina voor ontwikkelaars is standaard ingeschakeld in de ontwikkelomgeving voor minimale API-apps. Zie Fouten verwerken in ASP.NET Core-API'svoor informatie over het afhandelen van fouten en uitzonderingen.
- Zie dit GitHub-voorbeeldvoor een voorbeeld van het testen van een minimale API-app.
- OpenAPI-ondersteuning in minimale API's.
- Quickstart: Publiceren op Azure.
- Het Organiseren van ASP.NET Core Minimale API's.
Meer informatie
Minimale API's zijn ontworpen om HTTP-API's te maken met minimale afhankelijkheden. Ze zijn ideaal voor microservices en apps die alleen de minimale bestanden, functies en afhankelijkheden in ASP.NET Core willen opnemen.
In deze zelfstudie leert u de basisbeginselen van het bouwen van een minimale API met ASP.NET Core. Een andere benadering voor het maken van API's in ASP.NET Core is het gebruik van controllers. Zie het overzicht van API'svoor hulp bij het kiezen tussen minimale API's en api's op basis van een controller. Zie Een web-API-maken voor een handleiding over het creëren van een API-project gebaseerd op controllers met meer functionaliteiten.
Overzicht
In deze handleiding maakt u de volgende API:
API | Beschrijving | Aanvraaginhoud | Hoofdtekst van antwoord |
---|---|---|---|
GET /todoitems |
Haal alle to-do items op. | Geen | Matrix van to-do items |
GET /todoitems/complete |
Voltooide to-do items ophalen | Geen | Matrix van to-do items |
GET /todoitems/{id} |
Een item ophalen via ID | Geen | Actiepunt |
POST /todoitems |
Een nieuw item toevoegen | Actiepunt | Takenitem |
PUT /todoitems/{id} |
Een bestaand item bijwerken | Takenitem | Geen |
DELETE /todoitems/{id} |
Een item verwijderen | Geen | Geen |
Voorwaarden
Visual Studio 2022 met de ASP.NET- en webontwikkelingsworkload.
Een API-project maken
Start Visual Studio 2022 en selecteer Een nieuw project maken.
In het dialoogvenster Een nieuw project maken:
- Typ
Empty
in het zoekvak Zoeken naar sjablonen. - Selecteer de sjabloon ASP.NET Core Empty en selecteer Volgende.
- Typ
Geef het project de naam TodoApi- en selecteer Volgende.
In het dialoogvenster Aanvullende informatie:
- Selecteer .NET 7.0-
- Schakel Gebruik geen instructies op het hoogste niveau uit
- Selecteer Creëer
De code onderzoeken
Het bestand Program.cs
bevat de volgende code:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
De voorgaande code:
- Hiermee maakt u een WebApplicationBuilder en een WebApplication met vooraf geconfigureerde standaardinstellingen.
- Hiermee maakt u een HTTP GET-eindpunt
/
datHello World!
retourneert:
De app uitvoeren
Druk op Ctrl+F5 om uit te voeren zonder het foutopsporingsprogramma.
Visual Studio geeft het volgende dialoogvenster weer:
Selecteer Ja als u het IIS Express SSL-certificaat vertrouwt.
Het volgende dialoogvenster wordt weergegeven:
Selecteer Ja als u akkoord gaat met het vertrouwen van het ontwikkelingscertificaat.
Zie Firefox SEC_ERROR_INADEQUATE_KEY_USAGE certificaatfoutvoor meer informatie over het vertrouwen van de Firefox-browser.
Visual Studio start de Kestrel webserver en opent een browservenster.
Hello World!
wordt weergegeven in de browser. Het bestand Program.cs
bevat een minimale maar volledige app.
NuGet-pakketten toevoegen
NuGet-pakketten moeten worden toegevoegd ter ondersteuning van de database en diagnostische gegevens die in deze zelfstudie worden gebruikt.
- Selecteer in het menu ToolsNuGet Package Manager > NuGet-pakketten beheren voor Solution.
- Selecteer het tabblad Bladeren.
- Voer Microsoft.EntityFrameworkCore.InMemory- in het zoekvak in en selecteer vervolgens
Microsoft.EntityFrameworkCore.InMemory
. - Selecteer het selectievakje Project in het rechterdeelvenster.
- Selecteer in het dropdownmenu versie de meest recente beschikbare versie 7, bijvoorbeeld
7.0.17
, en selecteer vervolgens Installeren. - Volg de voorgaande instructies om het
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
-pakket toe te voegen met de nieuwste versie 7 die beschikbaar is.
De contextklassen van het model en de database
Maak in de projectmap een bestand met de naam Todo.cs
met de volgende code:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Met de voorgaande code wordt het model voor deze app gemaakt. Een model is een klasse die gegevens vertegenwoordigt die door de app worden beheerd.
Maak een bestand met de naam TodoDb.cs
met de volgende code:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
De voorgaande code definieert de databasecontext, de belangrijkste klasse die de functionaliteit van Entity Framework coördineert voor een gegevensmodel. Deze klasse is afgeleid van de klasse Microsoft.EntityFrameworkCore.DbContext.
De API-code toevoegen
Vervang de inhoud van het bestand Program.cs
door de volgende code:
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();
Met de volgende gemarkeerde code wordt de databasecontext toegevoegd aan de afhankelijkheidsinjectie (DI) container en worden databasegerelateerde uitzonderingen weergegeven:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
De DI-container biedt toegang tot de databasecontext en andere services.
API-testinterface maken met Swagger
Er zijn veel beschikbare hulpprogramma's voor het testen van web-API's waaruit u kunt kiezen en u kunt de inleidende API-teststappen van deze zelfstudie volgen met uw eigen favoriete hulpprogramma.
In deze zelfstudie wordt gebruikgemaakt van het .NET-pakket NSwag.AspNetCore, waarmee Swagger-hulpprogramma's worden geïntegreerd voor het genereren van een testgebruikersinterface die voldoet aan de OpenAPI-specificatie:
- NSwag: Een .NET-bibliotheek die Swagger rechtstreeks integreert in ASP.NET Core-toepassingen, waardoor middleware en configuratie worden geboden.
- Swagger: Een set opensource-hulpprogramma's zoals OpenAPIGenerator en SwaggerUI die API-testpagina's genereren die voldoen aan de OpenAPI-specificatie.
- OpenAPI-specificatie: een document dat de mogelijkheden van de API beschrijft, op basis van de XML- en kenmerkaantekeningen binnen de controllers en modellen.
Zie ASP.NET Core-web-API-documentatie met Swagger/OpenAPI-voor meer informatie over het gebruik van OpenAPI en NSwag met ASP.NET.
Swagger-hulpmiddelen installeren
Voer de volgende opdracht uit:
dotnet add package NSwag.AspNetCore
Met de vorige opdracht wordt het pakket NSwag.AspNetCore toegevoegd, dat hulpprogramma's bevat voor het genereren van Swagger-documenten en de gebruikersinterface.
Swagger-middleware configureren
Voeg de volgende gemarkeerde code toe voordat
app
is gedefinieerd in regelvar 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();
In de vorige code:
builder.Services.AddEndpointsApiExplorer();
: hiermee schakelt u de API Explorer in. Dit is een service die metagegevens over de HTTP-API biedt. De API Explorer wordt door Swagger gebruikt om het Swagger-document te genereren.builder.Services.AddOpenApiDocument(config => {...});
: voegt de Swagger OpenAPI-documentgenerator toe aan de toepassingsservices en configureert deze voor meer informatie over de API, zoals de titel en versie. Zie Aan de slag met NSwag en ASP.NET Core voor meer informatie over het bieden van krachtigere API-detailsVoeg de volgende gemarkeerde code toe aan de volgende regel nadat
app
is gedefinieerd in regelvar 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"; }); }
Met de vorige code wordt de Swagger middleware ingeschakeld voor het leveren van het gegenereerde JSON-document en de Swagger-gebruikersinterface. Swagger is alleen ingeschakeld in een ontwikkelomgeving. Het inschakelen van Swagger in een productieomgeving kan mogelijk gevoelige details over de structuur en implementatie van de API beschikbaar maken.
Testgegevens plaatsen
Met de volgende code in Program.cs
wordt een HTTP POST-eindpunt gemaakt /todoitems
waarmee gegevens worden toegevoegd aan de in-memory database:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
Voer de app uit. In de browser wordt een 404-fout weergegeven omdat er geen /
eindpunt meer is.
Het POST-eindpunt wordt gebruikt om gegevens toe te voegen aan de app.
Wanneer de app nog steeds wordt uitgevoerd, gaat u in de browser naar
https://localhost:<port>/swagger
om de API-testpagina weer te geven die is gegenereerd door Swagger.Selecteer op de testpagina van de Swagger-API Post /todoitems>Probeer het.
Houd er rekening mee dat het veld aanvraagbody een gegenereerde voorbeeldindeling bevat die de parameters voor de API weergeeft.
Voer in de aanvraagbody JSON in voor een to-do item, zonder de optionele
id
op te geven:{ "name":"walk dog", "isComplete":true }
Selecteer en voeruit.
Swagger biedt een deelvenster Antwoorden onder de knop Uitvoeren.
Let op een paar nuttige details:
- cURL: Swagger biedt een voorbeeld van een cURL-opdracht in unix-/Linux-syntaxis, die kan worden uitgevoerd op de opdrachtregel met elke bash-shell die gebruikmaakt van Unix/Linux-syntaxis, waaronder Git Bash vanuit Git voor Windows.
- Aanvraag-URL: Een vereenvoudigde weergave van de HTTP-aanvraag die is gedaan door de JavaScript-code van Swagger UI voor de API-aanroep. Werkelijke aanvragen kunnen details bevatten, zoals headers en queryparameters en een aanvraagbody.
- Serverantwoord: bevat de hoofdtekst en headers van het antwoord. In de hoofdtekst van het antwoord ziet u dat de
id
is ingesteld op1
. - Antwoordcode: Een 201
HTTP
statuscode is geretourneerd, wat aangeeft dat de aanvraag is verwerkt en heeft geresulteerd in het maken van een nieuwe resource.
De GET-eindpunten onderzoeken
De voorbeeld-app implementeert verschillende GET-eindpunten door MapGet
aan te roepen:
API | Beschrijving | Inhoud van het verzoek | Hoofdtekst van antwoord |
---|---|---|---|
GET /todoitems |
Alle to-do items ophalen | Geen | Matrix van to-do items |
GET /todoitems/complete |
Alle voltooide to-do items ophalen | Geen | Matrix van to-do items |
GET /todoitems/{id} |
Een item ophalen op id | Geen | Takenitem |
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());
De GET-eindpunten testen
Test de app door de eindpunten vanuit een browser of Swagger aan te roepen.
Selecteer in Swagger GET /todoitems>Probeer het uit>Uitvoeren.
U kunt ook GET /todoitems vanuit een browser aanroepen door de URI-
http://localhost:<port>/todoitems
in te voeren. Bijvoorbeeldhttp://localhost:5001/todoitems
De aanroep van GET /todoitems
produceert een antwoord dat er ongeveer als volgt uitziet:
[
{
"id": 1,
"name": "walk dog",
"isComplete": true
}
]
Roep GET /todoitems/{id} in Swagger aan om gegevens van een specifieke id te retourneren:
- Selecteer GET /todoitems>Probeer het uit.
- Stel het veld id in op
1
en selecteer uitvoeren.
U kunt ook GET /todoitems vanuit een browser aanroepen door de URI-
https://localhost:<port>/todoitems/1
in te voeren. Bijvoorbeeldhttps://localhost:5001/todoitems/1
Het antwoord is vergelijkbaar met het volgende:
{ "id": 1, "name": "walk dog", "isComplete": true }
Deze app maakt gebruik van een in-memory database. Als de app opnieuw wordt opgestart, retourneert de GET-aanvraag geen gegevens. Als er geen gegevens worden geretourneerd, POST gegevens naar de app en probeer de GET-aanvraag opnieuw.
Retourwaarden
ASP.NET Core het object automatisch serialiseert naar JSON- en schrijft de JSON naar de hoofdtekst van het antwoordbericht. De antwoordcode voor dit retourtype is 200 OK, ervan uitgaande dat er geen onverwerkte uitzonderingen zijn. Niet-verwerkte uitzonderingen worden omgezet in 5xx-fouten.
De retourtypen kunnen een breed scala aan HTTP-statuscodes vertegenwoordigen.
GET /todoitems/{id}
kan bijvoorbeeld twee verschillende statuswaarden retourneren:
- Als er geen item overeenkomt met de aangevraagde id, retourneert de methode een 404-statusNotFound foutcode.
- Anders retourneert de methode 200 met een JSON-antwoordtekst. Het retourneren van
item
resulteert in een HTTP 200-antwoord.
Het PUT-eindpunt onderzoeken
De voorbeeld-app implementeert één PUT-eindpunt met behulp van 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();
});
Deze methode is vergelijkbaar met de methode MapPost
, met uitzondering van HTTP PUT. Een geslaagd antwoord retourneert 204 (Geen inhoud). Volgens de HTTP-specificatie vereist een PUT-aanvraag dat de client de volledige bijgewerkte entiteit verzendt, niet alleen de wijzigingen. Gebruik HTTP PATCH-om gedeeltelijke updates te ondersteunen.
Het PUT-eindpunt testen
In dit voorbeeld wordt een in-memory database gebruikt die telkens wanneer de app wordt gestart, moet worden geïnitialiseerd. Er moet een item in de database staan voordat u een PUT-aanroep uitvoert. Roep GET aan om ervoor te zorgen dat er een item in de database staat voordat u een PUT-aanroep doet.
Werk het to-do item met Id = 1
bij en stel de naam ervan in op "feed fish"
.
Gebruik Swagger om een PUT-aanvraag te verzenden:
Selecteer /todoitems/{id}>probeer het.
Stel het veld id in op
1
.Stel de aanvraagbody in op de volgende JSON:
{ "name": "feed fish", "isComplete": false }
Selecteer Voeruit.
Het DELETE-eindpunt onderzoeken en testen
De voorbeeld-app implementeert één DELETE-eindpunt met behulp van 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();
});
Gebruik Swagger om een DELETE-aanvraag te verzenden:
Selecteer DELETE /todoitems/{id}>probeer het.
Stel het veld id in op
1
en selecteer uitvoeren.De DELETE-aanvraag wordt verzonden naar de app en het antwoord wordt weergegeven in het deelvenster Antwoorden. De hoofdtekst van het antwoord is leeg en het Server-antwoord statuscode is 204.
De MapGroup-API gebruiken
De code van de voorbeeld-app herhaalt het todoitems
URL-voorvoegsel telkens wanneer er een eindpunt wordt ingesteld. API's hebben vaak groepen eindpunten met een gemeenschappelijk URL-voorvoegsel en de MapGroup methode is beschikbaar om dergelijke groepen te organiseren. Het vermindert terugkerende code en maakt het mogelijk om hele groepen eindpunten aan te passen met één aanroep naar methoden zoals RequireAuthorization en WithMetadata.
Vervang de inhoud van Program.cs
door de volgende code:
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();
De voorgaande code heeft de volgende wijzigingen:
- Voegt
var todoItems = app.MapGroup("/todoitems");
toe om de groep in te stellen met behulp van het URL-voorvoegsel/todoitems
. - Hiermee worden alle
app.Map<HttpVerb>
methoden gewijzigd intodoItems.Map<HttpVerb>
. - Hiermee verwijdert u het URL-voorvoegsel
/todoitems
uit de aanroepen van deMap<HttpVerb>
methode.
Test de eindpunten om te controleren of ze hetzelfde werken.
De TypedResults-API gebruiken
Het retourneren van TypedResults in plaats van Results heeft verschillende voordelen, waaronder testbaarheid en het automatisch retourneren van de metagegevens van het antwoordtype voor OpenAPI om het eindpunt te beschrijven. Voor meer informatie, zie TypedResults versus Results.
De Map<HttpVerb>
methoden kunnen methoden voor routehandlers aanroepen in plaats van lambdas te gebruiken. Als u een voorbeeld wilt zien, werkt u Program.cs bij met de volgende code:
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();
}
De Map<HttpVerb>
-code roept nu methoden aan in plaats van 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);
Deze methoden retourneren objecten die IResult implementeren en worden gedefinieerd door 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();
}
Eenheidstests kunnen deze methoden aanroepen en testen of ze het juiste type retourneren. Als de methode bijvoorbeeld is GetAllTodos
:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Code voor eenheidstests kan controleren of een object van het type OK<Todo[]> wordt geretourneerd door de handlermethode. Bijvoorbeeld:
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);
}
Overmatig posten voorkomen
Op dit moment wordt in de voorbeeld-app het hele Todo
-object weergegeven. Productie-apps In productietoepassingen wordt vaak een subset van het model gebruikt om de gegevens te beperken die kunnen worden ingevoerd en geretourneerd. Er zijn meerdere redenen achter deze en beveiliging is een belangrijke. De subset van een model wordt meestal aangeduid als een DTO (Data Transfer Object), invoermodel of weergavemodel.
DTO- wordt in dit artikel gebruikt.
Een DTO kan worden gebruikt voor het volgende:
- Voorkom te veel berichten plaatsen
- Eigenschappen verbergen die klanten niet mogen zien.
- Laat sommige eigenschappen weg om de nettolading te verkleinen.
- Platte objectgrafieken die geneste objecten bevatten. Platgemaakte objectgrafieken kunnen handiger zijn voor clients.
Als u de DTO-benadering wilt demonstreren, werkt u de Todo
klasse bij om een geheim veld op te nemen:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Het geheime veld moet worden verborgen voor deze app, maar een beheer-app kan ervoor kiezen om het beschikbaar te maken.
Controleer of u het geheime veld kunt posten en ophalen.
Maak een bestand met de naam TodoItemDTO.cs
met de volgende code:
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);
}
Vervang de inhoud van het Program.cs
-bestand door de volgende code om dit DTO-model te gebruiken:
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>();
}
Controleer of u alle velden kunt posten en ophalen, behalve het geheime veld.
Problemen oplossen met het voltooide voorbeeld
Als u een probleem ondervindt dat u niet kunt oplossen, vergelijkt u de code met het voltooide project. Voltooid project weergeven of downloaden (downloaden van).
Volgende stappen
- JSON-serialisatieopties configureren.
- Fouten en uitzonderingen afhandelen: de uitzonderingspagina voor ontwikkelaars is standaard ingeschakeld in de ontwikkelomgeving voor minimale API-apps. Zie Fouten verwerken in ASP.NET Core-API'svoor informatie over het afhandelen van fouten en uitzonderingen.
- Zie dit GitHub-voorbeeldvoor een voorbeeld van het testen van een minimale API-app.
- OpenAPI-ondersteuning in minimale API's.
- Quickstart: Publiceren op Azure.
- ASP.NET Core Minimale API'sorganiseren.
Meer informatie
Minimale API's zijn ontworpen om HTTP-API's te maken met minimale afhankelijkheden. Ze zijn ideaal voor microservices en apps die alleen de minimale bestanden, functies en afhankelijkheden in ASP.NET Core willen opnemen.
In deze zelfstudie leert u de basisbeginselen van het bouwen van een minimale API met ASP.NET Core. Een andere benadering voor het maken van API's in ASP.NET Core is het gebruik van controllers. Zie het overzicht van API'svoor hulp bij het kiezen tussen minimale API's en api's op basis van een controller. Zie voor een tutorial over het maken van een API-project, gebaseerd op -controllers die meer functies bevatten, Een web-APImaken.
Overzicht
In deze handleiding gaan we de volgende API maken:
API | Beschrijving | Aanvraagtekst | Hoofdtekst van antwoord |
---|---|---|---|
GET /todoitems |
Alle to-do items ophalen | Geen | Matrix van to-do items |
GET /todoitems/complete |
Haal voltooide to-do items op | Geen | Matrix van to-do items |
GET /todoitems/{id} |
Een item ophalen met ID | Geen | Takenitem |
POST /todoitems |
Een nieuw item toevoegen | Takenitem | Takenitem |
PUT /todoitems/{id} |
Een bestaand item bijwerken | Takenitem | Geen |
DELETE /todoitems/{id} |
Een item verwijderen | Geen | Geen |
Voorwaarden
- Visual Studio 2022 met de ASP.NET- en webontwikkelingsworkload.
- .NET 6.0 SDK
Een API-project maken
Start Visual Studio 2022 en selecteer Een nieuw project maken.
In het dialoogvenster Een nieuw project maken:
- Typ
Empty
in het zoekvak Zoeken naar sjablonen. - Selecteer de ASP.NET Core Empty-sjabloon en kies Volgende.
- Typ
Geef het project de naam TodoApi- en selecteer Volgende.
In het dialoogvenster Aanvullende informatie:
- Selecteer .NET 6.0
- Deselecteer Gebruik geen top-level statements
- Selecteer Creëer
De code onderzoeken
Het bestand Program.cs
bevat de volgende code:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
De voorgaande code:
- Hiermee maakt u een WebApplicationBuilder en een WebApplication met vooraf geconfigureerde standaardinstellingen.
- Hiermee maakt u een HTTP GET-eindpunt
/
datHello World!
retourneert:
De app uitvoeren
Druk op Ctrl+F5 om uit te voeren zonder het foutopsporingsprogramma.
Visual Studio geeft het volgende dialoogvenster weer:
Selecteer Ja als u het IIS Express SSL-certificaat vertrouwt.
Het volgende dialoogvenster wordt weergegeven:
Selecteer Ja als u akkoord gaat met het vertrouwen van het ontwikkelingscertificaat.
Zie Firefox SEC_ERROR_INADEQUATE_KEY_USAGE certificaatfoutvoor meer informatie over het vertrouwen van de Firefox-browser.
Visual Studio start de Kestrel webserver en opent een browservenster.
Hello World!
wordt weergegeven in de browser. Het bestand Program.cs
bevat een minimale maar volledige app.
NuGet-pakketten toevoegen
NuGet-pakketten moeten worden toegevoegd ter ondersteuning van de database en diagnostische gegevens die in deze zelfstudie worden gebruikt.
- Selecteer in het menu ToolsNuGet Package Manager > NuGet-pakketten beheren voor Solution.
- Selecteer het tabblad Bladeren.
- Voer Microsoft.EntityFrameworkCore.InMemory- in het zoekvak in en selecteer vervolgens
Microsoft.EntityFrameworkCore.InMemory
. - Selecteer in het rechterdeelvenster het selectievakje Project.
- Selecteer in de vervolgkeuzelijst versie de meest recente versie 7, bijvoorbeeld
6.0.28
, en selecteer vervolgens Installeren. - Volg de voorgaande instructies om het
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
-pakket toe te voegen met de nieuwste versie 7 die beschikbaar is.
De contextklassen van het model en de database
Maak in de projectmap een bestand met de naam Todo.cs
met de volgende code:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Met de voorgaande code wordt het model voor deze app gemaakt. Een model is een klasse die gegevens vertegenwoordigt die door de app worden beheerd.
Maak een bestand met de naam TodoDb.cs
met de volgende code:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
De voorgaande code definieert de databasecontext, die als hoofdklasse de functionaliteit van Entity Framework coördineert voor een gegevensmodel. Deze klasse is afgeleid van de klasse Microsoft.EntityFrameworkCore.DbContext.
De API-code toevoegen
Vervang de inhoud van het bestand Program.cs
door de volgende code:
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();
Met de volgende gemarkeerde code wordt de databasecontext toegevoegd aan de afhankelijkheidsinjectie (DI) container en worden databasegerelateerde uitzonderingen weergegeven:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
De DI-container biedt toegang tot de databasecontext en andere services.
API-testinterface maken met Swagger
Er zijn veel beschikbare hulpprogramma's voor het testen van web-API's waaruit u kunt kiezen en u kunt de inleidende API-teststappen van deze zelfstudie volgen met uw eigen favoriete hulpprogramma.
In deze zelfstudie wordt gebruikgemaakt van het .NET-pakket NSwag.AspNetCore, waarmee Swagger-hulpprogramma's worden geïntegreerd voor het genereren van een testgebruikersinterface die voldoet aan de OpenAPI-specificatie:
- NSwag: Een .NET-bibliotheek die Swagger rechtstreeks integreert in ASP.NET Core-toepassingen, waardoor middleware en configuratie worden geboden.
- Swagger: Een set opensource-hulpprogramma's zoals OpenAPIGenerator en SwaggerUI die API-testpagina's genereren die voldoen aan de OpenAPI-specificatie.
- OpenAPI-specificatie: een document dat de mogelijkheden van de API beschrijft, op basis van de XML- en kenmerkaantekeningen binnen de controllers en modellen.
Zie ASP.NET Core-web-API-documentatie met Swagger/OpenAPI-voor meer informatie over het gebruik van OpenAPI en NSwag met ASP.NET.
Swagger-hulpprogramma's installeren
Voer de volgende opdracht uit:
dotnet add package NSwag.AspNetCore
Met de vorige opdracht wordt het pakket NSwag.AspNetCore toegevoegd, dat hulpprogramma's bevat voor het genereren van Swagger-documenten en de gebruikersinterface.
Swagger-middleware configureren
Voeg in Program.cs bovenaan de volgende
using
instructies toe:using NSwag.AspNetCore;
Voeg de hieronder gemarkeerde code toe voordat
app
wordt gedefinieerd in regelvar 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();
In de vorige code:
builder.Services.AddEndpointsApiExplorer();
: hiermee schakelt u de API Explorer in. Dit is een service die metagegevens over de HTTP-API biedt. De API Explorer wordt door Swagger gebruikt om het Swagger-document te genereren.builder.Services.AddOpenApiDocument(config => {...});
: voegt de Swagger OpenAPI-documentgenerator toe aan de toepassingsservices en configureert deze voor meer informatie over de API, zoals de titel en versie. Zie Aan de slag met NSwag en ASP.NET Core voor meer informatie over het bieden van krachtigere API-detailsVoeg de volgende gemarkeerde code toe aan de volgende regel nadat
app
is gedefinieerd in regelvar 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"; }); }
Met de vorige code wordt de Swagger middleware ingeschakeld voor het leveren van het gegenereerde JSON-document en de Swagger-gebruikersinterface. Swagger is alleen ingeschakeld in een ontwikkelomgeving. Het inschakelen van Swagger in een productieomgeving kan mogelijk gevoelige details over de structuur en implementatie van de API beschikbaar maken.
Testgegevens plaatsen
Met de volgende code in Program.cs
wordt een HTTP POST-eindpunt gemaakt /todoitems
waarmee gegevens worden toegevoegd aan de in-memory database:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
Voer de app uit. In de browser wordt een 404-fout weergegeven omdat er geen /
eindpunt meer is.
Het POST-eindpunt wordt gebruikt om gegevens toe te voegen aan de app.
Wanneer de app nog steeds wordt uitgevoerd, gaat u in de browser naar
https://localhost:<port>/swagger
om de API-testpagina weer te geven die is gegenereerd door Swagger.Selecteer op de testpagina van de Swagger-API Post /todoitems>Probeer het.
Houd er rekening mee dat het veld aanvraagbody een gegenereerde voorbeeldindeling bevat die de parameters voor de API weergeeft.
Voer in de aanvraagbody JSON in voor een to-do item, zonder de optionele
id
op te geven:{ "name":"walk dog", "isComplete":true }
Selecteer Voeruit.
Swagger biedt een paneel Antwoorden onder de knop Uitvoeren.
Let op een paar nuttige details:
- cURL: Swagger biedt een voorbeeld van een cURL-opdracht in unix-/Linux-syntaxis, die kan worden uitgevoerd op de opdrachtregel met elke bash-shell die gebruikmaakt van Unix/Linux-syntaxis, waaronder Git Bash vanuit Git voor Windows.
- Aanvraag-URL: Een vereenvoudigde weergave van de HTTP-aanvraag die is gedaan door de JavaScript-code van Swagger UI voor de API-aanroep. Werkelijke aanvragen kunnen details bevatten, zoals headers en queryparameters en een aanvraagbody.
- Serverantwoord: bevat de hoofdtekst en headers van het antwoord. In de hoofdtekst van het antwoord ziet u dat de
id
is ingesteld op1
. - Antwoordcode: Een 201
HTTP
statuscode is geretourneerd, wat aangeeft dat de aanvraag is verwerkt en heeft geresulteerd in het maken van een nieuwe resource.
De GET-eindpunten onderzoeken
De voorbeeld-app implementeert verschillende GET-eindpunten door MapGet
aan te roepen:
API | Beschrijving | Aanvraaginhoud | Hoofdtekst van antwoord |
---|---|---|---|
GET /todoitems |
Alle to-do items ophalen | Geen | Matrix van to-do items |
GET /todoitems/complete |
Alle voltooide to-do items ophalen | Geen | Matrix van to-do items |
GET /todoitems/{id} |
Een item ophalen op basis van ID | Geen | Takenitem |
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());
De GET-eindpunten testen
Test de app door de eindpunten vanuit een browser of Swagger aan te roepen.
Selecteer in Swagger GET /todoitems>Uitproberen>Uitvoeren.
U kunt ook GET /todoitems vanuit een browser aanroepen door de URI-
http://localhost:<port>/todoitems
in te voeren. Bijvoorbeeldhttp://localhost:5001/todoitems
De aanroep van GET /todoitems
produceert een antwoord dat er ongeveer als volgt uitziet:
[
{
"id": 1,
"name": "walk dog",
"isComplete": true
}
]
Roep GET /todoitems/{id} in Swagger aan om gegevens van een specifieke id te retourneren:
- Selecteer GET /todoitems>Probeer het uit.
- Stel het veld id in op
1
en selecteer uitvoeren.
U kunt ook GET /todoitems vanuit een browser aanroepen door de URI-
https://localhost:<port>/todoitems/1
in te voeren. Bijvoorbeeld, bijvoorbeeldhttps://localhost:5001/todoitems/1
Het antwoord is vergelijkbaar met het volgende:
{ "id": 1, "name": "walk dog", "isComplete": true }
Deze app maakt gebruik van een in-memory database. Als de app opnieuw wordt opgestart, retourneert de GET-aanvraag geen gegevens. Als er geen gegevens worden geretourneerd, POST gegevens naar de app en probeer de GET-aanvraag opnieuw.
Retourwaarden
ASP.NET Core het object automatisch serialiseert naar JSON- en schrijft de JSON naar de hoofdtekst van het antwoordbericht. De antwoordcode voor dit retourtype is 200 OK, ervan uitgaande dat er geen onverwerkte uitzonderingen zijn. Niet-verwerkte uitzonderingen worden omgezet in 5xx-fouten.
De retourtypen kunnen een breed scala aan HTTP-statuscodes vertegenwoordigen.
GET /todoitems/{id}
kan bijvoorbeeld twee verschillende statuswaarden retourneren:
- Als er geen item overeenkomt met de aangevraagde id, retourneert de methode een 404-statusNotFound foutcode.
- Anders retourneert de methode 200 met een JSON-antwoordtekst. Het retourneren van
item
resulteert in een HTTP 200-antwoord.
Het PUT-eindpunt onderzoeken
De voorbeeld-app implementeert één PUT-eindpunt met behulp van 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();
});
Deze methode is vergelijkbaar met de methode MapPost
, met uitzondering van HTTP PUT. Een geslaagd antwoord retourneert 204 (Geen inhoud). Volgens de HTTP-specificatie vereist een PUT-aanvraag dat de client de volledige bijgewerkte entiteit verzendt, niet alleen de wijzigingen. Gebruik HTTP PATCH-om gedeeltelijke updates te ondersteunen.
Het PUT-eindpunt testen
In dit voorbeeld wordt een in-memory database gebruikt die telkens wanneer de app wordt gestart, moet worden geïnitialiseerd. Er moet een item in de database staan voordat u een PUT-aanroep uitvoert. Roep GET aan om ervoor te zorgen dat er een item in de database staat voordat u een PUT-aanroep doet.
Werk het to-do item met Id = 1
bij en stel de naam ervan in op "feed fish"
.
Gebruik Swagger om een PUT-aanvraag te verzenden:
Selecteer /todoitems/{id}>probeer het.
Stel het veld id in op
1
.Stel de aanvraagbody in op de volgende JSON:
{ "name": "feed fish", "isComplete": false }
Selecteer Voeruit.
Het DELETE-eindpunt onderzoeken en testen
De voorbeeld-app implementeert één DELETE-eindpunt met behulp van 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();
});
Gebruik Swagger om een DELETE-aanvraag te verzenden:
Selecteer DELETE /todoitems/{id}>probeer het.
Stel het veld id in op
1
en selecteer uitvoeren.De DELETE-aanvraag wordt verzonden naar de app en het antwoord wordt weergegeven in het deelvenster Antwoorden. De hoofdtekst van het antwoord is leeg en het Server-antwoord statuscode is 204.
Overboeking voorkomen
Op dit moment wordt in de voorbeeld-app het hele Todo
-object weergegeven. Productie-apps In productietoepassingen wordt vaak een subset van het model gebruikt om de gegevens te beperken die kunnen worden ingevoerd en geretourneerd. Er zijn meerdere redenen achter deze en beveiliging is een belangrijke. De subset van een model wordt meestal aangeduid als een DTO (Data Transfer Object), invoermodel of weergavemodel.
DTO- wordt in dit artikel gebruikt.
Een DTO kan worden gebruikt voor het volgende:
- Voorkom overboeking.
- Eigenschappen verbergen die klanten niet mogen zien.
- Laat sommige eigenschappen weg om de nettolading te verkleinen.
- Platte objectgrafieken die geneste objecten bevatten. Platgemaakte objectgrafieken kunnen handiger zijn voor clients.
Als u de DTO-benadering wilt demonstreren, werkt u de Todo
klasse bij om een geheim veld op te nemen:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Het geheime veld moet worden verborgen voor deze app, maar een beheer-app kan ervoor kiezen om het beschikbaar te maken.
Controleer of u het geheime veld kunt posten en ophalen.
Maak een bestand met de naam TodoItemDTO.cs
met de volgende code:
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);
}
Vervang de inhoud van het Program.cs
-bestand door de volgende code om dit DTO-model te gebruiken:
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>();
}
Controleer of u alle velden kunt posten en ophalen, behalve het geheime veld.
Minimale API testen
Zie dit GitHub-voorbeeldvoor een voorbeeld van het testen van een minimale API-app.
Publiceren naar Azure
Zie Quickstart: Een ASP.NET-web-app implementerenvoor meer informatie over het implementeren in Azure.
Aanvullende informatiebronnen
Minimale API's zijn ontworpen om HTTP-API's te maken met minimale afhankelijkheden. Ze zijn ideaal voor microservices en apps die alleen de minimale bestanden, functies en afhankelijkheden in ASP.NET Core willen opnemen.
In deze zelfstudie leert u de basisbeginselen van het bouwen van een minimale API met ASP.NET Core. Een andere benadering voor het maken van API's in ASP.NET Core is het gebruik van controllers. Zie het overzicht van API'svoor hulp bij het kiezen tussen minimale API's en api's op basis van een controller. Zie voor een zelfstudie over het maken van een API-project gebaseerd op controllers met meer functies, Een web-API maken.
Overzicht
In deze tutorial wordt de volgende API gemaakt:
API | Beschrijving | Aanvraagtekst | Hoofdtekst van antwoord |
---|---|---|---|
GET /todoitems |
Alle to-do items ophalen | Geen | Matrix van to-do items |
GET /todoitems/complete |
Voltooide to-do items verkrijgen | Geen | Matrix van to-do items |
GET /todoitems/{id} |
Een item ophalen met ID | Geen | Takenitem |
POST /todoitems |
Een nieuw item toevoegen | Actiepunt | Takenitem |
PUT /todoitems/{id} |
Een bestaand item bijwerken | Actiepunt | Geen |
DELETE /todoitems/{id} |
Een item verwijderen | Geen | Geen |
Voorwaarden
Visual Studio 2022 met de workload voor ASP.NET en webontwikkeling.
Een API-project maken
Start Visual Studio 2022 en selecteer Een nieuw project maken.
In het dialoogvenster Een nieuw project aanmaken:
- Typ
Empty
in het zoekvak Zoeken naar sjablonen. - Selecteer de sjabloon ASP.NET Core Empty en selecteer Volgende.
- Typ
Geef het project de naam TodoApi- en selecteer Volgende.
In het dialoogvenster Aanvullende informatie:
- Selecteer .NET 8.0 (Long Term Support)
- Schakel Gebruik geen instructies op het hoogste niveau uit
- Selecteer Creëer
De code onderzoeken
Het bestand Program.cs
bevat de volgende code:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
De voorgaande code:
- Hiermee maakt u een WebApplicationBuilder en een WebApplication met vooraf geconfigureerde standaardinstellingen.
- Hiermee maakt u een HTTP GET-eindpunt
/
datHello World!
retourneert:
De app uitvoeren
Druk op Ctrl+F5 om uit te voeren zonder het foutopsporingsprogramma.
Visual Studio geeft het volgende dialoogvenster weer:
Selecteer Ja als u het IIS Express SSL-certificaat vertrouwt.
Het volgende dialoogvenster wordt weergegeven:
Selecteer Ja als u akkoord gaat met het vertrouwen van het ontwikkelingscertificaat.
Zie Firefox SEC_ERROR_INADEQUATE_KEY_USAGE certificaatfoutvoor meer informatie over het vertrouwen van de Firefox-browser.
Visual Studio start de Kestrel webserver en opent een browservenster.
Hello World!
wordt weergegeven in de browser. Het bestand Program.cs
bevat een minimale maar volledige app.
Sluit het browservenster.
NuGet-pakketten toevoegen
NuGet-pakketten moeten worden toegevoegd ter ondersteuning van de database en diagnostische gegevens die in deze zelfstudie worden gebruikt.
- Selecteer in het menu ToolsNuGet Package Manager > NuGet-pakketten beheren voor Solution.
- Selecteer het tabblad Bladeren.
- Voer Microsoft.EntityFrameworkCore.InMemory- in het zoekvak in en selecteer vervolgens
Microsoft.EntityFrameworkCore.InMemory
. - Vink het selectievakje Project aan in het rechterdeelvenster en selecteer dan Installeren.
- Volg de voorgaande instructies om het
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
-pakket toe te voegen.
De contextklassen van het model en de database
- Maak in de projectmap een bestand met de naam
Todo.cs
met de volgende code:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Met de voorgaande code wordt het model voor deze app gemaakt. Een model is een klasse die gegevens vertegenwoordigt die door de app worden beheerd.
- Maak een bestand met de naam
TodoDb.cs
met de volgende code:
using Microsoft.EntityFrameworkCore;
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
De voorgaande code definieert de databasecontext, de hoofdklasse die de functionaliteit van Entity Framework voor een gegevensmodel coördineert. Deze klasse is afgeleid van de klasse Microsoft.EntityFrameworkCore.DbContext.
De API-code toevoegen
- Vervang de inhoud van het bestand
Program.cs
door de volgende code:
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();
Met de volgende gemarkeerde code wordt de databasecontext toegevoegd aan de afhankelijkheidsinjectie (DI) container en worden databasegerelateerde uitzonderingen weergegeven:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
De DI-container biedt toegang tot de databasecontext en andere services.
In deze zelfstudie worden Endpoints Explorer en .http-bestanden gebruikt om de API te testen.
Boekingsgegevens testen
Met de volgende code in Program.cs
wordt een HTTP POST-eindpunt gemaakt /todoitems
waarmee gegevens worden toegevoegd aan de in-memory database:
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
Voer de app uit. In de browser wordt een 404-fout weergegeven omdat er geen /
eindpunt meer is.
Het POST-eindpunt wordt gebruikt om gegevens toe te voegen aan de app.
Selecteer Weergave>Andere Windows>Endpoints Explorer.
Klik met de rechtermuisknop op het eindpunt POST en selecteer Aanvraag genereren.
Er wordt een nieuw bestand gemaakt in de projectmap met de naam
TodoApi.http
, met inhoud die vergelijkbaar is met het volgende voorbeeld:@TodoApi_HostAddress = https://localhost:7031 Post {{TodoApi_HostAddress}}/todoitems ###
- Met de eerste regel maakt u een variabele die wordt gebruikt voor alle eindpunten.
- De volgende regel definieert een POST-aanvraag.
- De drievoudige hashtag (
###
) regel is een scheidingsteken voor aanvragen: wat erna komt, is bedoeld voor een andere aanvraag.
De POST-aanvraag heeft headers en een hoofdtekst nodig. Als u deze onderdelen van de aanvraag wilt definiëren, voegt u de volgende regels toe direct na de POST-aanvraagregel:
Content-Type: application/json { "name":"walk dog", "isComplete":true }
Met de voorgaande code worden een header voor het inhoudstype en een hoofdtekst van een JSON-aanvraag toegevoegd. Het todoApi.http-bestand moet er nu uitzien zoals in het volgende voorbeeld, maar met uw poortnummer:
@TodoApi_HostAddress = https://localhost:7057 Post {{TodoApi_HostAddress}}/todoitems Content-Type: application/json { "name":"walk dog", "isComplete":true } ###
Voer de app uit.
Selecteer de link Aanvraag verzenden die boven de
POST
aanvraagregel staat.De POST-aanvraag wordt verzonden naar de app en het antwoord wordt weergegeven in het deelvenster Antwoord.
De GET-eindpunten onderzoeken
De voorbeeld-app implementeert verschillende GET-eindpunten door MapGet
aan te roepen:
API | Beschrijving | Aanvraagbody | Hoofdtekst van antwoord |
---|---|---|---|
GET /todoitems |
Alle to-do artikelen ophalen | Geen | Matrix van to-do items |
GET /todoitems/complete |
Alle voltooide to-do items ophalen | Geen | Matrix van to-do items |
GET /todoitems/{id} |
Een item ophalen via ID | Geen | Takenitem |
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());
De GET-eindpunten testen
Test de app door de GET
-eindpunten aan te roepen vanuit een browser of met behulp van Endpoints Explorer. De volgende stappen zijn bedoeld voor Endpoints Explorer.
Klik in Endpoints Explorermet de rechtermuisknop op het eerste GET--eindpunt en selecteer Aanvraag genereren.
De volgende inhoud wordt toegevoegd aan het
TodoApi.http
-bestand:Get {{TodoApi_HostAddress}}/todoitems ###
Selecteer de koppeling Aanvraag verzenden die zich boven de nieuwe
GET
aanvraagregel bevindt.De GET-aanvraag wordt verzonden naar de app en het antwoord wordt weergegeven in het deelvenster Antwoord.
De hoofdtekst van het antwoord is vergelijkbaar met de volgende JSON:
[ { "id": 1, "name": "walk dog", "isComplete": true } ]
Klik in Endpoints Explorermet de rechtermuisknop op het eindpunt
/todoitems/{id}
GET en selecteer Aanvraag genereren. De volgende inhoud wordt toegevoegd aan hetTodoApi.http
-bestand:GET {{TodoApi_HostAddress}}/todoitems/{id} ###
Vervang
{id}
door1
.Selecteer de aanvraag verzenden koppeling die zich boven de nieuwe GET-aanvraagregel bevindt.
De GET-aanvraag wordt verzonden naar de app en het antwoord wordt weergegeven in het deelvenster Antwoord.
De hoofdtekst van het antwoord is vergelijkbaar met de volgende JSON:
{ "id": 1, "name": "walk dog", "isComplete": true }
Deze app maakt gebruik van een in-memory database. Als de app opnieuw wordt opgestart, retourneert de GET-aanvraag geen gegevens. Als er geen gegevens worden geretourneerd, POST gegevens naar de app en probeer de GET-aanvraag opnieuw.
Retourwaarden
ASP.NET Core het object automatisch serialiseert naar JSON- en schrijft de JSON naar de hoofdtekst van het antwoordbericht. De antwoordcode voor dit retourtype is 200 OK, ervan uitgaande dat er geen onverwerkte uitzonderingen zijn. Niet-verwerkte uitzonderingen worden omgezet in 5xx-fouten.
De retourtypen kunnen een breed scala aan HTTP-statuscodes vertegenwoordigen.
GET /todoitems/{id}
kan bijvoorbeeld twee verschillende statuswaarden retourneren:
- Als er geen item overeenkomt met de aangevraagde id, retourneert de methode een 404-statusNotFound foutcode.
- Anders retourneert de methode 200 met een JSON-antwoordtekst. Het retourneren van
item
resulteert in een HTTP 200-antwoord.
Het PUT-eindpunt onderzoeken
De voorbeeld-app implementeert één PUT-eindpunt met behulp van 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();
});
Deze methode is vergelijkbaar met de methode MapPost
, met uitzondering van HTTP PUT. Een geslaagd antwoord retourneert 204 (Geen inhoud). Volgens de HTTP-specificatie vereist een PUT-aanvraag dat de client de volledige bijgewerkte entiteit verzendt, niet alleen de wijzigingen. Gebruik HTTP PATCH-om gedeeltelijke updates te ondersteunen.
Het PUT-eindpunt testen
In dit voorbeeld wordt een in-memory database gebruikt die telkens wanneer de app wordt gestart, moet worden geïnitialiseerd. Er moet een item in de database staan voordat u een PUT-aanroep uitvoert. Roep GET aan om ervoor te zorgen dat er een item in de database staat voordat u een PUT-aanroep doet.
Werk het to-do item met Id = 1
bij en stel de naam ervan in op "feed fish"
.
Klik in Endpoints Explorermet de rechtermuisknop op het PUT-eindpunt en selecteer Aanvraag genereren.
De volgende inhoud wordt toegevoegd aan het
TodoApi.http
-bestand:Put {{TodoApi_HostAddress}}/todoitems/{id} ###
Vervang
{id}
in de PUT-aanvraagregel door1
.Voeg de volgende regels toe direct na de PUT-aanvraagregel:
Content-Type: application/json { "name": "feed fish", "isComplete": false }
Met de voorgaande code worden een header voor het inhoudstype en een hoofdtekst van een JSON-aanvraag toegevoegd.
Selecteer de koppeling Aanvraag verzenden boven de nieuwe PUT-aanvraagregel.
De PUT-aanvraag wordt verzonden naar de app en het antwoord wordt weergegeven in het deelvenster Antwoord. De hoofdtekst van het antwoord is leeg en de statuscode is 204.
Het DELETE-eindpunt onderzoeken en testen
De voorbeeld-app implementeert één DELETE-eindpunt met behulp van 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();
});
Klik in Endpoints Explorermet de rechtermuisknop op het eindpunt DELETE en selecteer Aanvraag genereren.
Er wordt een DELETE-aanvraag toegevoegd aan
TodoApi.http
.Vervang
{id}
in de DELETE-aanvraagregel door1
. De DELETE-aanvraag moet eruitzien als in het volgende voorbeeld:DELETE {{TodoApi_HostAddress}}/todoitems/1 ###
Selecteer de koppeling Aanvraag verzenden voor de DELETE-aanvraag.
De DELETE-aanvraag wordt verzonden naar de app en het antwoord wordt weergegeven in het deelvenster Antwoord. De hoofdtekst van het antwoord is leeg en de statuscode is 204.
De MapGroup-API gebruiken
De code van de voorbeeld-app herhaalt het todoitems
URL-voorvoegsel telkens wanneer er een eindpunt wordt ingesteld. API's hebben vaak groepen eindpunten met een gemeenschappelijk URL-voorvoegsel en de MapGroup methode is beschikbaar om dergelijke groepen te organiseren. Het vermindert terugkerende code en maakt het mogelijk om hele groepen eindpunten aan te passen met één aanroep naar methoden zoals RequireAuthorization en WithMetadata.
Vervang de inhoud van Program.cs
door de volgende code:
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();
De voorgaande code heeft de volgende wijzigingen:
- Voegt
var todoItems = app.MapGroup("/todoitems");
toe om de groep in te stellen met behulp van het URL-voorvoegsel/todoitems
. - Hiermee worden alle
app.Map<HttpVerb>
methoden gewijzigd intodoItems.Map<HttpVerb>
. - Hiermee verwijdert u het URL-voorvoegsel
/todoitems
uit de aanroepen van deMap<HttpVerb>
methode.
Test de eindpunten om te controleren of ze hetzelfde werken.
De TypedResults-API gebruiken
Het retourneren van TypedResults in plaats van Results heeft verschillende voordelen, waaronder testbaarheid en het automatisch retourneren van de metagegevens van het antwoordtype voor OpenAPI om het eindpunt te beschrijven. Voor meer informatie, zie TypedResults versus Results.
De Map<HttpVerb>
methoden kunnen methoden voor routehandlers aanroepen in plaats van lambdas te gebruiken. Als u een voorbeeld wilt zien, werkt u Program.cs bij met de volgende code:
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();
}
De Map<HttpVerb>
-code roept nu methoden aan in plaats van 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);
Deze methoden retourneren objecten die IResult implementeren en worden gedefinieerd door 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();
}
Eenheidstests kunnen deze methoden aanroepen en testen of ze het juiste type retourneren. Als de methode bijvoorbeeld is GetAllTodos
:
static async Task<IResult> GetAllTodos(TodoDb db)
{
return TypedResults.Ok(await db.Todos.ToArrayAsync());
}
Code voor eenheidstests kan controleren of een object van het type OK<Todo[]> wordt geretourneerd door de handlermethode. Bijvoorbeeld:
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);
}
Overmatig posten voorkomen
Op dit moment wordt in de voorbeeld-app het hele Todo
-object weergegeven. Productie-apps In productietoepassingen wordt vaak een subset van het model gebruikt om de gegevens te beperken die kunnen worden ingevoerd en geretourneerd. Er zijn meerdere redenen achter deze en beveiliging is een belangrijke. De subset van een model wordt meestal aangeduid als een DTO (Data Transfer Object), invoermodel of weergavemodel.
DTO- wordt in dit artikel gebruikt.
Een DTO kan worden gebruikt voor het volgende:
- Voorkom overboeking.
- Eigenschappen verbergen die clients niet mogen bekijken.
- Laat sommige eigenschappen weg om de nettolading te verkleinen.
- Platte objectgrafieken die geneste objecten bevatten. Platgemaakte objectgrafieken kunnen handiger zijn voor clients.
Als u de DTO-benadering wilt demonstreren, werkt u de Todo
klasse bij om een geheim veld op te nemen:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
Het geheime veld moet worden verborgen voor deze app, maar een beheer-app kan ervoor kiezen om het beschikbaar te maken.
Controleer of u het geheime veld kunt versturen en ophalen.
Maak een bestand met de naam TodoItemDTO.cs
met de volgende code:
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);
}
Vervang de inhoud van het Program.cs
-bestand door de volgende code om dit DTO-model te gebruiken:
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();
}
Controleer of u alle velden kunt posten en ophalen, behalve het geheime veld.
Problemen oplossen met het voltooide voorbeeld
Als u een probleem ondervindt dat u niet kunt oplossen, vergelijkt u de code met het voltooide project. Voltooide project downloaden of weergeven (hoe te downloaden).
Volgende stappen
- JSON-serialisatieopties configureren.
- Fouten en uitzonderingen afhandelen: de uitzonderingspagina voor ontwikkelaars is standaard ingeschakeld in de ontwikkelomgeving voor minimale API-apps. Zie Fouten verwerken in ASP.NET Core-API'svoor informatie over het afhandelen van fouten en uitzonderingen.
- Zie dit GitHub-voorbeeldvoor een voorbeeld van het testen van een minimale API-app.
- OpenAPI-ondersteuning in minimale API's.
- Quickstart: Publiceren op Azure.
- Organiseren van ASP.NET Core Minimale API's.