Minimal-APIs sind eine spannende Funktion, die in .NET 6 eingeführt wurde und darauf abzielt, die Art und Weise, wie Sie APIs erstellen, zu revolutionieren.
Stellen Sie sich vor, Sie bauen robuste APIs mit minimalem Code und null Boilerplate – Schluss mit dem Ringen um Controller, Routing oder Middleware. Genau das ermöglichen Ihnen Minimal-APIs. Die Idee hinter diesen APIs ist es, den Entwicklungsprozess zu optimieren und ihn unglaublich einfach und effizient zu gestalten.
In diesem Artikel tauchen wir in die Welt der Minimal-APIs in .NET 8 ein und führen Sie durch die Erstellung einer voll funktionsfähigen Buchladen-API. Sie lernen, wie Sie alle Bücher abrufen, ein Buch nach seiner ID erhalten, neue Bücher hinzufügen und sogar Bücher löschen. Lassen Sie uns beginnen.
Inhaltsverzeichnis
Voraussetzungen
Bevor wir loslegen, stellen Sie sicher, dass die folgenden Voraussetzungen auf Ihrem Computer installiert sind:
-
Visual Studio Code oder jeder andere Code-Editor Ihrer Wahl
-
C# Dev Kit für Visual Studio Code
Alternativ können Sie Visual Studio 2022 verwenden, das integrierte Unterstützung für .NET 8 bietet. In diesem Artikel werden wir jedoch Visual Studio Code verwenden. Es ist leichtgewichtig, einfach zu bedienen und plattformübergreifend.
Wir werden Swagger UI verwenden, um unsere API zu testen. Swagger UI ist ein leistungsstarkes Tool, das Ihnen ermöglicht, direkt aus Ihrem Browser mit Ihrer API zu interagieren. Es bietet eine benutzerfreundliche Oberfläche zum Testen Ihrer API-Endpunkte, was das Testen und Debuggen Ihrer API erleichtert.
Wenn Sie ein neues Projekt erstellen, installiert es automatisch die erforderlichen Pakete und konfiguriert das Projekt für die Verwendung von Swagger UI. .NET 8 enthält Swagger UI standardmäßig, sodass Swagger UI für Sie konfiguriert wird, egal ob Sie Ihre Anwendung in Visual Studio oder mit .NET erstellen.
Führen Sie Ihre Anwendung aus, und die Swagger UI öffnet sich automatisch in Ihrem Browser – da wir jedoch VS Code verwenden, müssen wir auf die Portnummer in unserem Terminal klicken.
Sie finden den Quellcode für dieses Projekt auf GitHub.
Einführung in Minimal APIs
Stellen Sie sich vor, Sie arbeiten in einem Codebase mit zahlreichen Endpunkten, die es ziemlich groß und komplex machen. Traditionell beinhaltet das Erstellen einer API in ASP.NET Core die Verwendung von Controllern, Routing, Middleware und einer erheblichen Menge an Boilerplate-Code. Es gibt jedoch zwei Ansätze zum Erstellen einer API in ASP.NET Core: den traditionellen Weg und den minimalistischen Weg.
Der traditionelle Weg ist den meisten Entwicklern vertraut und beinhaltet Controller und umfassenden Infrastruktur-Code. Der minimalistische Weg, eingeführt in .NET 6
, ermöglicht es Ihnen, APIs mit minimalem Code und null Boilerplate zu erstellen. Dieser Ansatz vereinfacht den Entwicklungsprozess und ermöglicht es Ihnen, sich auf das Schreiben von Geschäftslogik zu konzentrieren, anstatt sich mit Infrastruktur-Code zu befassen.
Minimal APIs sind leichtgewichtig, schnell und perfekt für den Aufbau kleiner bis mittelgroßer APIs. Sie sind ideal für Prototyping, das Erstellen von Mikroservices oder die Entwicklung simpler APIs, die nicht viel Komplexität erfordern. In diesem Handbuch werden wir die Welt der minimalen APIs in .NET 6 erkunden und lernen, wie man eine voll funktionsfähige Buchladen-API von Grund auf erstellt.
Wie man eine Minimal API erstellt
Das Erstellen einer minimalen API ist mit der dotnet CLI
straightforward, da die Standardvorlage bereits eine minimale API ist. Wenn Sie jedoch Visual Studio verwenden, müssen Sie den Boilerplate-Code entfernen, der mit der Projektvorlage geliefert wird.
Lasst uns mit der Verwendung der dotnet CLI
ein minimales API-Projekt erstellen.
dotnet new webapi -n BookStoreApi
Der Befehl dotnet new webapi
erstellt ein neues minimales API-Projekt namens BookStoreApi
. Dieses Projekt enthält die notwendigen Dateien und Ordner, um loszulegen.
Lassen Sie uns die Projektstruktur erkunden:
-
Program.cs
: Der Einstiegspunkt der Anwendung, wo der Host konfiguriert wird. -
bookapi-minimal.sln
: Die Lösungsdatei, die das Projekt enthält. -
bookapi-minimal.http
: Eine Datei, die Beispiel-HTTP-Anfragen zum Testen der API enthält. -
bookapi-minimal.csproj
: Die Projektdatei, die die Projekt-Konfiguration enthält. -
appsettings.json
: Die Konfigurationsdatei, die Anwendungseinstellungen speichert. -
appsettings.Development.json
: Die Konfigurationsdatei für die Entwicklungsumgebung.
Wenn Sie die Datei program.cs öffnen, werden Sie feststellen, dass der Code minimal ist. Die Datei Program.cs
enthält den folgenden Code:
var builder = WebApplication.CreateBuilder(args);
// Dienste zum Container hinzufügen.
// Erfahren Sie mehr über die Konfiguration von Swagger/OpenAPI unter https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Konfigurieren der HTTP-Anforderungspipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();
app.Run();
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
Wenn Sie den Code noch nicht vollständig verstehen, machen Sie sich keine Sorgen – wir werden ihn in den kommenden Abschnitten im Detail behandeln. Der wichtigste Punkt ist, dass minimale APIs sehr wenig Code erfordern, was einer ihrer Hauptvorteile ist.
Der Standardcode richtet eine einfache Wettervorhersage-API ein, die Sie zur Überprüfung Ihrer Einrichtung verwenden können. Er generiert eine Liste von Wettervorhersagen und gibt sie zurück, wenn Sie eine GET
-Anfrage an den Endpunkt /weatherforecast
stellen. Außerdem enthält der Code Swagger UI, um Ihnen beim Testen der API zu helfen.
Beachten Sie besonders die Methode app.MapGet
, die eine Route zu einer Handler-Funktion mappt. In diesem Fall mappt sie die Route /weatherforecast
auf eine Funktion, die eine Liste von Wettervorhersagen zurückgibt. Wir werden ähnliche Methoden verwenden, um in den nächsten Abschnitten unsere eigenen Endpunkte zu erstellen.
Bevor wir mit der Erstellung unserer Projektordnerstruktur beginnen, lassen Sie uns die HTTP-Methoden in sowohl Controller-basierten als auch Minimal APIs verstehen.
HTTP-Methoden in Controller-basierten und Minimal APIs
Bei einem Controller-basierten Ansatz, der die traditionelle Methode zur Erstellung von Web-APIs ist, müssen Sie eine Controller-Klasse erstellen und Methoden für jede HTTP-Methode definieren. Zum Beispiel:
-
Um eine
GET
-Methode zu erstellen, verwenden Sie das[HttpGet]
-Attribut. -
Um eine
POST
-Methode zu erstellen, verwenden Sie das[HttpPost]
-Attribut. -
Um eine
PUT
-Methode zu erstellen, verwenden Sie das[HttpPut]
-Attribut. -
Um eine
DELETE
-Methode zu erstellen, verwenden Sie das[HttpDelete]
-Attribut.
So werden Endpunkte in einem Controller-basierten Ansatz erstellt.
Im Gegensatz dazu verwenden Minimal APIs Methoden wie app.MapGet
, app.MapPost
, app.MapPut
und app.MapDelete
, um Endpunkte zu erstellen. Dies ist der Hauptunterschied zwischen den beiden Ansätzen: Controller-basierte APIs verwenden Attribute, um Endpunkte zu definieren, während Minimal APIs Methoden verwenden.
Nun, da Sie verstehen, wie Sie HTTP-Anfragen sowohl in Controller-basierten als auch in Minimal APIs behandeln, erstellen wir unsere Projektordnerstruktur.
Bevor wir unsere Projektordnerstruktur erstellen, lassen Sie uns zunächst das ausführen, was wir haben. Wie wir ранее gelernt haben, wenn Sie ein Projekt mit entweder Visual Studio oder .NET CLI erstellen, kommt es mit einem Standard-WeatherForecast-Projekt, das wir ausführen und in der Benutzeroberfläche sehen können. Lassen Sie uns es ausführen, um sicherzustellen, dass alles funktioniert, bevor wir fortfahren, unsere Projektordner zu erstellen.
Führen Sie diesen Befehl aus:
dotnet run
Sie sollten die folgende Ausgabe sehen:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5228
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: D:\Devolopemnt\Dotnet\bookapi-minimal
Das bedeutet, dass die Anwendung läuft und auf http://localhost:5228
lauscht. Wie ich oben erwähnt habe, da wir die dotnet CLI
und Visual Studio Code verwenden, wird die Anwendung den Browser nicht automatisch für uns öffnen. Dies müssen wir manuell tun.
Öffnen Sie Ihren Browser und navigieren Sie zu http://localhost:5228/swagger/index.html
, um die Standardantwort der API zu sehen.
Sie sollten etwas wie folgt sehen:
Als nächstes müssen wir einen Weg finden, unser Projekt zu strukturieren und die notwendigen Dateien und Ordner zu erstellen, um loszulegen.
Minimale API-Projektdateien
Um unser Projekt zu organisieren, werden wir eine strukturierte Ordnerhierarchie erstellen. Dies wird uns helfen, unseren Code sauber und wartbar zu halten. Hier ist die Ordnerstruktur, die wir verwenden werden:
-
AppContext: Enthält den Datenbankkontext und die zugehörigen Konfigurationen.
-
Konfigurationen: Enthält Entity Framework Core-Konfigurationen und Seed-Daten für die Datenbank.
-
Verträge: Enthält Data Transfer Objects (DTOs), die in unserer Anwendung verwendet werden.
-
Endpunkte: Hier definieren und konfigurieren wir unsere minimalen API-Endpunkte.
-
Ausnahmen: Enthält benutzerdefinierte Ausnahmeklassen, die im Projekt verwendet werden.
-
Erweiterungen: Enthält Erweiterungsmethoden, die wir im gesamten Projekt verwenden werden.
-
Modelle: Enthält Geschäftslogik-Modelle.
-
Dienste: Enthält Dienstklassen, die Geschäftslogik implementieren.
-
Schnittstellen: Enthält Schnittstellendefinitionen, die zur Abbildung unserer Dienste verwendet werden.
In Visual Studio Code können Sie diese Ordnerstruktur wie folgt erstellen:
- AppContext
- Configurations
- Contracts
- Endpoints
- Exceptions
- Extensions
- Models
- Services
- Interfaces
Nach der Einrichtung sollte Ihre Projektordnerstruktur so aussehen:
Nun, da unsere Projektstruktur eingerichtet ist, können wir damit beginnen, unseren Code zu schreiben. Fangen wir an, unsere Modelle zu erstellen.
Wie man die Modelle erstellt
In diesem Abschnitt werden wir Modelle für unsere Anwendung erstellen. Modelle sind die Bausteine unserer Anwendung und repräsentieren die Daten, mit denen unsere Anwendung arbeiten wird. Für unser Beispiel erstellen wir ein Modell für ein Buch.
Um zu beginnen, erstellen Sie einen Ordner namens Models
in Ihrem Projektverzeichnis. Innerhalb dieses Ordners erstellen Sie eine Datei namens BookModel.cs
und fügen den folgenden Code hinzu:
// Models/BookModel.cs
namespace bookapi_minimal.Models
{
public class BookModel
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Description { get; set; }
public string Category { get; set; }
public string Language { get; set; }
public int TotalPages { get; set; }
}
}
Diese BookModel-Klasse
definiert die Eigenschaften, die die Details eines Buches darstellen, wie z.B. seinen Titel
, Autor
, Beschreibung
, Kategorie
, Sprache
und Seitenanzahl
. Jede Eigenschaft ist darauf ausgelegt, spezifische Informationen über das Buch zu speichern, was die Verwaltung und Manipulation von Buchdaten innerhalb unserer Anwendung erleichtert.
Nun, da wir unser Modell erstellt haben, erstellen wir unseren Datenbankkontext.
Wie man den Datenbankkontext erstellt
Der Datenbankkontext ist eine Klasse, die eine Sitzung mit der Datenbank darstellt. Er ist verantwortlich für die Interaktion mit der Datenbank und die Ausführung von Datenbankoperationen. In unserer Anwendung werden wir Entity Framework Core verwenden, um mit unserer Datenbank zu interagieren.
Installieren Sie die erforderlichen Pakete
Bevor wir unseren Datenbankkontext erstellen, müssen wir die folgenden Pakete installieren:
-
Microsoft.EntityFrameworkCore
-
Microsoft.EntityFrameworkCore.SqlServer
-
FluentValidation.DependencyInjectionExtensions
Sie können diese Pakete mit den folgenden Befehlen installieren:
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package FluentValidation.DependencyInjectionExtensions
Überprüfung der Paketinstallation
Um zu überprüfen, ob die Pakete installiert sind, öffnen Sie die Datei bookapi-minimal.csproj
im Stammverzeichnis Ihres Projekts. Sie sollten die installierten Pakete wie folgt aufgelistet sehen:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>bookapi_minimal</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
</Project>
Dies bestätigt, dass die Pakete erfolgreich installiert wurden.
Jetzt erstellen wir unseren Datenbankkontext.
Im Ordner AppContext erstellen Sie eine neue Datei mit dem Namen ApplicationContext.cs
und fügen den folgenden Code hinzu:
// AppContext/ApplicationContext.cs
using bookapi_minimal.Models;
using Microsoft.EntityFrameworkCore;
namespace bookapi_minimal.AppContext
{
public class ApplicationContext(DbContextOptions<ApplicationContext> options) : DbContext(options)
{
// Standardschema für den Datenbankkontext
private const string DefaultSchema = "bookapi";
// DbSet zur Darstellung der Sammlung von Büchern in unserer Datenbank
public DbSet<BookModel> Books { get; set; }
// Konstruktor zur Konfiguration des Datenbankkontexts
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema(DefaultSchema);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly);
}
}
}
Lassen Sie uns den obigen Code zerlegen:
-
Wir definieren eine Klasse mit dem Namen
ApplicationContext
, die vonDbContext
erbt. Die KlasseDbContext
ist Teil von Entity Framework Core und repräsentiert eine Sitzung mit der Datenbank. -
Der Konstruktor akzeptiert eine Instanz von
DbContextOptions<ApplicationContext>
. Dieser Konstruktor wird verwendet, um die Optionen des Datenbankkontexts zu konfigurieren. -
Wir definieren eine Eigenschaft mit dem Namen
Books
vom TypDbSet<BookModel>
. Diese Eigenschaft repräsentiert die Sammlung von Büchern in unserer Datenbank. -
Wir überschreiben die Methode
OnModelCreating
, um das Datenbankschema zu konfigurieren und alle in unserer Anwendung definierten Konfigurationen anzuwenden.
Nun, da wir unseren Datenbankkontext erstellt haben, erstellen wir unsere Erweiterungsmethode und registrieren unseren Datenbankkontext im Abhängigkeitsinjektionscontainer.
Eine Erweiterungsmethode erstellen
Bevor wir die Erweiterungsmethode erstellen, lassen Sie uns verstehen, was eine Erweiterungsmethode im Kontext von ASP.NET Core ist.
Eine Erweiterungsmethode ist eine statische Methode, die neue Funktionalität zu einem bestehenden Typ hinzufügt, ohne den ursprünglichen Typ zu modifizieren. In ASP.NET Core werden Erweiterungsmethoden häufig verwendet, um die Funktionalität der IServiceCollection
-Schnittstelle zu erweitern, die zum Registrieren von Diensten im Abhängigkeitsinjektionscontainer verwendet wird.
Dienste sind Komponenten, die Funktionalität für eine Anwendung bereitstellen, wie z.B. Datenbankzugriff, Protokollierung und Konfiguration. Durch die Erstellung einer Erweiterungsmethode für die IServiceCollection
-Schnittstelle können Sie den Prozess der Registrierung Ihrer Dienste im Abhängigkeitsinjektionscontainer vereinfachen.
Statt alles in die Datei Program.cs
zu legen, werden wir eine Erweiterungsmethode erstellen, um unsere Dienste im Abhängigkeitsinjektionscontainer zu registrieren. Dies hilft uns, unseren Code sauber und organisiert zu halten.
Im Ordner Extensions
erstellen Sie eine neue Datei namens ServiceExtensions.cs
und fügen Sie den folgenden Code hinzu:
using System.Reflection;
using bookapi_minimal.AppContext;
using FluentValidation;
using Microsoft.EntityFrameworkCore;
namespace bookapi_minimal.Extensions
{
public static class ServiceExtensions
{
public static void AddApplicationServices(this IHostApplicationBuilder builder)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
if (builder.Configuration == null) throw new ArgumentNullException(nameof(builder.Configuration));
// Hinzufügen des Datenbankkontexts
builder.Services.AddDbContext<ApplicationContext>(configure =>
{
configure.UseSqlServer(builder.Configuration.GetConnectionString("sqlConnection"));
});
// Hinzufügen von Validatoren aus der aktuellen Assembly
builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
}
}
}
Lassen Sie uns den obigen Code herunterbrechen:
-
Wir definieren eine statische Klasse namens
ServiceExtensions
, die eine Erweiterungsmethode namensAddApplicationServices
enthält. Diese Methode erweitert dieIHostApplicationBuilder
-Schnittstelle, die verwendet wird, um die Anforderungsverarbeitungspipeline der Anwendung zu konfigurieren. -
Die Methode
AddApplicationServices
akzeptiert eine Instanz vonIHostApplicationBuilder
als Parameter. Dieser Parameter wird verwendet, um auf die Konfiguration und Dienste der Anwendung zuzugreifen. -
Wir fügen den
ApplicationContext
zum Dependency-Injection-Container hinzu und konfigurieren ihn so, dass er SQL Server als Datenbankanbieter verwendet. Wir holen uns die Verbindungszeichenfolge aus der Dateiappsettings.json
mit der MethodeGetConnectionString
. -
Wir fügen
Validators
aus der aktuellenAssembly
mit der MethodeAddValidatorsFromAssembly
hinzu. Diese Methode durchsucht die aktuelle Assembly nach Klassen, die das IValidator-Interface implementieren, und registriert sie im Dependency-Injection-Container.
Als nächstes müssen wir die Verbindungszeichenfolge zur Datei appsettings.json
hinzufügen. Fügen Sie den folgenden Code in Ihre Datei appsettings.json
ein:
{
"ConnectionStrings": {
"sqlConnection": "Server=localhost\\SQLEXPRESS02;Database=BookAPIMinimalAPI;Integrated Security=true;TrustServerCertificate=true;"
}
}
Stellen Sie sicher, dass Sie your_password
durch Ihr tatsächliches SQL Server-Passwort ersetzen.
Ihre Datei appsettings.json
sollte wie folgt aussehen:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"sqlConnection": "Server=localhost\\SQLEXPRESS02;Database=BookAPIMinimalAPI;Integrated Security=true;TrustServerCertificate=true;"
},
"AllowedHosts": "*"
}
Herzlichen Glückwunsch! Sie haben den Datenbankkontext, die Erweiterungsmethode und die Verbindungszeichenfolge für Ihre Anwendung erfolgreich erstellt. Im nächsten Abschnitt werden wir einen Vertrag erstellen.
Wie man einen Vertrag erstellt
Verträge sind Data Transfer Objects (DTOs), die die Struktur der zwischen Client und Server ausgetauschten Daten definieren. In unserer Anwendung werden wir Verträge erstellen, um die von unseren API-Endpunkten gesendeten und empfangenen Daten darzustellen.
Hier sind die Verträge, die wir erstellen werden:
-
CreateBookRequest: Dies stellt die Daten dar, die beim Erstellen eines neuen Buches gesendet werden.
-
UpdateBookRequest: tHI Stellt die Daten dar, die beim Aktualisieren eines vorhandenen Buches gesendet werden.
-
BookResponse: Stellt die Daten dar, die beim Abrufen eines Buches zurückgegeben werden.
-
ErrorResponse: Stellt die Fehlerantwort dar, die zurückgegeben wird, wenn eine Ausnahme auftritt.
-
ApiResponse: Stellt die von der API zurückgegebene Antwort dar.
Im Ordner Contracts
eine neue Datei mit dem Namen CreateBookRequest
erstellen und den folgenden Code hinzufügen:
// Contracts/CreateBookRequest.cs
namespace bookapi_minimal.Contracts
{
public record CreateBookRequest
{
public string Title { get; init; }
public string Author { get; init; }
public string Description { get; init; }
public string Category { get; init; }
public string Language { get; init; }
public int TotalPages { get; init; }
}
}
Im Ordner Contracts
eine neue Datei mit dem Namen UpdateBookRequest
erstellen und den folgenden Code hinzufügen:
// Contracts/UpdateBookRequest.cs
namespace bookapi_minimal.Contracts
{
public record UpdateBookRequest
{
public string Title { get; set; }
public string Author { get; set; }
public string Description { get; set; }
public string Category { get; set; }
public string Language { get; set; }
public int TotalPages { get; set; }
}
}
Im Ordner Contracts
erstellen Sie eine neue Datei mit dem Namen BookResponse
und fügen Sie den folgenden Code hinzu:
// Contracts/BookResponse.cs
namespace bookapi_minimal.Contracts
{
public record BookResponse
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Description { get; set; }
public string Category { get; set; }
public string Language { get; set; }
public int TotalPages { get; set; }
}
}
Im Ordner Contracts
erstellen Sie eine neue Datei mit dem Namen ErrorResponse
und fügen Sie den folgenden Code hinzu:
// Contracts/ErrorResponse.cs
namespace bookapi_minimal.Contracts
{
public record ErrorResponse
{
public string Title { get; set; }
public int StatusCode { get; set; }
public string Message { get; set; }
}
}
Im Ordner Contracts
erstellen Sie eine neue Datei mit dem Namen ApiResponse
und fügen Sie den folgenden Code hinzu:
// Contracts/ApiResponse.cs
namespace bookapi_minimal.Contracts
{
public class ApiResponse<T>
{
public T Data { get; set; }
public string Message { get; set; }
public ApiResponse(T data, string message)
{
Data = data;
Message = message;
}
}
}
Diese Verträge helfen uns, die Struktur der zwischen Client und Server ausgetauschten Daten zu definieren, was die Arbeit mit den Daten in unserer Anwendung erleichtert.
Im nächsten Abschnitt werden wir Dienste erstellen, um die Geschäftslogik unserer Anwendung zu implementieren.
Wie man Dienste hinzufügt
Dienste sind Komponenten, die Funktionalität für eine Anwendung bereitstellen. In unserer Anwendung werden wir Dienste erstellen, um die Geschäftslogik unserer Anwendung zu implementieren. Wir werden Dienste erstellen, um CRUD-Vorgänge für Bücher zu handhaben, Buchdaten zu validieren und Ausnahmen zu behandeln.
In ASP.NET Core werden Dienste im Abhängigkeitsinjektionscontainer registriert und können in andere Komponenten, wie Controller und Endpunkte, injiziert werden. Da es sich jedoch um eine minimale API handelt, werden wir die Dienste direkt in die Endpunkte injizieren.
Lassen Sie uns eine Schnittstelle für unsere Dienste erstellen. Im Ordner Interfaces
erstellen Sie eine neue Datei namens IBookService.cs
und fügen Sie den folgenden Code hinzu:
// Interfaces/IBookService.cs
using bookapi_minimal.Contracts;
namespace bookapi_minimal.Interfaces
{
public interface IBookService
{
Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest);
Task<BookResponse> GetBookByIdAsync(Guid id);
Task<IEnumerable<BookResponse>> GetBooksAsync();
Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest updateBookRequest);
Task<bool> DeleteBookAsync(Guid id);
}
}
Lassen Sie uns den obigen Code analysieren: Wir haben eine Schnittstelle namens IBookService
definiert, die Methoden zur Handhabung von CRUD-Operationen für Bücher enthält. Die Schnittstelle definiert die folgenden Methoden:
-
AddBookAsync
: Fügt ein neues Buch in die Datenbank ein. -
GetBookByIdAsync
: Ruft ein Buch anhand seiner ID ab. -
GetBooksAsync
: Ruft alle Bücher aus der Datenbank ab. -
UpdateBookAsync
: Aktualisiert ein vorhandenes Buch.
Wir verwenden den Vertrag, den wir zuvor im Ordner Contracts
erstellt haben. Die IBookService
-Schnittstelle definiert die Struktur der Methoden, die von den Service-Klassen implementiert werden. Dies hilft uns, die Schnittstelle von der Implementierung zu trennen, was die Wartung und das Testen unseres Codes erleichtert.
Nun, da wir die Schnittstelle erstellt haben, lassen Sie uns die Service-Klasse erstellen, die die Schnittstelle implementiert.
Wie man den Buch-Dienst implementiert
Dieser Dienst wird die Schnittstelle IBookService
implementieren und die Geschäftslogik für unsere Anwendung bereitstellen. Im Ordner Services
erstellen Sie eine neue Datei namens BookService.cs
. Ihre initiale Datei sollte wie folgt aussehen:
// Services/BookService.cs
namespace bookapi_minimal.Services
{
public class BookService
{
}
}
Das Erste, was wir tun müssen, ist die Schnittstelle zur BookService
-Klasse hinzuzufügen. Aktualisieren Sie die BookService
-Klasse, um die IBookService
-Schnittstelle wie folgt zu implementieren:
// Services/BookService.cs
using bookapi_minimal.Interfaces;
namespace bookapi_minimal.Services
{
public class BookService:IBookService
{
}
}
Wenn Sie dies tun, zeigt Ihr VS Code möglicherweise einen Fehler an, weil wir die Methoden in der Schnittstelle noch nicht implementiert haben. Lassen Sie uns nun die Methoden in der BookService
-Klasse implementieren.
In VS Code können Sie das Tastenkürzel Ctrl + .
verwenden, um die Methoden in der Schnittstelle zu implementieren. Dann sehen Sie den folgenden Code, der für Sie generiert wird:
using bookapi_minimal.Contracts;
using bookapi_minimal.Interfaces;
namespace bookapi_minimal.Services
{
// Service-Klasse für die Verwaltung von Büchern
public class BookService : IBookService
{
// Methode zum Hinzufügen eines neuen Buchs zur Datenbank
public Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest)
{
throw new NotImplementedException();
}
// Methode zum Löschen eines Buchs aus der Datenbank
public Task<bool> DeleteBookAsync(Guid id)
{
throw new NotImplementedException();
}
// Methode zum Abrufen eines Buchs aus der Datenbank nach seiner ID
public Task<BookResponse> GetBookByIdAsync(Guid id)
{
throw new NotImplementedException();
}
// Methode zum Abrufen aller Bücher aus der Datenbank
public Task<IEnumerable<BookResponse>> GetBooksAsync()
{
throw new NotImplementedException();
}
// Methode zum Aktualisieren eines Buchs in der Datenbank
public Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest updateBookRequest)
{
throw new NotImplementedException();
}
}
}
Jetzt können Sie sehen, dass die Methoden im Interface in der Klasse BookService
implementiert wurden. Wir werden die Geschäftslogik für jede Methode im nächsten Abschnitt implementieren.
Bevor wir das tun, fügen wir die notwendigen Abhängigkeiten zur Klasse BookService
hinzu. Wir müssen die Abhängigkeiten ApplicationContext
und ILogger
in die Klasse BookService
injizieren. ApplicationContext
wird verwendet, um mit der Datenbank zu interagieren, während ILogger
für das Protokollieren verwendet wird.
Um die Abhängigkeiten zu injizieren, aktualisieren Sie die Klasse BookService
wie folgt:
// Services/BookService.cs
// ...
private readonly ApplicationContext _context; // Datenbankkontext
private readonly ILogger<BookService> _logger; // Logger für das Protokollieren von Informationen und Fehlern
//..
Da wir die Abhängigkeiten hinzugefügt haben, müssen wir den Konstruktor von BookService
aktualisieren, um die Abhängigkeiten zu akzeptieren. Aktualisieren Sie den Konstruktor von BookService
wie folgt:
// Services/BookService.cs
// ...
// Konstruktor zur Initialisierung des Datenbankkontexts und des Loggers
public BookService(ApplicationContext context, ILogger<BookService> logger)
{
_context = context;
_logger = logger;
}
// ...
Nun, da wir die Abhängigkeiten hinzugefügt und den Konstruktor aktualisiert haben, können wir die Geschäftslogik für jede Methode in der Klasse BookService
implementieren.
Lassen Sie uns die Logik für die CREATE, READ, UPDATE und DELETE Operationen in der BookService
Klasse erstellen.
Wie man die Methode AddBookAsync
implementiert
Wie bereits erwähnt, werden wir die Methode AddBookAsync
verwenden, um ein neues Buch in die Datenbank hinzuzufügen. In dieser Methode erstellen wir eine neue Buchentität, übertragen die Daten vom CreateBookRequest
Objekt auf die Buchentität und speichern die Buchentität in der Datenbank. Wir geben auch die Buchentität als ein BookResponse
Objekt zurück.
Aktualisieren Sie die Methode AddBookAsync
in der BookService
Klasse wie folgt:
// Services/BookService.cs
// ...
/// <summary>
/// Ein neues Buch hinzufügen
/// </summary>
/// <param name="createBookRequest">Buchanfrage, die hinzugefügt werden soll</param>
/// <returns>Details des erstellten Buches</returns>
public async Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest)
{
try
{
var book = new BookModel
{
Title = createBookRequest.Title,
Author = createBookRequest.Author,
Description = createBookRequest.Description,
Category = createBookRequest.Category,
Language = createBookRequest.Language,
TotalPages = createBookRequest.TotalPages
};
// Das Buch zur Datenbank hinzufügen
_context.Books.Add(book);
await _context.SaveChangesAsync();
_logger.LogInformation("Book added successfully.");
// Details des erstellten Buches zurückgeben
return new BookResponse
{
Id = book.Id,
Title = book.Title,
Author = book.Author,
Description = book.Description,
Category = book.Category,
Language = book.Language,
TotalPages = book.TotalPages
};
}
catch (Exception ex)
{
_logger.LogError($"Error adding book: {ex.Message}");
throw;
}
}
// ...
In diesem Code erstellen wir eine neue Buchentität aus dem CreateBookRequest
-Objekt, ordnen die Daten vom CreateBookRequest
-Objekt auf die Buchentität zu, speichern die Buchentität in der Datenbank und geben die Buchentität als BookResponse
-Objekt zurück.
Wir protokollieren auch Informationen und Fehler mit der ILogger
-Abhängigkeit. Wenn während des Prozesses eine Ausnahme auftritt, protokollieren wir die Fehlermeldung und werfen die Ausnahme erneut.
Nun, da wir die Methode AddBookAsync
implementiert haben, lassen Sie uns die Methode GetBookByIdAsync
implementieren.
Wie man die Methode GetBookByIdAsync
implementiert
Die Methode GetBookByIdAsync
wird verwendet, um ein Buch anhand seiner ID aus der Datenbank abzurufen. In dieser Methode werden wir die Datenbank nach dem Buch mit der angegebenen ID abfragen, die Buchentität auf ein BookResponse
-Objekt abbilden und das BookResponse
-Objekt zurückgeben.
Aktualisieren Sie die Methode GetBookByIdAsync
in der Klasse BookService
wie folgt:
// Services/BookService.cs
//...
/// <summary>
/// Ein Buch anhand seiner ID abrufen
/// </summary>
/// <param name="id">ID des Buches</param>
/// <returns>Details des Buches</returns>
public async Task<BookResponse> GetBookByIdAsync(Guid id)
{
try
{
// Das Buch anhand seiner ID suchen
var book = await _context.Books.FindAsync(id);
if (book == null)
{
_logger.LogWarning($"Book with ID {id} not found.");
return null;
}
// Die Details des Buches zurückgeben
return new BookResponse
{
Id = book.Id,
Title = book.Title,
Author = book.Author,
Description = book.Description,
Category = book.Category,
Language = book.Language,
TotalPages = book.TotalPages
};
}
catch (Exception ex)
{
_logger.LogError($"Error retrieving book: {ex.Message}");
throw;
}
}
//...
In diesem Code fragen wir die Datenbank nach dem Buch mit der angegebenen ID ab, mappen die Buchentität auf ein BookResponse
-Objekt und geben das BookResponse
-Objekt zurück. Wir protokollieren auch Informationen und Fehler mit der ILogger
-Abhängigkeit.
Wenn das Buch mit der angegebenen ID nicht gefunden wird, protokollieren wir eine Warnmeldung und geben null zurück. Wenn während des Prozesses eine Ausnahme auftritt, protokollieren wir die Fehlermeldung und werfen die Ausnahme erneut.
Nun, da wir die Methode GetBookByIdAsync
implementiert haben, lassen Sie uns die Methode GetBooksAsync
implementieren.
Wie man die Methode GetBooksAsync
implementiert
Die Methode GetBooksAsync
wird verwendet, um alle Bücher aus der Datenbank abzurufen. In dieser Methode werden wir die Datenbank nach allen Büchern abfragen, jede Buchentität auf ein BookResponse
-Objekt abbilden und eine Liste von BookResponse
-Objekten zurückgeben.
Aktualisieren Sie die Methode GetBooksAsync
in der Klasse BookService
wie folgt:
// Services/BookService.cs
//...
/// <summary>
/// Alle Bücher abrufen
/// </summary>
/// <returns>Liste aller Bücher</returns>
public async Task<IEnumerable<BookResponse>> GetBooksAsync()
{
try
{
// Alle Bücher aus der Datenbank abrufen
var books = await _context.Books.ToListAsync();
// Details aller Bücher zurückgeben
return books.Select(book => new BookResponse
{
Id = book.Id,
Title = book.Title,
Author = book.Author,
Description = book.Description,
Category = book.Category,
Language = book.Language,
TotalPages = book.TotalPages
});
}
catch (Exception ex)
{
_logger.LogError($"Error retrieving books: {ex.Message}");
throw;
}
}
//...
Hierbei fragen wir die Datenbank nach allen Büchern ab, mappen jede Buchentität auf ein BookResponse
-Objekt und geben eine Liste von BookResponse
-Objekten zurück. Wir protokollieren auch Informationen und Fehler mit der Abhängigkeit ILogger
. Wenn während des Prozesses eine Ausnahme auftritt, protokollieren wir die Fehlermeldung und werfen die Ausnahme erneut.
Nun, da wir die Methode GetBooksAsync
implementiert haben, lassen Sie uns die Methode UpdateBookAsync
implementieren.
Wie man die Methode UpdateBookAsync
implementiert
Die Methode UpdateBookAsync
wird verwendet, um ein vorhandenes Buch in der Datenbank zu aktualisieren. In dieser Methode werden wir die Datenbank nach dem Buch mit der angegebenen ID abfragen, die Buchentität mit den Daten aus dem UpdateBookRequest
-Objekt aktualisieren, die aktualisierte Buchentität in der Datenbank speichern und die aktualisierte Buchentität als BookResponse
-Objekt zurückgeben.
Aktualisieren Sie die Methode UpdateBookAsync
in der Klasse BookService
wie folgt:
// Services/BookService.cs
//...
/// <summary>
/// Ein vorhandenes Buch aktualisieren
/// </summary>
/// <param name="id">ID des zu aktualisierenden Buches</param>
/// <param name="book">Aktualisiertes Buchmodell</param>
/// <returns>Details des aktualisierten Buches</returns>
public async Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest book)
{
try
{
// Das vorhandene Buch nach seiner ID suchen
var existingBook = await _context.Books.FindAsync(id);
if (existingBook == null)
{
_logger.LogWarning($"Book with ID {id} not found.");
return null;
}
// Die Buchdetails aktualisieren
existingBook.Title = book.Title;
existingBook.Author = book.Author;
existingBook.Description = book.Description;
existingBook.Category = book.Category;
existingBook.Language = book.Language;
existingBook.TotalPages = book.TotalPages;
// Die Änderungen in der Datenbank speichern
await _context.SaveChangesAsync();
_logger.LogInformation("Book updated successfully.");
// Die Details des aktualisierten Buches zurückgeben
return new BookResponse
{
Id = existingBook.Id,
Title = existingBook.Title,
Author = existingBook.Author,
Description = existingBook.Description,
Category = existingBook.Category,
Language = existingBook.Language,
TotalPages = existingBook.TotalPages
};
}
catch (Exception ex)
{
_logger.LogError($"Error updating book: {ex.Message}");
throw;
}
}
//...
Hier fragen wir die Datenbank nach dem Buch mit der angegebenen ID ab, aktualisieren die Buchentität mit den Daten aus dem UpdateBookRequest
-Objekt, speichern die aktualisierte Buchentität in der Datenbank und geben die aktualisierte Buchentität als BookResponse
-Objekt zurück. Wir protokollieren auch Informationen und Fehler mit der ILogger
-Abhängigkeit.
Wenn das Buch mit der angegebenen ID nicht gefunden wird, protokollieren wir eine Warnmeldung und geben null zurück. Wenn während des Prozesses eine Ausnahme auftritt, protokollieren wir die Fehlermeldung und werfen die Ausnahme erneut.
Nun, da wir die Methode UpdateBookAsync
implementiert haben, lassen Sie uns die Methode DeleteBookAsync
implementieren.
Wie man die Methode DeleteBookAsync
implementiert
Die Methode DeleteBookAsync
wird verwendet, um ein vorhandenes Buch aus der Datenbank zu löschen. In dieser Methode werden wir die Datenbank nach dem Buch mit der angegebenen ID abfragen, die Buchentität aus der Datenbank entfernen und einen booleschen Wert zurückgeben, der angibt, ob das Buch erfolgreich gelöscht wurde.
Aktualisieren Sie die Methode DeleteBookAsync
in der Klasse BookService
wie folgt:
// Services/BookService.cs
//...
/// <summary>
/// Löscht ein Buch anhand seiner ID
/// </summary>
/// <param name="id">ID des zu löschenden Buches</param>
/// <returns>True, wenn das Buch gelöscht wurde, andernfalls false</returns>
public async Task<bool> DeleteBookAsync(Guid id)
{
try
{
// Findet das Buch anhand seiner ID
var book = await _context.Books.FindAsync(id);
if (book == null)
{
_logger.LogWarning($"Book with ID {id} not found.");
return false;
}
// Entfernt das Buch aus der Datenbank
_context.Books.Remove(book);
await _context.SaveChangesAsync();
_logger.LogInformation($"Book with ID {id} deleted successfully.");
return true;
}
catch (Exception ex)
{
_logger.LogError($"Error deleting book: {ex.Message}");
throw;
}
}
//...
In diesem Code fragen wir die Datenbank nach dem Buch mit der angegebenen ID ab, entfernen die Buchentität aus der Datenbank und geben einen booleschen Wert zurück, der angibt, ob das Buch erfolgreich gelöscht wurde. Wir protokollieren auch Informationen und Fehler mit der ILogger
-Abhängigkeit.
Wenn das Buch mit der angegebenen ID nicht gefunden wird, protokollieren wir eine Warnmeldung und geben false zurück. Wenn während des Prozesses eine Ausnahme auftritt, protokollieren wir die Fehlermeldung und werfen die Ausnahme erneut.
Jetzt haben Sie die Geschäftslogik für die Methoden AddBookAsync
, GetBookByIdAsync
, GetBooksAsync
, UpdateBookAsync
und DeleteBookAsync
in der Klasse BookService
erfolgreich implementiert. Diese Methoden behandeln die CRUD-Vorgänge für Bücher, validieren Buchdaten und behandeln Ausnahmen. Bis jetzt sollte Ihre Klasse BookService
wie folgt aussehen:
using bookapi_minimal.AppContext;
using bookapi_minimal.Contracts;
using bookapi_minimal.Interfaces;
using bookapi_minimal.Models;
using Microsoft.EntityFrameworkCore;
namespace bookapi_minimal.Services
{
public class BookService : IBookService
{
private readonly ApplicationContext _context; // Datenbankkontext
private readonly ILogger<BookService> _logger; // Logger für die Protokollierung von Informationen und Fehlern
// Konstruktor zur Initialisierung des Datenbankkontexts und des Loggers
public BookService(ApplicationContext context, ILogger<BookService> logger)
{
_context = context;
_logger = logger;
}
/// Ein neues Buch hinzufügen
/// </summary>
/// <param name="createBookRequest">Buchanfrage, die hinzugefügt werden soll</param>
/// <returns>Details des erstellten Buchs</returns>
public async Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest)
{
try
{
var book = new BookModel
{
Title = createBookRequest.Title,
Author = createBookRequest.Author,
Description = createBookRequest.Description,
Category = createBookRequest.Category,
Language = createBookRequest.Language,
TotalPages = createBookRequest.TotalPages
};
// Das Buch zur Datenbank hinzufügen
_context.Books.Add(book);
await _context.SaveChangesAsync();
_logger.LogInformation("Book added successfully.");
// Die Details des erstellten Buchs zurückgeben
return new BookResponse
{
Id = book.Id,
Title = book.Title,
Author = book.Author,
Description = book.Description,
Category = book.Category,
Language = book.Language,
TotalPages = book.TotalPages
};
}
catch (Exception ex)
{
_logger.LogError($"Error adding book: {ex.Message}");
throw;
}
}
/// <summary>
/// Ein Buch nach seiner ID abrufen
/// </summary>
/// <param name="id">ID des Buchs</param>
/// <returns>Details des Buchs</returns>
public async Task<BookResponse> GetBookByIdAsync(Guid id)
{
try
{
// Das Buch nach seiner ID finden
var book = await _context.Books.FindAsync(id);
if (book == null)
{
_logger.LogWarning($"Book with ID {id} not found.");
return null;
}
// Die Details des Buchs zurückgeben
return new BookResponse
{
Id = book.Id,
Title = book.Title,
Author = book.Author,
Description = book.Description,
Category = book.Category,
Language = book.Language,
TotalPages = book.TotalPages
};
}
catch (Exception ex)
{
_logger.LogError($"Error retrieving book: {ex.Message}");
throw;
}
}
/// <summary>
/// Alle Bücher abrufen
/// </summary>
/// <returns>Liste aller Bücher</returns>
public async Task<IEnumerable<BookResponse>> GetBooksAsync()
{
try
{
// Alle Bücher aus der Datenbank abrufen
var books = await _context.Books.ToListAsync();
// Die Details aller Bücher zurückgeben
return books.Select(book => new BookResponse
{
Id = book.Id,
Title = book.Title,
Author = book.Author,
Description = book.Description,
Category = book.Category,
Language = book.Language,
TotalPages = book.TotalPages
});
}
catch (Exception ex)
{
_logger.LogError($"Error retrieving books: {ex.Message}");
throw;
}
}
/// <summary>
/// Ein vorhandenes Buch aktualisieren
/// </summary>
/// <param name="id">ID des zu aktualisierenden Buchs</param>
/// <param name="book">Aktualisiertes Buchmodell</param>
/// <returns>Details des aktualisierten Buchs</returns>
public async Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest book)
{
try
{
// Das vorhandene Buch nach seiner ID finden
var existingBook = await _context.Books.FindAsync(id);
if (existingBook == null)
{
_logger.LogWarning($"Book with ID {id} not found.");
return null;
}
// Die Buchdetails aktualisieren
existingBook.Title = book.Title;
existingBook.Author = book.Author;
existingBook.Description = book.Description;
existingBook.Category = book.Category;
existingBook.Language = book.Language;
existingBook.TotalPages = book.TotalPages;
// Die Änderungen in der Datenbank speichern
await _context.SaveChangesAsync();
_logger.LogInformation("Book updated successfully.");
// Die Details des aktualisierten Buchs zurückgeben
return new BookResponse
{
Id = existingBook.Id,
Title = existingBook.Title,
Author = existingBook.Author,
Description = existingBook.Description,
Category = existingBook.Category,
Language = existingBook.Language,
TotalPages = existingBook.TotalPages
};
}
catch (Exception ex)
{
_logger.LogError($"Error updating book: {ex.Message}");
throw;
}
}
/// <summary>
/// Ein Buch nach seiner ID löschen
/// </summary>
/// <param name="id">ID des zu löschenden Buchs</param>
/// <returns>True, wenn das Buch gelöscht wurde, andernfalls false</returns>
public async Task<bool> DeleteBookAsync(Guid id)
{
try
{
// Das Buch nach seiner ID finden
var book = await _context.Books.FindAsync(id);
if (book == null)
{
_logger.LogWarning($"Book with ID {id} not found.");
return false;
}
// Das Buch aus der Datenbank entfernen
_context.Books.Remove(book);
await _context.SaveChangesAsync();
_logger.LogInformation($"Book with ID {id} deleted successfully.");
return true;
}
catch (Exception ex)
{
_logger.LogError($"Error deleting book: {ex.Message}");
throw;
}
}
}
}
Herzlichen Glückwunsch! Sie haben die Geschäftslogik für die Methoden AddBookAsync
, GetBookByIdAsync
, GetBooksAsync
, UpdateBookAsync
und DeleteBookAsync
in der Klasse BookService
erfolgreich implementiert.
Es gibt noch eine Sache, die wir tun müssen: Wir müssen den Dienst in unserer Erweiterungsmethode registrieren. Lassen Sie uns das gleich erledigen.
In Ihrer Datei ServiceExtensions.cs
fügen Sie den folgenden Code hinzu:
// Extensions/ServiceExtensions.cs
//..
builder.Services.AddScoped<IBookService, BookService>();
//...
Dies registriert die Klasse BookService
als scoped Dienst. Das bedeutet, dass der Dienst einmal pro Anfrage erstellt und nach Abschluss der Anfrage disposed wird.
Nun, da der Dienst funktioniert, lassen Sie uns weitermachen und die Ausnahme-Klassen erstellen.
Wie man Ausnahmen erstellt
Das richtige Handhaben von Ausnahmen ist entscheidend, um die Stabilität und Zuverlässigkeit einer Anwendung sicherzustellen. Im Kontext von ASP.NET Core gibt es zwei Haupttypen von Ausnahmen:
-
Systemausnahmen: Dies sind Ausnahmen, die vom .NET Laufzeitumgebung oder dem zugrunde liegenden System ausgelöst werden.
-
Anwendungsausnahmen: Dies sind Ausnahmen, die vom Anwendungscode ausgelöst werden, um spezifische Fehler oder Bedingungen zu behandeln.
In ASP.NET Core mit .NET 8 wurde eine neue Funktion namens globale Ausnahmebehandlung eingeführt. Diese Funktion ermöglicht es Ihnen, Ausnahmen global in Ihrer Anwendung zu behandeln, was die Verwaltung von Fehlern erleichtert und eine konsistente Benutzererfahrung bietet.
In unserer Anwendung werden wir benutzerdefinierte Ausnahmeklassen erstellen, um spezifische Fehler und Bedingungen zu behandeln. Wir werden auch die Funktion der globalen Ausnahmebehandlung nutzen, um Ausnahmen global zu verwalten und sicherzustellen, dass ein einheitlicher Ansatz zur Fehlerbehandlung im gesamten Anwendungsumfang angewendet wird.
Wir werden die folgenden Ausnahmeklassen erstellen:
-
NoBookFoundException
: Ausgelöst, wenn ein Buch mit der angegebenen ID nicht gefunden wird. -
BookDoesNotExistException
: Ausgelöst, wenn ein Buch mit der angegebenen ID nicht existiert. -
GlobalExceptionHandler
: Behandelt Ausnahmen global in der Anwendung.
Im Ordner Exceptions
erstellen Sie eine neue Datei namens NoBookFoundException.cs
und fügen Sie den folgenden Code hinzu:
// Exceptions/NoBookFoundException.cs
namespace bookapi_minimal.Exceptions
{
public class NoBookFoundException : Exception
{
public NoBookFoundException() : base("No books found")
{}
}
}
In diesem Code erstellen wir eine benutzerdefinierte Ausnahme-Klasse namens NoBookFoundException
, die von der Exception
-Klasse erbt. Die NoBookFoundException
-Klasse wird verwendet, um den Fall zu behandeln, in dem keine Bücher in der Datenbank gefunden werden. Wir stellen auch eine benutzerdefinierte Fehlermeldung für die Ausnahme bereit.
Im Exceptions
-Ordner erstellen Sie eine neue Datei namens BookDoesNotExistException.cs
und fügen Sie den folgenden Code hinzu:
namespace bookapi_minimal.Exceptions
{
public class BookDoesNotExistException : Exception
{
private int id { get; set; }
public BookDoesNotExistException(int id) : base($"Book with id {id} does not exist")
{
this.id = id;
}
}
}
In diesem Code erstellen wir eine benutzerdefinierte Ausnahme-Klasse namens BookDoesNotExistException
, die von der Exception
-Klasse erbt. Die BookDoesNotExistException
-Klasse wird verwendet, um den Fall zu behandeln, in dem ein Buch mit der angegebenen ID nicht in der Datenbank existiert. Wir stellen auch eine benutzerdefinierte Fehlermeldung für die Ausnahme bereit.
Im Exceptions
-Ordner erstellen Sie eine neue Datei namens GlobalExceptionHandler.cs
und fügen Sie den folgenden Code hinzu:
// Exceptions/GlobalExceptionHandler.cs
using System.Net;
using bookapi_minimal.Contracts;
using Microsoft.AspNetCore.Diagnostics;
namespace bookapi_minimal.Exceptions
{
// Globale Ausnahmebehandlungsklasse, die IExceptionHandler implementiert
public class GlobalExceptionHandler : IExceptionHandler
{
private readonly ILogger<GlobalExceptionHandler> _logger;
// Konstruktor zur Initialisierung des Loggers
public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
{
_logger = logger;
}
// Methode zur asynchronen Handhabung von Ausnahmen
public async ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
// Protokollieren der Ausnahmedetails
_logger.LogError(exception, "An error occurred while processing your request");
var errorResponse = new ErrorResponse
{
Message = exception.Message,
Title = exception.GetType().Name
};
// Bestimmen des Statuscodes basierend auf dem Typ der Ausnahme
switch (exception)
{
case BadHttpRequestException:
errorResponse.StatusCode = (int)HttpStatusCode.BadRequest;
break;
case NoBookFoundException:
case BookDoesNotExistException:
errorResponse.StatusCode = (int)HttpStatusCode.NotFound;
break;
default:
errorResponse.StatusCode = (int)HttpStatusCode.InternalServerError;
break;
}
// Setzen des Antwortstatuscodes
httpContext.Response.StatusCode = errorResponse.StatusCode;
// Schreiben der Fehlerantwort als JSON
await httpContext.Response.WriteAsJsonAsync(errorResponse, cancellationToken);
// Rückgabe von true, um anzuzeigen, dass die Ausnahme behandelt wurde
return true;
}
}
}
Lassen Sie uns den obigen Code analysieren:
-
Wir definieren eine Klasse namens
GlobalExceptionHandler
, die dasIExceptionHandler
-Interface implementiert. DasIExceptionHandler
-Interface wird verwendet, um Ausnahmen global in der Anwendung zu behandeln. -
Die
GlobalExceptionHandler
-Klasse enthält einen Konstruktor, der dieILogger<GlobalExceptionHandler>
-Abhängigkeit initialisiert. DerILogger
wird zur Protokollierung von Informationen und Fehlern verwendet. -
Die Methode
TryHandleAsync
wird verwendet, um Ausnahmen asynchron zu behandeln. Diese Methode akzeptiert denHttpContext
,Exception
undCancellationToken
als Parameter. -
Wir protokollieren die Ausnahmedetails mit der
ILogger
-Abhängigkeit. -
Wir erstellen ein
ErrorResponse
-Objekt, um die Fehlerantwort darzustellen, die von der API zurückgegeben wird. DasErrorResponse
-Objekt enthält die Fehlermeldung, den Titel und den Statuscode. -
Wir bestimmen den Statuscode basierend auf der Art der Ausnahme. Wenn die Ausnahme eine
BadHttpRequestException
ist, setzen wir den Statuscode aufBadRequest
. Wenn die Ausnahme eineNoBookFoundException
oderBookDoesNotExistException
ist, setzen wir den Statuscode aufNotFound
. Andernfalls setzen wir den Statuscode aufInternalServerError
. -
Wir setzen den Antwortstatuscode über die Eigenschaft
httpContext.Response.StatusCode
. -
Wir schreiben die Fehlerantwort als JSON mit der Methode
httpContext.Response.WriteAsJsonAsync
. -
Wir geben
true
zurück, um anzuzeigen, dass die Ausnahme erfolgreich behandelt wurde.
Nun, da wir die Ausnahmeklassen erstellt haben, registrieren wir den GlobalExceptionHandler
im Abhängigkeitsinjektionscontainer. Da wir eine Erweiterungsmethode für die Registrierung von Diensten im Abhängigkeitsinjektionscontainer erstellt haben, fügen wir den GlobalExceptionHandler
zur Klasse ServiceExtensions
hinzu.
Aktualisieren Sie die Klasse ServiceExtensions
im Ordner Extensions
wie folgt:
// Extensions/ServiceExtensions.cs
//...
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.AddProblemDetails();
//...
Die Methode AddExceptionHandler
registriert den GlobalExceptionHandler
im Abhängigkeitsinjektionscontainer. Die Methode AddProblemDetails
registriert die Klasse ProblemDetails
im Abhängigkeitsinjektionscontainer.
Nun da wir den GlobalExceptionHandler
im Abhängigkeitsinjektionscontainer registriert haben, können wir ihn verwenden, um Ausnahmen global in unserer Anwendung zu behandeln. Im nächsten Abschnitt werden wir die API-Endpunkte erstellen, um mit den Buchdaten zu interagieren.
Wie man die API-Endpunkte erstellt
Im Kontext von Minimal-APIs in ASP.NET Core gibt es viele Möglichkeiten, Ihre Endpunkte einzurichten.
Sie können sie direkt in Ihrer Datei Program.cs
definieren. Aber wenn Ihr Projekt wächst und Sie mehr Endpunkte oder Funktionalitäten hinzufügen müssen, ist es hilfreich, Ihren Code besser zu organisieren. Eine Möglichkeit, dies zu erreichen, besteht darin, eine separate Klasse zu erstellen, um alle Endpunkte zu verwalten.
Wie bereits oben besprochen, verwenden Minimal-APIs keine Controller oder Ansichten wie traditionelle ASP.NET Core-Anwendungen. Stattdessen verwenden sie Methoden wie MapGet
, MapPost
, MapPut
und MapDelete
, um HTTP-Methoden und Routen für API-Endpunkte zu definieren.
Um zu beginnen, navigieren Sie zum Ordner Endpoints
und erstellen Sie eine neue Datei namens BookEndpoints.cs
. Fügen Sie den folgenden Code in die Datei ein:
// Endpoints/BookEndpoints.cs
namespace bookapi_minimal.Endpoints
{
public static class BookEndPoint
{
public static IEndpointRouteBuilder MapBookEndPoint(this IEndpointRouteBuilder app)
{
return app;
}
}
}
Die Klasse BookEndpoints
enthält eine Methode MapBookEndPoint
, die ein IEndpointRouteBuilder
-Objekt zurückgibt. Das IEndpointRouteBuilder
-Objekt wird verwendet, um die HTTP-Methoden und -Routen für die API-Endpunkte zu definieren. In den nächsten Abschnitten werden wir die API-Endpunkte für das Erstellen
, Lesen
, Aktualisieren
und Löschen
von Büchern definieren.
Wie man den AddBookAsync
-Bücher-Endpunkt erstellt
In diesem Abschnitt werden wir den AddBookAsync
-Endpunkt erstellen. Dieser Endpunkt wird ein Book
-Objekt als JSON-Nutzlast akzeptieren und es zur Datenbank hinzufügen. Wir werden die Methode MapPost
verwenden, um die HTTP-Methode und -Route für diesen Endpunkt zu definieren.
Fügen Sie den folgenden Code zur Klasse BookEndpoints
hinzu:
// Endpoints/BookEndpoints.cs
//...
// Endpunkt zum Hinzufügen eines neuen Buches
app.MapPost("/books", async (CreateBookRequest createBookRequest, IBookService bookService) =>
{
var result = await bookService.AddBookAsync(createBookRequest);
return Results.Created($"/books/{result.Id}", result);
});
//...
-
Routendefinition: Die Methode MapPost definiert die Route für den Endpunkt als
/books
. -
Anforderungsmodell: Der Endpunkt akzeptiert ein
CreateBookRequest
-Objekt als JSON-Nutzlast. DasCreateBookRequest
-Objekt enthält die Daten, die zum Erstellen eines neuen Buches erforderlich sind. -
Antwortmodell: Der Endpunkt gibt ein
Book
-Objekt als JSON-Nutzlast zurück. DasBook
-Objekt enthält die Daten für das neu erstellte Buch. -
Rückgabewert: Der Endpunkt gibt ein
Created
-Ergebnis zurück. DasCreated
-Ergebnis enthält den Speicherort des neu erstellten Buchs und dasBook
-Objekt.
Wie man den GetBookAsync
Buch-Endpunkt erstellt
In diesem Abschnitt werden wir den GetBookAsync
-Endpunkt erstellen. Dieser Endpunkt wird eine Buch-ID als Abfrageparameter akzeptieren und das Buch mit der angegebenen ID zurückgeben. Wir werden die MapGet
-Methode verwenden, um die HTTP-Methode und die Route für diesen Endpunkt zu definieren.
Fügen Sie den folgenden Code zur BookEndpoints
-Klasse hinzu:
// Endpoints/BookEndpoints.cs
// ...
// Endpunkt zum Abrufen aller Bücher
app.MapGet("/books", async (IBookService bookService) =>
{
var result = await bookService.GetBooksAsync();
return Results.Ok(result);
});
//...
-
Routendefinition: Die Methode MapGet definiert die Route für den Endpunkt als
/books
. -
Anforderungsmodell: Der Endpunkt akzeptiert ein
Book
-Objekt als JSON-Nutzlast. DasBook
-Objekt enthält die Daten, die zum Erstellen eines neuen Buches erforderlich sind. -
Antwortmodell: Der Endpunkt gibt ein
Book
-Objekt als JSON-Nutzlast zurück. DasBook
-Objekt enthält die Daten für das neu erstellte Buch. -
Rückgabewert: Der Endpunkt gibt ein
Ok
-Ergebnis zurück. DasOk
-Ergebnis enthält dasBook
-Objekt.
Wie man den GetBookByIdAsync
Buch-Endpunkt erstellt
In diesem Abschnitt werden wir den Endpunkt GetBookByIdAsync
erstellen. Dieser Endpunkt akzeptiert eine Buch-ID als Routenparameter und gibt das Buch mit der angegebenen ID zurück. Wir verwenden die Methode MapGet
, um die HTTP-Methode und die Route für diesen Endpunkt zu definieren.
Fügen Sie den folgenden Code zur Klasse BookEndpoints
hinzu:
// Endpoints/BookEndpoints.cs
//...
// Endpunkt zum Abrufen eines Buches nach ID
app.MapGet("/books/{id:guid}", async (Guid id, IBookService bookService) =>
{
var result = await bookService.GetBookByIdAsync(id);
return result != null ? Results.Ok(result) : Results.NotFound();
});
//...
-
Routendefinition: Die Methode MapGet definiert die Route für den Endpunkt als
/books/{id:guid}
. Der Parameter{id:guid}
gibt an, dass derid
-Parameter eine GUID sein sollte. -
Anforderungsmodell: Der Endpunkt akzeptiert ein
Book
-Objekt als JSON-Nutzlast. DasBook
-Objekt enthält die Daten, die zum Erstellen eines neuen Buches erforderlich sind. -
Antwortmodell: Der Endpunkt gibt ein
Book
-Objekt als JSON-Nutzlast zurück. DasBook
-Objekt enthält die Daten für das neu erstellte Buch. -
Rückgabewert: Der Endpunkt gibt ein
Ok
-Ergebnis zurück, wenn das Buch gefunden wird. DasNotFound
-Ergebnis wird zurückgegeben, wenn das Buch nicht gefunden wird.
Wie man den UpdateBookAsync
-Buch-Endpunkt erstellt
In diesem Abschnitt werden wir den UpdateBookAsync
-Endpunkt erstellen. Dieser Endpunkt wird eine Buch-ID als Routenparameter und ein Book
-Objekt als JSON-Nutzlast akzeptieren und das Buch mit der angegebenen ID aktualisieren. Wir werden die MapPut
-Methode verwenden, um die HTTP-Methode und die Route für diesen Endpunkt zu definieren.
Fügen Sie den folgenden Code zur BookEndpoints
-Klasse hinzu:
// Endpoints/BookEndpoints.cs
//...
// Endpunkt zum Aktualisieren eines Buches nach ID
app.MapPut("/books/{id:guid}", async (Guid id, UpdateBookRequest updateBookRequest, IBookService bookService) =>
{
var result = await bookService.UpdateBookAsync(id, updateBookRequest);
return result != null ? Results.Ok(result) : Results.NotFound();
});
//...
-
Routendefinition: Die MapPut-Methode definiert die Route für den Endpunkt als
/books/{id:guid}
. Der{id:guid}
-Parameter gibt an, dass derid
-Parameter eine GUID sein sollte. -
Anforderungsmodell: Der Endpunkt akzeptiert ein
Book
-Objekt als JSON-Nutzlast. DasBook
-Objekt enthält die Daten, die erforderlich sind, um ein neues Buch zu erstellen. -
Antwortmodell: Der Endpunkt gibt ein
Book
-Objekt als JSON-Nutzlast zurück. DasBook
-Objekt enthält die Daten für das neu erstellte Buch. -
Rückgabewert: Der Endpunkt gibt ein
Ok
-Ergebnis zurück, wenn das Buch gefunden wird. DasNotFound
-Ergebnis wird zurückgegeben, wenn das Buch nicht gefunden wird.
Wie man den Endpunkt DeleteBookAsync
für Bücher erstellt
In diesem Abschnitt werden wir den Endpunkt DeleteBookAsync
erstellen. Dieser Endpunkt wird eine Buch-ID als Routenparameter akzeptieren und das Buch mit der angegebenen ID löschen. Wir werden die Methode MapDelete
verwenden, um die HTTP-Methode und die Route für diesen Endpunkt zu definieren.
Fügen Sie den folgenden Code zur Klasse BookEndpoints
hinzu:
// Endpoints/BookEndpoints.cs
//...
// Endpunkt zum Löschen eines Buches nach ID
app.MapDelete("/books/{id:guid}", async (Guid id, IBookService bookService) =>
{
var result = await bookService.DeleteBookAsync(id);
return result ? Results.NoContent() : Results.NotFound();
});
//...
-
Routendefinition: Die Methode MapDelete definiert die Route für den Endpunkt als
/books/{id:guid}
. Der Parameter{id:guid}
gibt an, dass derid
-Parameter eine GUID sein sollte. -
Anforderungsmodell: Der Endpunkt akzeptiert ein
Book
-Objekt als JSON-Nutzlast. DasBook
-Objekt enthält die Daten, die zum Erstellen eines neuen Buches erforderlich sind. -
Antwortmodell: Der Endpunkt gibt ein
Book
-Objekt als JSON-Nutzlast zurück. DasBook
-Objekt enthält die Daten für das neu erstellte Buch. -
Rückgabewert: Der Endpunkt gibt ein
NoContent
-Ergebnis zurück, wenn das Buch erfolgreich gelöscht wurde. DasNotFound
-Ergebnis wird zurückgegeben, wenn das Buch nicht gefunden wird.
Nun haben wir alle Methoden für die Buch-Endpunkte definiert. Daher sollte Ihre Endpunkt-Klasse so aussehen:
// Endpoints/BookEndpoints.cs
using bookapi_minimal.Contracts;
using bookapi_minimal.Interfaces;
namespace bookapi_minimal.Endpoints
{
public static class BookEndPoint
{
public static IEndpointRouteBuilder MapBookEndPoint(this IEndpointRouteBuilder app)
{
// Definieren der Endpunkte
// Endpunkt zum Hinzufügen eines neuen Buchs
app.MapPost("/books", async (CreateBookRequest createBookRequest, IBookService bookService) =>
{
var result = await bookService.AddBookAsync(createBookRequest);
return Results.Created($"/books/{result.Id}", result);
});
// Endpunkt zum Abrufen aller Bücher
app.MapGet("/books", async (IBookService bookService) =>
{
var result = await bookService.GetBooksAsync();
return Results.Ok(result);
});
// Endpunkt zum Abrufen eines Buchs nach ID
app.MapGet("/books/{id:guid}", async (Guid id, IBookService bookService) =>
{
var result = await bookService.GetBookByIdAsync(id);
return result != null ? Results.Ok(result) : Results.NotFound();
});
// Endpunkt zum Aktualisieren eines Buchs nach ID
app.MapPut("/books/{id:guid}", async (Guid id, UpdateBookRequest updateBookRequest, IBookService bookService) =>
{
var result = await bookService.UpdateBookAsync(id, updateBookRequest);
return result != null ? Results.Ok(result) : Results.NotFound();
});
// Endpunkt zum Löschen eines Buchs nach ID
app.MapDelete("/books/{id:guid}", async (Guid id, IBookService bookService) =>
{
var result = await bookService.DeleteBookAsync(id);
return result ? Results.NoContent() : Results.NotFound();
});
return app;
}
}
}
Herzlichen Glückwunsch! Sie haben alle Endpunkte für die Buch-API erstellt. Die Endpunkte verarbeiten die CRUD-Operationen für Bücher und geben die entsprechenden Antworten basierend auf der Anfrage und den Daten zurück.
Wie man die Endpunkte registriert
Nach der Definition der API-Endpunkte für die Buch-API ist der nächste Schritt, diese Endpunkte in der Datei Program.cs
zu registrieren. Wir werden die Methode MapBookEndpoints
verwenden, um die Buch-Endpunkte zu registrieren.
Wir sollten auch unsere Program.cs
-Klasse aufräumen, um sicherzustellen, dass sie organisiert und wartbar bleibt.
// Program.cs
using System.Reflection;
using bookapi_minimal.Endpoints;
using bookapi_minimal.Services;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.AddApplicationServices();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c=>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Mimal API", Version = "v1", Description = "Showing how you can build minimal " +
"api with .net" });
// Setzen des Kommentarpfads für das Swagger-JSON und die Benutzeroberfläche.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
var app = builder.Build();
// Konfiguration des HTTP-Anforderungspipelines.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseExceptionHandler();
app.MapGroup("/api/v1/")
.WithTags(" Book endpoints")
.MapBookEndPoint();
app.Run();
Lassen Sie uns die Schlüsselkomponenten der Datei Program.cs
analysieren:
-
AddApplicationServices: Diese Methode registriert die notwendigen Dienste für die API. Es handelt sich um eine von uns zuvor erstellte Erweiterungsmethode, um Dienste in den Abhängigkeitsinjektionscontainer einzufügen.
-
AddSwaggerGen: Diese Methode registriert den Swagger-Generator, der zur Erstellung der Swagger-Dokumentation für die API verwendet wird. Wir geben den Titel, die Version und die Beschreibung der API im Swagger-Dokument an.
-
MapGroup: Diese Methode gruppiert die Endpunkte. Sie nimmt einen Pfad als Parameter und gibt ein
IEndpointRouteBuilder
-Objekt zurück. Wir verwenden die MethodeWithTags
, um Tags zu den Endpunkten hinzuzufügen, und die MethodeMapBookEndpoints
, um die Buchendpunkte zu registrieren. -
Run: Diese Methode startet die Anwendung.
Um die Swagger-Dokumentation zu aktivieren, müssen Sie die Eigenschaft GenerateDocumentationFile
zu Ihrer .csproj
-Datei hinzufügen. In diesem Beispiel ist die Datei bookapi-minimal.csproj
benannt, aber der Name kann je nach Projekt variieren.
Fügen Sie die folgende Zeile zu Ihrer .csproj
-Datei hinzu:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
Am Ende sollte die bookapi-minimal.csproj so aussehen:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<RootNamespace>bookapi_minimal</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
</Project>
Nun da wir die Buchendpunkte in der Datei Program.cs
registriert haben, können wir die Anwendung ausführen und die API-Endpunkte mit Swagger testen.
Wenn Sie die Anwendung ausführen, sollten Sie die Swagger-Dokumentation unter folgender URL sehen: https://localhost:5001/swagger/index.html
. Die Swagger-Dokumentation bietet Informationen zu den API-Endpunkten, Anfrage- und Antwortmodellen und ermöglicht es Ihnen, die Endpunkte direkt aus dem Browser zu testen. Sie sollten etwas wie folgt sehen:
Herzlichen Glückwunsch! Sie haben die Geschäftslogik für den Buchdienst implementiert, benutzerdefinierte Ausnahmen erstellt, API-Endpunkte definiert und die Endpunkte in der Datei Program.cs
registriert. Sie haben auch die Swagger-Dokumentation aktiviert, um die API-Endpunkte zu testen.
Wie man Startdaten zur Datenbank hinzufügt
Ein weiterer wichtiger Schritt ist das Befüllen der Datenbank mit Startdaten, wenn die Anwendung startet. Diese Startdaten füllen die Datenbank, sodass Sie Ihre API-Endpunkte testen können, ohne manuell Daten hinzuzufügen.
Lassen Sie uns einige Startdaten hinzufügen, bevor wir Migrationen durchführen und unsere API-Endpunkte testen.
Um dies zu erreichen, werden wir eine neue Klasse in unserem Konfigurationsordner namens BookTypeConfigurations
erstellen und den folgenden Code hinzufügen:
using bookapi_minimal.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace bookapi_minimal.Configurations
{
public class BookTypeConfigurations : IEntityTypeConfiguration<BookModel>
{
public void Configure(EntityTypeBuilder<BookModel> builder)
{
// Konfigurieren Sie den Tabellennamen
builder.ToTable("Books");
// Konfigurieren Sie den Primärschlüssel
builder.HasKey(x => x.Id);
// Konfigurieren Sie Eigenschaften
builder.Property(x => x.Id).ValueGeneratedOnAdd();
builder.Property(x => x.Title).IsRequired().HasMaxLength(100);
builder.Property(x => x.Author).IsRequired().HasMaxLength(100);
builder.Property(x => x.Description).IsRequired().HasMaxLength(500);
builder.Property(x => x.Category).IsRequired().HasMaxLength(100);
builder.Property(x => x.Language).IsRequired().HasMaxLength(50);
builder.Property(x => x.TotalPages).IsRequired();
// Startdaten
builder.HasData(
new BookModel
{
Id = Guid.NewGuid(),
Title = "The Alchemist",
Author = "Paulo Coelho",
Description = "The Alchemist follows the journey of an Andalusian shepherd",
Category = "Fiction",
Language = "English",
TotalPages = 208
},
new BookModel
{
Id = Guid.NewGuid(),
Title = "To Kill a Mockingbird",
Author = "Harper Lee",
Description = "A novel about the serious issues of rape and racial inequality.",
Category = "Fiction",
Language = "English",
TotalPages = 281
},
new BookModel
{
Id = Guid.NewGuid(),
Title = "1984",
Author = "George Orwell",
Description = "A dystopian social science fiction novel and cautionary tale about the dangers of totalitarianism. ",
Category = "Fiction",
Language = "English",
TotalPages = 328
}
);
}
}
}
Lassen Sie uns den obigen Code analysieren:
In Entity Framework Core können Sie die IEntityTypeConfiguration
-Schnittstelle verwenden, um den Entitätstyp und Startdaten für die Datenbank zu konfigurieren. Die BookTypeConfigurations
-Klasse implementiert die IEntityTypeConfiguration<BookModel>
-Schnittstelle und bietet die Konfiguration für die BookModel
-Entität.
-
Konfigurationsmethode: Diese Methode wird verwendet, um den
BookModel
-Entitätstyp zu konfigurieren. Sie definiert den Tabellennamen, den Primärschlüssel und die Eigenschaften für dieBookModel
-Entität.-
Tabellenname: Die
ToTable
-Methode gibt den Namen der zu erstellenden Tabelle in der Datenbank an. In diesem Fall wird der Tabellenname auf „Books“ gesetzt. -
Primärschlüssel: Die
HasKey
-Methode gibt den Primärschlüssel für dieBookModel
-Entität an. Der Primärschlüssel wird auf dieId
-Eigenschaft gesetzt. -
Eigenschaften: Die
Property
-Methode konfiguriert die Eigenschaften derBookModel
-Entität. Sie gibt den Datentyp, die Länge und die Einschränkungen für jede Eigenschaft an.
-
-
Startdaten: Die Methode
HasData
füllt die Datenbank mit anfänglichen Daten. Sie erstellt dreiBookModel
-Objekte mit Beispieldaten zum Testen der API-Endpunkte.
Nun, da wir die Klasse BookTypeConfigurations
erstellt haben, müssen wir diese Konfiguration in der Klasse ApplicationContext
registrieren. Dies stellt sicher, dass die Konfiguration angewendet wird, wenn die Datenbank erstellt oder migriert wird.
Wir sind endlich fast bereit, unsere API zu testen. Aber bevor wir das tun, müssen wir Migrationen durchführen, um die Datenbank zu erstellen und die Seed-Daten anzuwenden.
Erinnern Sie sich, dass wir unseren Datenbankverbindungsstring in der Datei appsettings.json
hinzugefügt haben? Nun führen wir eine Migration durch und aktualisieren später unsere Datenbank, damit die Migration wirksam wird.
Wie man eine Migration durchführt
Mit Migrations können Sie das Datenbankschema basierend auf Änderungen an Ihren Modellklassen aktualisieren. In Entity Framework Core können Sie den Befehl dotnet ef migrations add
verwenden, um eine neue Migration zu erstellen, die diese Änderungen widerspiegelt.
Um eine Migration durchzuführen, führen Sie den folgenden Befehl im Terminal aus:
dotnet ef migrations add InitialCreate
Wenn der Befehl erfolgreich ist, sollten Sie eine Ausgabe ähnlich wie diese sehen:
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'
Sie werden nun einen neuen Ordner namens Migrations
in Ihrem Projekt sehen. Dieser Ordner enthält die Migrationsdateien, die basierend auf den Änderungen an Ihren Modellklassen erstellt wurden.
Wie man die Datenbank aktualisiert
Nachdem Sie die Migration erstellt haben, müssen Sie die Migration anwenden, um das Datenbankschema zu aktualisieren. Sie können den Befehl dotnet ef database update
verwenden, um die Migration anzuwenden und die Datenbank zu aktualisieren. Stellen Sie sicher, dass der SQL-Server läuft.
Führen Sie den folgenden Befehl im Terminal aus:
dotnet ef database update
Dies wird das Datenbankschema basierend auf den Änderungen an Ihren Modellklassen aktualisieren. Stellen Sie sicher, dass es keine Fehler in Ihrem Datenbankverbindungsstring gibt.
Wie man die API-Endpunkte testet
Jetzt können wir unsere Endpunkte mit Swagger testen. Dazu führen wir die Anwendung aus, indem wir den folgenden Befehl im Terminal ausführen:
dotnet run
Dies startet unsere Anwendung. Sie können Ihren Browser öffnen und zu https://localhost:5001/swagger/index.html
navigieren, um auf die Swagger-Dokumentation zuzugreifen. Sie sollten eine Liste der API-Endpunkte, Anfrage- und Antwortmodelle sowie die Möglichkeit sehen, die Endpunkte direkt aus dem Browser zu testen.
Wenn Ihre Portnummer anders als 5001
ist, machen Sie sich keine Sorgen – es wird trotzdem funktionieren. Die Portnummer kann je nach Art des verwendeten Geräts variieren, aber sie wird immer noch das gleiche Ergebnis erzielen.
Wie man den Alle Bücher abrufen
-Endpunkt testet
Um den Alle Bücher abrufen
-Endpunkt zu testen, folgen Sie diesen Schritten:
-
In der Swagger-Dokumentation klicken Sie auf den
GET /api/v1/books
-Endpunkt. -
Klicken Sie auf die Schaltfläche
Try it out
. -
Klicken Sie auf die Schaltfläche
Ausführen
.
Dies sendet eine Anfrage an die API, um alle Bücher in der Datenbank abzurufen.
Sie sollten die Antwort der API sehen, die die Liste der in der Datenbank angelegten Bücher enthält.
Das folgende Bild zeigt die Antwort der API:
Wie man den Buch nach ID abrufen
-Endpunkt testet
Um den Buch nach ID abrufen
-Endpunkt zu testen, folgen Sie diesen Schritten:
-
In der Swagger-Dokumentation klicken Sie auf den
GET /api/v1/books/{id}
-Endpunkt. -
Geben Sie die ID eines Buches in das
id
-Feld ein. Sie können eine der Buch-IDs verwenden, die in der Datenbank eingespielt wurde. -
Klicken Sie auf die Schaltfläche
Ausprobieren
.
Dies sendet eine Anfrage an die API, um das Buch mit der angegebenen ID abzurufen. Sie sollten die Antwort der API sehen, die das Buch mit der angegebenen ID enthält.
Das folgende Bild zeigt die Antwort der API:
Wie man den Buch hinzufügen
-Endpunkt testet
Um den Buch hinzufügen
-Endpunkt zu testen, folgen Sie diesen Schritten:
-
In der Swagger-Dokumentation klicken Sie auf den
POST /api/v1/books
-Endpunkt. -
Klicken Sie auf die Schaltfläche
Ausprobieren
. -
Geben Sie die Buchdetails im Anfragekörper ein.
-
Klicken Sie auf die Schaltfläche
Ausführen
.
Dadurch wird eine Anfrage an die API gesendet, um ein neues Buch in die Datenbank hinzuzufügen.
Sie sollten die Antwort der API sehen, die das neu erstellte Buch enthält.
Das folgende Bild zeigt die Antwort der API:
Wie man den Buch aktualisieren
Endpunkt testet
Um den Buch aktualisieren
Endpunkt zu testen, folgen Sie diesen Schritten:
-
In der Swagger-Dokumentation klicken Sie auf den
PUT /api/v1/books/{id}
Endpunkt. -
Geben Sie die ID eines Buches in das
id
Feld ein. Sie können die ID eines der Bücher verwenden, die wir gerade hinzugefügt haben. -
Klicken Sie auf die Schaltfläche
Ausprobieren
.
Dadurch wird eine Anfrage an die API gesendet, um das Buch mit der angegebenen ID zu aktualisieren.
Sie sollten die Antwort der API sehen, die das aktualisierte Buch enthält.
Das folgende Bild zeigt die Antwort der API:
Wie man den Buch löschen
Endpunkt testet
Um den Buch löschen
Endpunkt zu testen, folgen Sie diesen Schritten:
-
In der Swagger-Dokumentation klicken Sie auf den
DELETE /api/v1/books/{id}
Endpunkt. -
Geben Sie die ID eines Buches in das
id
-Feld ein. Sie können eine der IDs der Bücher verwenden, die wir gerade hinzugefügt haben, oder die vorgegebenen Daten. -
Klicken Sie auf die Schaltfläche
Try it out
.
Dadurch wird eine Anfrage an die API gesendet, um das Buch mit der angegebenen ID zu löschen.
Das folgende Bild zeigt die Antwort der API:
Herzlichen Glückwunsch! Sie haben alle CRUD-Vorgänge für Bücher implementiert und die API-Endpunkte mit Swagger getestet, um zu überprüfen, dass sie wie erwartet funktionieren. Sie können nun auf dieser Grundlage aufbauen, um mehr Funktionen und Funktionalitäten zu Ihrer API hinzuzufügen.
Schlussfolgerung
Dieses Handbuch hat untersucht, wie man eine minimale API in ASP.NET Core mit .NET 8 erstellt. Wir haben eine umfassende Buch-API gebaut, die CRUD-Vorgänge unterstützt, benutzerdefinierte Ausnahmen implementiert, API-Endpunkte definiert und registriert sowie die Swagger-Dokumentation für einfache Tests aktiviert.
Nach diesem Tutorial haben Sie ein solides Fundament für die Erstellung von minimalen APIs mit ASP.NET Core erworben. Sie können dieses Wissen nun anwenden und robuste APIs für verschiedene Bereiche und Industrien erstellen.
Ich hoffe, dieses Tutorial war sowohl hilfreich als auch informativ. Vielen Dank fürs Lesen!
Stellen Sie gerne eine Verbindung mit mir über soziale Medien her:
Source:
https://www.freecodecamp.org/news/create-a-minimal-api-in-net-core-handbook/