Minimal APIs zijn een spannende functie geïntroduceerd in .NET 6, ontworpen om de manier waarop je APIs maakt te revolutioneren.

Stel je voor dat je robuuste APIs bouwt met minimaal code en geen boilerplate—geen gedoe meer met controllers, routing of middleware. Dat is wat minimal APIs je toestaan te doen. Het idee met deze APIs is om het ontwikkelingsproces te stroomlijnen, waardoor het uiterst eenvoudig en efficiënt wordt.

In dit artikel duiken we in de wereld van minimal APIs in .NET 8 en leiden we je door het proces van het maken van een volledig functionele boekwinkel API. Je leert hoe je alle boeken kunt ophalen, een boek kunt opvragen op basis van zijn ID, nieuwe boeken kunt toevoegen en zelfs boeken kunt verwijderen. Laten we beginnen.

Inhoudsopgave

Vereisten

Voor we beginnen, zorg ervoor dat je de volgende vereisten op je machine hebt geïnstalleerd:

Als alternatief kunt u Visual Studio 2022 gebruiken, dat ingebouwde ondersteuning biedt voor .NET 8. Maar in dit artikel zullen we Visual Studio Code gebruiken. Het is lichtgewicht, gemakkelijk te gebruiken en cross-platform.

We zullen Swagger UI gebruiken om onze API te testen. Swagger UI is een krachtig hulpmiddel dat u in staat stelt om direct vanuit uw browser met uw API te interageren. Het biedt een gebruiksvriendelijke interface om uw API-eindpunten te testen, waardoor het gemakkelijker wordt om uw API te testen en te debuggen.

Wanneer u een nieuw project maakt, zal het automatisch de benodigde pakketten installeren en het project configureren om Swagger UI te gebruiken. .NET 8 bevat Swagger UI standaard, dus of u nu uw toepassing maakt in Visual Studio of met .NET, Swagger UI zal voor u worden geconfigureerd.

Start uw toepassing, en de Swagger UI zal automatisch openen in uw browser – maar aangezien we VS Code gebruiken, moeten we op het poortnummer in onze terminal klikken.

Je kunt de broncode voor dit project vinden op GitHub.

Inleiding tot Minimal APIs

Stel je voor dat je werkt in een codebase met talloze endpoints, waardoor deze vrij groot en complex is. Traditioneel houdt het bouwen van een API in ASP.NET Core in dat je controllers, routing, middleware en een aanzienlijke hoeveelheid boilerplate-code gebruikt. Maar er zijn twee benaderingen voor het bouwen van een API in ASP.NET Core: de traditionele manier en de minimalistische manier.

De traditionele manier is bekend bij de meeste ontwikkelaars en omvat controllers en uitgebreide infrastructuurcode. De minimalistische manier, geïntroduceerd in .NET 6, stelt je in staat om APIs te maken met minimale code en geen boilerplate. Deze benadering vereenvoudigt het ontwikkelingsproces, waardoor je je kunt concentreren op het schrijven van bedrijfslogica in plaats van het omgaan met infrastructuurcode.

Minimal APIs zijn lichtgewicht, snel en perfect voor het bouwen van kleine tot middelgrote APIs. Ze zijn ideaal voor prototyping, het bouwen van microservices of het maken van eenvoudige APIs die niet veel complexiteit vereisen. In deze handleiding verkennen we de wereld van minimal APIs in .NET 6 en leren we hoe we een volledig functionele boekwinkel API vanaf nul kunnen maken.

Hoe een Minimal API te maken

Het maken van een minimal API is eenvoudig met behulp van de dotnet CLI, omdat het standaard sjabloon al een minimal API is. Maar als je Visual Studio gebruikt, moet je de boilerplate-code die bij het project sjabloon wordt geleverd, verwijderen.

Laten we beginnen met het gebruik van de dotnet CLI om een minimaal API-project te maken.


dotnet new webapi  -n BookStoreApi

De opdracht dotnet new webapi maakt een nieuw minimaal API-project genaamd BookStoreApi. Dit project bevat de benodigde bestanden en mappen om je op weg te helpen.

Laten we de projectstructuur verkennen:

  • Program.cs: Het startpunt van de applicatie, waar de host wordt geconfigureerd.

  • bookapi-minimal.sln: Het oplossingsbestand dat het project bevat.

  • bookapi-minimal.http: Een bestand dat voorbeeld-HTTP-verzoeken bevat om de API te testen.

  • bookapi-minimal.csproj: Het projectbestand dat de projectconfiguratie bevat.

  • appsettings.json: Het configuratiebestand dat applicatie-instellingen opslaat.

  • appsettings.Development.json: Het configuratiebestand voor de ontwikkelomgeving.

Wanneer je het program.cs-bestand opent, merk je dat de code minimaal is. Het Program.cs-bestand bevat de volgende code:


var builder = WebApplication.CreateBuilder(args);

// Voeg services toe aan de container.
// Meer informatie over het configureren van Swagger/OpenAPI op https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configureer de HTTP-verzoekpijplijn.
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);
}

Als je de code nog niet volledig begrijpt, maak je geen zorgen—we zullen het in detail bespreken in de volgende secties. Het belangrijkste is dat minimale API’s heel weinig code vereisen, wat een van hun belangrijkste voordelen is.

De standaardcode zet een eenvoudige weersvoorspellings-API op die je kunt gebruiken om je setup te testen. Het genereert een lijst van weersvoorspellingen en retourneert deze wanneer je een GET-verzoek doet naar het /weatherforecast-eindpunt. Ook bevat de code Swagger UI om je te helpen de API te testen.

Let vooral op de app.MapGet-methode, die een route toewijst aan een handlerfunctie. In dit geval wijst het de /weatherforecast-route toe aan een functie die een lijst van weersvoorspellingen retourneert. We zullen soortgelijke methoden gebruiken om onze eigen eindpunten te maken in de volgende secties.

Voordat we beginnen met het maken van onze projectmapstructuur, laten we de HTTP-methoden begrijpen in zowel Controller-based als Minimal API’s.

HTTP-methoden in Controller-based en Minimal API’s

In een Controller-based benadering, wat de traditionele manier is om web API’s te maken, moet je een controllerklasse maken en methoden definiëren voor elke HTTP-methode. Bijvoorbeeld:

  • Om een GET methode te creëren, gebruik je de [HttpGet] attribuut.

  • Om een POST methode te creëren, gebruik je de [HttpPost] attribuut.

  • Om een PUT methode te creëren, gebruik je de [HttpPut] attribuut.

  • Om een DELETE methode te creëren, gebruik je de [HttpDelete] attribuut.

Dit is hoe eindpunten worden gecreëerd in een Controller-gebaseerde benadering.

In tegenstelling hiermee gebruiken Minimal API’s methoden zoals app.MapGet, app.MapPost, app.MapPut, en app.MapDelete om eindpunten te creëren. Dit is het belangrijkste verschil tussen de twee benaderingen: Controller-gebaseerde API’s gebruiken attributen om eindpunten te definiëren, terwijl Minimal API’s methoden gebruiken.

Nu je begrijpt hoe je HTTP-verzoeken moet afhandelen in zowel Controller-gebaseerde als Minimal API’s, laten we onze projectmapstructuur creëren.

Voordat we onze projectmapstructuur maken, laten we eerst uitvoeren wat we hebben. Zoals we eerder hebben geleerd, wanneer je een project maakt met Visual Studio of .NET CLI, wordt het geleverd met een standaard WeatherForecast-project dat we kunnen uitvoeren en zien op de UI. Laten we het uitvoeren om ervoor te zorgen dat alles werkt voordat we doorgaan met het maken van onze projectmap.

Voer deze opdracht uit:


dotnet run

Je zou de volgende output moeten zien:

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

Dit betekent dat de toepassing draait en luistert op http://localhost:5228. Zoals ik hierboven al heb vermeld, aangezien we de dotnet CLI en Visual Studio Code gebruiken, opent de toepassing niet automatisch de browser voor ons. We moeten dit handmatig doen.

Open je browser en navigeer naar http://localhost:5228/swagger/index.html om de standaardreactie van de API te zien.

Je zou iets dergelijks moeten zien:

Nu is het volgende voor ons om een manier te vinden om onze project te structureren en de nodige bestanden en mappen te maken om te beginnen.

Minimale API-projectbestanden

Om ons project te organiseren, zullen we een gestructureerde maphiërarchie maken. Dit zal helpen om onze code schoon en onderhoudbaar te houden. Hier is de mappenstructuur die we zullen gebruiken:

  • AppContext: Bevat de databasecontext en gerelateerde configuraties.

  • Configuraties: Bevat Entity Framework Core-configuraties en seedgegevens voor de database.

  • Contracten: Bevat Data Transfer Objects (DTO’s) die worden gebruikt in onze toepassing.

  • Eindpunten: Hier definiëren en configureren we onze minimale API-eindpunten.

  • Uitzonderingen: Bevat aangepaste uitzonderingsklassen die worden gebruikt in het project.

  • Extensies: Bevat extensiemethoden die we zullen gebruiken in het project.

  • Modellen: Bevat modellen voor bedrijfslogica.

  • Diensten: Bevat serviceklassen die bedrijfslogica implementeren.

  • Interfaces: Bevat interface-definities die worden gebruikt om onze diensten te mappen.

In Visual Studio Code kun je deze mapstructuur als volgt maken:

- AppContext
- Configurations
- Contracts
- Endpoints
- Exceptions
- Extensions
- Models
- Services
- Interfaces

Na het instellen zou de mapstructuur van je project er als volgt uit moeten zien:

Nu onze projectstructuur is ingesteld, kunnen we doorgaan en onze code gaan schrijven. Laten we beginnen met het maken van onze modellen.

Hoe modellen te maken

In dit gedeelte zullen we modellen maken voor onze applicatie. Modellen zijn de bouwstenen van onze applicatie, die de gegevens vertegenwoordigen waarmee onze applicatie zal werken. Voor ons voorbeeld zullen we een model maken voor een boek.

Om te beginnen, maak een map genaamd Modellen in je projectdirectory. Maak in deze map een bestand met de naam BookModel.cs en voeg de volgende code toe:

// Modellen/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; }
    }
}

Deze BookModel-klasse definieert de eigenschappen die de details van een boek vertegenwoordigen, zoals de titel, auteur, beschrijving, categorie, taal en totaal aantal pagina's. Elke eigenschap is ontworpen om specifieke informatie over het boek vast te houden, waardoor het gemakkelijk is om boekgegevens binnen onze applicatie te beheren en te manipuleren.

Nu we ons model hebben gemaakt, laten we onze databasecontext maken.

Hoe de databasecontext te maken

De databasecontext is een klasse die een sessie met de database vertegenwoordigt. Het is verantwoordelijk voor de interactie met de database en het uitvoeren van databasebewerkingen. In onze applicatie zullen we Entity Framework Core gebruiken om te interageren met onze database.

Installeer de vereiste pakketten

Voor het maken van onze databasecontext moeten we de volgende pakketten installeren:

Je kunt deze pakketten installeren met de volgende commando’s:

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

Controleer Pakketinstallatie

Om te controleren of de pakketten zijn geïnstalleerd, open het bestand bookapi-minimal.csproj in de hoofdmap van je project. Je zou de geïnstalleerde pakketten als volgt vermeld moeten zien:

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

Dit bevestigt dat de pakketten succesvol zijn geïnstalleerd.

Nu kunnen we onze databasecontext maken.

In de AppContext-map, maak een nieuw bestand genaamd ApplicationContext.cs aan en voeg de volgende code toe:

// AppContext/ApplicationContext.cs

using bookapi_minimal.Models;
using Microsoft.EntityFrameworkCore;

namespace bookapi_minimal.AppContext
{

    public class ApplicationContext(DbContextOptions<ApplicationContext> options) : DbContext(options)
    {

        // Standaardschema voor de databasecontext
        private const string DefaultSchema = "bookapi";


       // DbSet om de verzameling boeken in onze database voor te stellen
        public DbSet<BookModel> Books { get; set; }

        // Constructor om de databasecontext te configureren

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.HasDefaultSchema(DefaultSchema);

            modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly);

            modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly);

        }

    }
}

Laten we de bovenstaande code uiteenzetten:

  • We definiëren een klasse genaamd ApplicationContext die erft van DbContext. De klasse DbContext maakt deel uit van Entity Framework Core en vertegenwoordigt een sessie met de database.

  • De constructor accepteert een instantie van DbContextOptions<ApplicationContext>. Deze constructor wordt gebruikt om de databasecontextopties te configureren.

  • We definiëren een eigenschap genaamd Books van het type DbSet<BookModel>. Deze eigenschap vertegenwoordigt de verzameling boeken in onze database.

  • We overschrijven de methode OnModelCreating om het databaseschema te configureren en eventuele configuraties toe te passen die in onze applicatie zijn gedefinieerd.

Nu we onze database-context hebben aangemaakt, laten we onze extensiemethode maken en onze database-context registreren in de dependency injection-container.

Maak een extensiemethode

Voordat we de extensiemethode maken, laten we begrijpen wat een extensiemethode is in de context van ASP.NET Core.

Een extensiemethode is een statische methode die nieuwe functionaliteit toevoegt aan een bestaand type zonder het oorspronkelijke type te wijzigen. In ASP.NET Core worden extensiemethoden vaak gebruikt om de functionaliteit van de IServiceCollection-interface uit te breiden, die wordt gebruikt om services te registreren in de dependency injection-container.

Services zijn componenten die functionaliteit bieden aan een applicatie, zoals toegang tot databases, logging en configuratie. Door een extensiemethode te maken voor de IServiceCollection-interface, kunt u het proces van het registreren van uw services in de dependency injection-container vereenvoudigen.

In plaats van alles in het Program.cs bestand te plaatsen, zullen we een extensiemethode maken om onze services te registreren in de dependency injection-container. Dit zal ons helpen om onze code schoon en georganiseerd te houden.

In de Extensions map, maak een nieuw bestand genaamd ServiceExtensions.cs en voeg de volgende code toe:

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

            // Het toevoegen van de databasecontext
            builder.Services.AddDbContext<ApplicationContext>(configure =>
            {
                configure.UseSqlServer(builder.Configuration.GetConnectionString("sqlConnection"));
            });

            // Het toevoegen van validators van de huidige assembly
            builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
        }
    }
}

Laten we de bovenstaande code uitsplitsen:

  • We definiëren een statische klasse genaamd ServiceExtensions die een extensiemethode genaamd AddApplicationServices bevat. Deze methode breidt de IHostApplicationBuilder interface uit, die wordt gebruikt om de verwerkingspijplijn van het aanvraagverwerking van de applicatie te configureren.

  • De AddApplicationServices methode accepteert een instantie van IHostApplicationBuilder als parameter. Deze parameter wordt gebruikt om toegang te krijgen tot de configuratie en services van de applicatie.

  • We voegen de ApplicationContext toe aan de dependency injection-container en configureren deze om SQL Server als de databaseprovider te gebruiken. We halen de verbindingsreeks op uit het appsettings.json bestand met behulp van de GetConnectionString methode.

  • We voegen validators toe van de huidige assembly met behulp van de AddValidatorsFromAssembly methode. Deze methode scant de huidige assembly op klassen die de IValidator-interface implementeren en registreert ze in de dependency injection-container.

Vervolgens moeten we de verbindingsreeks toevoegen aan het appsettings.json bestand. Voeg de volgende code toe aan je appsettings.json bestand:

{ 
     "ConnectionStrings": {
    "sqlConnection": "Server=localhost\\SQLEXPRESS02;Database=BookAPIMinimalAPI;Integrated Security=true;TrustServerCertificate=true;"
  }
  }

Zorg ervoor dat je your_password vervangt door je daadwerkelijke SQL Server wachtwoord.

Je appsettings.json bestand zou er als volgt uit moeten zien:


{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "sqlConnection": "Server=localhost\\SQLEXPRESS02;Database=BookAPIMinimalAPI;Integrated Security=true;TrustServerCertificate=true;"
  },
  "AllowedHosts": "*"
}

Gefeliciteerd! Je hebt succesvol de databasecontext, extensiemethode en verbindingsreeks voor je applicatie aangemaakt. In de volgende sectie zullen we een Contract aanmaken.

Hoe maak je een contract

Contracten zijn Data Transfer Objects (DTO’s) die de structuur van de gegevens definiëren die worden uitgewisseld tussen de client en de server. In onze applicatie zullen we contracten maken om de gegevens te vertegenwoordigen die worden verzonden en ontvangen door onze API-eindpunten.

Hier zijn de contracten die we gaan maken:

  • CreateBookRequest: Dit vertegenwoordigt de gegevens die worden verzonden bij het maken van een nieuw boek.

  • UpdateBookRequest: Dit vertegenwoordigt de gegevens die worden verzonden bij het bijwerken van een bestaand boek.

  • BookResponse: Vertegenwoordigt de gegevens die worden geretourneerd bij het ophalen van een boek.

  • ErrorResponse: Vertegenwoordigt de foutreactie die wordt geretourneerd wanneer er een uitzondering optreedt.

  • ApiResponse: Vertegenwoordigt de reactie die wordt geretourneerd door de API.

In de map Contracts, maak een nieuw bestand met de naam CreateBookRequest en voeg de volgende code toe:

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

In de map Contracts, maak een nieuw bestand met de naam UpdateBookRequest en voeg de volgende code toe:


// Contracten/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; }

    }
}

In de Contracten map, maak een nieuw bestand met de naam BookResponse en voeg de volgende code toe:

// Contracten/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; }
    }
}

In de Contracten map, maak een nieuw bestand met de naam ErrorResponse en voeg de volgende code toe:



// Contracten/ErrorResponse.cs
namespace bookapi_minimal.Contracts
{

        public record ErrorResponse
    {
        public string Title { get; set; }
        public int StatusCode { get; set; }
        public string Message { get; set; }

    }

}

In de Contracten map, maak een nieuw bestand met de naam ApiResponse en voeg de volgende code toe:

// Contracten/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;
        }
    }
}

Deze contracten helpen ons bij het definiëren van de structuur van de gegevens die worden uitgewisseld tussen de client en de server, waardoor het gemakkelijker wordt om met de gegevens in onze applicatie te werken.

In het volgende gedeelte zullen we services maken om de bedrijfslogica van onze applicatie te implementeren.

Hoe services toevoegen

Services zijn componenten die functionaliteit bieden aan een applicatie. In onze applicatie zullen we services maken om de bedrijfslogica van onze applicatie te implementeren. We zullen services maken om CRUD-operaties voor boeken af te handelen, boekgegevens te valideren en uitzonderingen af te handelen.

In ASP.NET Core worden services geregistreerd in de dependency injection-container en kunnen ze worden geïnjecteerd in andere componenten, zoals controllers en eindpunten, Maar dit is een minimale API, dus we zullen de services rechtstreeks in de eindpunten injecteren.

Laten we een interface maken voor onze services. In de Interfaces map, maak een nieuw bestand genaamd IBookService.cs en voeg de volgende code toe:

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

Laten we de bovenstaande code uitsplitsen: We hebben een interface genaamd IBookService gedefinieerd die methoden bevat om CRUD-operaties voor boeken te verwerken. De interface definieert de volgende methoden:

  • AddBookAsync: Voegt een nieuw boek toe aan de database.

  • GetBookByIdAsync: Haalt een boek op aan de hand van zijn ID.

  • GetBooksAsync: Haalt alle boeken op uit de database.

  • UpdateBookAsync: Werkt een bestaand boek bij.

We gebruiken het Contract dat we eerder hebben gemaakt in de Contracts map. De IBookService interface definieert de structuur van de methoden die zullen worden geïmplementeerd door de serviceklassen. Dit helpt ons om de interface te scheiden van de implementatie, waardoor het gemakkelijker wordt om onze code te onderhouden en te testen.

Nu we de interface hebben gemaakt, laten we de serviceklasse maken die de interface implementeert.

Hoe de boekenservice te implementeren

Deze service zal de interface IBookService implementeren en de bedrijfslogica voor onze applicatie bieden. In de map Services, maak een nieuw bestand genaamd BookService.cs aan. Je oorspronkelijke bestand zou er als volgt uit moeten zien:


// Services/BookService.cs

namespace bookapi_minimal.Services
{
    public class BookService
    {

    }
}

Het eerste wat we moeten doen is de interface toevoegen aan de klasse BookService. Werk de klasse BookService bij om de interface IBookService te implementeren zoals hieronder:



// Services/BookService.cs



using bookapi_minimal.Interfaces;

namespace bookapi_minimal.Services
{
    public class BookService:IBookService
    {

    }
}

Wanneer je dit doet, kan je VS Code een foutmelding tonen omdat we de methoden in de interface nog niet hebben geïmplementeerd. Laten we doorgaan en de methoden in de klasse BookService implementeren.

In VS Code kan je de sneltoets Ctrl + . gebruiken om de methoden in de interface te implementeren. Dan zal je de volgende code gegenereerd zien worden:


using bookapi_minimal.Contracts;
using bookapi_minimal.Interfaces;

namespace bookapi_minimal.Services
{
     // Serviceklasse voor het beheren van boeken
   public class BookService : IBookService
   {
       // Methode om een nieuw boek aan de database toe te voegen
       public Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest)
       {
           throw new NotImplementedException();
       }

      // Methode om een boek uit de database te verwijderen
       public Task<bool> DeleteBookAsync(Guid id)
       {
           throw new NotImplementedException();
       }

       // Methode om een boek uit de database op te halen aan de hand van zijn ID

       public Task<BookResponse> GetBookByIdAsync(Guid id)
       {
           throw new NotImplementedException();
       }

      // Methode om alle boeken uit de database op te halen
       public Task<IEnumerable<BookResponse>> GetBooksAsync()
       {
           throw new NotImplementedException();
       }

       // Methode om een boek in de database bij te werken
       public Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest updateBookRequest)
       {
           throw new NotImplementedException();
       }
   }
}

Nu kunt u zien dat de methoden in de interface zijn geïmplementeerd in de klasse BookService. We zullen de bedrijfslogica implementeren voor elke methode in de volgende sectie.

Voordat we dat doen, laten we de benodigde afhankelijkheden toevoegen aan de klasse BookService. We moeten de afhankelijkheden ApplicationContext en ILogger injecteren in de klasse BookService. ApplicationContext wordt gebruikt om te communiceren met de database, terwijl ILogger wordt gebruikt voor logging.

Om de afhankelijkheden te injecteren, werk de klasse BookService als volgt bij:


// Services/BookService.cs

// ...
 private readonly ApplicationContext _context; // Database context
  private readonly ILogger<BookService> _logger; // Logger voor het vastleggen van informatie en fouten

//..

Aangezien we de afhankelijkheden hebben toegevoegd, moeten we de constructor van BookService bijwerken om de afhankelijkheden te accepteren. Werk de constructor van BookService als volgt bij:


// Services/BookService.cs

// ...

  // Constructor om de databasecontext en logger te initialiseren
 public BookService(ApplicationContext context, ILogger<BookService> logger)
 {
            _context = context;
            _logger = logger;
}

// ...

Nu we de afhankelijkheden hebben toegevoegd en de constructor hebben bijgewerkt, kunnen we de bedrijfslogica implementeren voor elke methode in de klasse BookService.

Laten we logica creëren voor de CREATE, READ, UPDATE en DELETE operaties in de BookService klasse.

Hoe de AddBookAsync Methode Implementeren

Zoals eerder vermeld, zullen we de AddBookAsync methode gebruiken om een nieuw boek aan de database toe te voegen. In deze methode zullen we een nieuwe boek entiteit maken, de gegevens van het CreateBookRequest object naar de boek entiteit mappen en de boek entiteit opslaan in de database. We zullen ook de boek entiteit retourneren als een BookResponse object.

Update de AddBookAsync methode in de BookService klasse als volgt:

// Services/BookService.cs

// ...
 /// <summary>
        /// Voeg een nieuw boek toe
        /// </summary>
        /// <param name="createBookRequest">Boekverzoek dat moet worden toegevoegd</param>
        /// <returns>Details van het aangemaakte boek</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
                };

                // Voeg het boek toe aan de database
                _context.Books.Add(book);
                await _context.SaveChangesAsync();
                _logger.LogInformation("Book added successfully.");

                // Retourneer de details van het aangemaakte boek
                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 deze code maken we een nieuwe boek entiteit aan vanuit het CreateBookRequest object, mappen we de data van het CreateBookRequest object naar de boek entiteit, slaan we de boek entiteit op in de database, en retourneren we de boek entiteit als een BookResponse object.

We loggen ook informatie en fouten met behulp van de ILogger afhankelijkheid. Als er een uitzondering optreedt tijdens het proces, loggen we het foutbericht en gooien we de uitzondering opnieuw.

Nu we de AddBookAsync methode geïmplementeerd hebben, laten we de GetBookByIdAsync methode implementeren.

Hoe de GetBookByIdAsync Methode te Implementeren

De GetBookByIdAsync methode wordt gebruikt om een boek op te halen aan de hand van zijn ID uit de database. In deze methode zullen we de database bevragen voor het boek met het gespecificeerde ID, de boek entiteit mappen naar een BookResponse object, en het BookResponse object retourneren.

Update de GetBookByIdAsync methode in de BookService klasse als volgt:


// Services/BookService.cs

//... 

    /// <summary>
        /// Haal een boek op aan de hand van zijn ID
        /// </summary>
        /// <param name="id">ID van het boek</param>
        /// <returns>Details van het boek</returns>
        public async Task<BookResponse>  GetBookByIdAsync(Guid id)
        {
            try
            {
                // Vind het boek aan de hand van zijn ID
                var book = await _context.Books.FindAsync(id);
                if (book == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return null;
                }

                // Geef de details van het boek terug
                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 deze code bevragen we de database naar het boek met het opgegeven ID, mappen de boekentiteit naar een BookResponse object, en retourneren het BookResponse object. We loggen ook informatie en fouten met behulp van de ILogger afhankelijkheid.

Als het boek met het opgegeven ID niet wordt gevonden, loggen we een waarschuwingsbericht en retourneren null. Als er een uitzondering optreedt tijdens het proces, loggen we het foutenbericht en gooien we de uitzondering opnieuw.

Nu we de GetBookByIdAsync methode hebben geïmplementeerd, laten we de GetBooksAsync methode implementeren.

Hoe de GetBooksAsync Methode te Implementeren

De methode GetBooksAsync wordt gebruikt om alle boeken uit de database op te halen. In deze methode zullen we de database bevragen voor alle boeken, elke boekentiteit toewijzen aan een BookResponse-object, en een lijst van BookResponse-objecten retourneren.

Update de methode GetBooksAsync in de klasse BookService als volgt:



// Services/BookService.cs

//... 


  /// <summary>
        /// Alle boeken ophalen
        /// </summary>
        /// <returns>Lijst van alle boeken</returns>
        public async Task<IEnumerable<BookResponse>> GetBooksAsync()
        {
            try
            {
                // Alle boeken uit de database ophalen
                var books = await _context.Books.ToListAsync();

                // De details van alle boeken retourneren
                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;
            }
        }
//...

Hier bevragen we de database voor alle boeken, elke boekentiteit toewijzen aan een BookResponse-object, en een lijst van BookResponse-objecten retourneren. We loggen ook informatie en fouten met behulp van de ILogger-afhankelijkheid. Als er een uitzondering optreedt tijdens het proces, loggen we het foutbericht en gooien we de uitzondering opnieuw.

Nu we de methode GetBooksAsync hebben geïmplementeerd, laten we de methode UpdateBookAsync implementeren.

Hoe de methode UpdateBookAsync te implementeren

De methode UpdateBookAsync wordt gebruikt om een bestaand boek in de database bij te werken. In deze methode zullen we de database bevragen voor het boek met de gespecificeerde ID, het boekentiteit bijwerken met de gegevens uit het UpdateBookRequest-object, de bijgewerkte boekentiteit opslaan in de database, en de bijgewerkte boekentiteit teruggeven als een BookResponse-object.

Update de UpdateBookAsync-methode in de BookService-klasse als volgt:

// Services/BookService.cs
 //...
 /// <summary>
        /// Werk een bestaand boek bij
        /// </summary>
        /// <param name="id">ID van het te bijwerken boek</param>
        /// <param name="book">Bijgewerkt boekmodel</param>
        /// <returns>Details van het bijgewerkte boek</returns>
        public async Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest book)
        {
            try
            {
                // Vind het bestaande boek op basis van de ID
                var existingBook = await _context.Books.FindAsync(id);
                if (existingBook == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return null;
                }

                // Werk de boekgegevens bij
                existingBook.Title = book.Title;
                existingBook.Author = book.Author;
                existingBook.Description = book.Description;
                existingBook.Category = book.Category;
                existingBook.Language = book.Language;
                existingBook.TotalPages = book.TotalPages;

                // Sla de wijzigingen op in de database
                await _context.SaveChangesAsync();
                _logger.LogInformation("Book updated successfully.");

                // Geef de details van het bijgewerkte boek terug
                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 worden we de database bevragen voor het boek met het gespecificeerde ID, wordt de boekentiteit bijgewerkt met de gegevens uit het UpdateBookRequest-object, de bijgewerkte boekentiteit wordt opgeslagen in de database en geretourneerd als een BookResponse-object. We loggen ook informatie en fouten met behulp van de ILogger-afhankelijkheid.

Als het boek met het gespecificeerde ID niet wordt gevonden, loggen we een waarschuwingsbericht en retourneren null. Als er een uitzondering optreedt tijdens het proces, loggen we het foutbericht en gooi de uitzondering opnieuw.

Nu we de methode UpdateBookAsync hebben geïmplementeerd, laten we de methode DeleteBookAsync implementeren.

Hoe de methode DeleteBookAsync te implementeren

De methode DeleteBookAsync wordt gebruikt om een bestaand boek uit de database te verwijderen. In deze methode bevragen we de database voor het boek met het gespecificeerde ID, verwijderen de boekentiteit uit de database en retourneren een boolean-waarde die aangeeft of het boek succesvol is verwijderd.

Update de methode DeleteBookAsync in de klasse BookService als volgt:

// Services/BookService.cs

 //...


/// <summary>
        /// Verwijder een boek op basis van het ID
        /// </summary>
        /// <param name="id">ID van het te verwijderen boek</param>
        /// <returns>True als het boek is verwijderd, anders false</returns>
        public async Task<bool> DeleteBookAsync(Guid id)
        {
            try
            {
                // Vind het boek op basis van het ID
                var book = await _context.Books.FindAsync(id);
                if (book == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return false;
                }

                // Verwijder het boek uit de database
                _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 deze code bevragen we de database voor het boek met het opgegeven ID, verwijderen we de boekentiteit uit de database, en geven we een booleaanse waarde terug die aangeeft of het boek succesvol is verwijderd. We loggen ook informatie en fouten met behulp van de ILogger afhankelijkheid.

Als het boek met het opgegeven ID niet wordt gevonden, loggen we een waarschuwingsbericht en geven we false terug. Als er een uitzondering optreedt tijdens het proces, loggen we het foutbericht en gooien we de uitzondering opnieuw.

Nu heb je succesvol de bedrijfslogica geïmplementeerd voor de AddBookAsync, GetBookByIdAsync, GetBooksAsync, UpdateBookAsync en DeleteBookAsync methoden in de BookService klasse. Deze methoden behandelen de CRUD-operaties voor boeken, valideren boekgegevens en behandelen uitzonderingen. Op dit moment zou je BookService klasse er ongeveer zo uit moeten zien:



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; // Databasecontext
        private readonly ILogger<BookService> _logger; // Logger voor het loggen van informatie en fouten
          // Constructor om de databasecontext en logger te initialiseren
        public BookService(ApplicationContext context, ILogger<BookService> logger)
        {
            _context = context;
            _logger = logger;
        }

           /// Voeg een nieuw boek toe
        /// </samenvatting>
        /// <param name="createBookRequest">Boekverzoek dat moet worden toegevoegd</param>
        /// <returns>Details van het aangemaakte boek</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
                };

                // Voeg het boek toe aan de database
                _context.Books.Add(book);
                await _context.SaveChangesAsync();
                _logger.LogInformation("Book added successfully.");

                // Geef de details van het aangemaakte boek terug
                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;
            }
        }

          /// <samenvatting>
        /// Haal een boek op aan de hand van zijn ID
        /// </samenvatting>
        /// <param name="id">ID van het boek</param>
        /// <returns>Details van het boek</returns>
        public async Task<BookResponse>  GetBookByIdAsync(Guid id)
        {
            try
            {
                // Vind het boek op basis van zijn ID
                var book = await _context.Books.FindAsync(id);
                if (book == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return null;
                }

                // Geef de details van het boek terug
                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;
            }
        }



  /// <samenvatting>
        /// Haal alle boeken op
        /// </samenvatting>
        /// <returns>Lijst van alle boeken</returns>
        public async Task<IEnumerable<BookResponse>> GetBooksAsync()
        {
            try
            {
                // Haal alle boeken op uit de database
                var books = await _context.Books.ToListAsync();

                // Geef de details van alle boeken terug
                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;
            }
        }


         /// <samenvatting>
        /// Werk een bestaand boek bij
        /// </samenvatting>
        /// <param name="id">ID van het te updaten boek</param>
        /// <param name="book">Bijgewerkt boekmodel</param>
        /// <returns>Details van het bijgewerkte boek</returns>
        public async Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest book)
        {
            try
            {
                // Vind het bestaande boek op basis van zijn ID
                var existingBook = await _context.Books.FindAsync(id);
                if (existingBook == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return null;
                }

                // Werk de boekdetails bij
                existingBook.Title = book.Title;
                existingBook.Author = book.Author;
                existingBook.Description = book.Description;
                existingBook.Category = book.Category;
                existingBook.Language = book.Language;
                existingBook.TotalPages = book.TotalPages;

                // Sla de wijzigingen op in de database
                await _context.SaveChangesAsync();
                _logger.LogInformation("Book updated successfully.");

                // Geef de details van het bijgewerkte boek terug
                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;
            }
        }



        /// <samenvatting>
        /// Verwijder een boek aan de hand van zijn ID
        /// </samenvatting>
        /// <param name="id">ID van het te verwijderen boek</param>
        /// <returns>True als het boek is verwijderd, anders false</returns>
        public async Task<bool> DeleteBookAsync(Guid id)
        {
            try
            {
                // Vind het boek op basis van zijn ID
                var book = await _context.Books.FindAsync(id);
                if (book == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return false;
                }

                // Verwijder het boek uit de database
                _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;
            }
        }

    }
}

Gefeliciteerd! U heeft succesvol de bedrijfslogica geïmplementeerd voor de methoden AddBookAsync, GetBookByIdAsync, GetBooksAsync, UpdateBookAsync en DeleteBookAsync in de klasse BookService.

Er is nog één ding dat we moeten doen: we moeten de service registreren in onze extensiemethode. Laten we dat nu doen.

Voeg in uw bestand ServiceExtensions.cs de volgende code toe:


// Extensions/ServiceExtensions.cs

//..

 builder.Services.AddScoped<IBookService, BookService>();
//...

Dit zal de klasse BookService registreren als een scoped service. Dit betekent dat de service één keer per verzoek wordt aangemaakt en na het voltooien van het verzoek wordt verwijderd.

Nu de service werkt, laten we doorgaan en de uitzonderingsklassen maken.

Hoe uitzonderingen te maken

Het correct omgaan met uitzonderingen is cruciaal om de stabiliteit en betrouwbaarheid van een applicatie te waarborgen. In de context van ASP.NET Core zijn er twee hoofdtypen uitzonderingen:

  • Systeemuitzonderingen: Dit zijn uitzonderingen die worden gegenereerd door de .NET-runtime of het onderliggende systeem.

  • Toepassingsuitzonderingen: Dit zijn uitzonderingen die worden gegenereerd door de toepassingscode om specifieke fouten of omstandigheden af te handelen.

In ASP.NET Core met .NET 8 is een nieuwe functie geïntroduceerd genaamd globale uitzonderingsafhandeling. Deze functie stelt u in staat om uitzonderingen op globaal niveau in uw toepassing te beheren, waardoor het gemakkelijker wordt om fouten te beheren en een consistente gebruikerservaring te bieden.

In onze toepassing zullen we aangepaste uitzonderingsklassen maken om specifieke fouten en situaties te behandelen. We zullen ook gebruikmaken van de functie voor globale uitzonderingsafhandeling om uitzonderingen op globaal niveau te beheren, ervoor zorgend dat er een uniforme benadering van foutafhandeling wordt toegepast in de gehele toepassing.

We gaan de volgende uitzonderingsklassen maken:

  • NoBookFoundException: Gegenereerd wanneer een boek met de gespecificeerde ID niet wordt gevonden.

  • BookDoesNotExistException: Gegenereerd wanneer een boek met de gespecificeerde ID niet bestaat.

  • GlobalExceptionHandler: Behandelt uitzonderingen op globaal niveau in de toepassing.

In de map Uitzonderingen maak een nieuw bestand met de naam NoBookFoundException.cs en voeg de volgende code toe:


// Uitzonderingen/NoBookFoundException.cs

namespace bookapi_minimal.Exceptions
{

    public class NoBookFoundException : Exception
    {

        public NoBookFoundException() : base("No books found")
        {}
    }
}

In deze code creëren we een aangepaste uitzonderingsklasse met de naam NoBookFoundException die overerft van de klasse Exception. De klasse NoBookFoundException wordt gebruikt om het scenario te behandelen waarin er geen boeken worden gevonden in de database. We voorzien ook een aangepast foutbericht voor de uitzondering.

In de map Exceptions maak een nieuw bestand met de naam BookDoesNotExistException.cs en voeg de volgende code toe:

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 deze code creëren we een aangepaste uitzonderingsklasse met de naam BookDoesNotExistException die overerft van de klasse Exception. De klasse BookDoesNotExistException wordt gebruikt om het scenario te behandelen waarin een boek met de gespecificeerde ID niet bestaat in de database. We voorzien ook een aangepast foutbericht voor de uitzondering.

In de map Exceptions maak een nieuw bestand met de naam GlobalExceptionHandler.cs en voeg de volgende code toe:

// Uitzonderingen/GlobalExceptionHandler.cs

using System.Net;
using bookapi_minimal.Contracts;
using Microsoft.AspNetCore.Diagnostics;

namespace bookapi_minimal.Exceptions
{

   // Globale uitzondering handler klasse die IExceptionHandler implementeert
    public class GlobalExceptionHandler : IExceptionHandler
    {
        private readonly ILogger<GlobalExceptionHandler> _logger;

        // Constructor om de logger te initialiseren
        public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
        {
            _logger = logger;
        }

        // Methode om uitzonderingen asynchroon af te handelen
        public async ValueTask<bool> TryHandleAsync(
            HttpContext httpContext,
            Exception exception,
            CancellationToken cancellationToken)
        {
            // Log de details van de uitzondering
            _logger.LogError(exception, "An error occurred while processing your request");

            var errorResponse = new ErrorResponse
            {
                Message = exception.Message,
                Title = exception.GetType().Name
            };

            // Bepaal de statuscode op basis van het type uitzondering
            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;
            }

            // Stel de statuscode van de reactie in
            httpContext.Response.StatusCode = errorResponse.StatusCode;

            // Schrijf de foutreactie als JSON
            await httpContext.Response.WriteAsJsonAsync(errorResponse, cancellationToken);

            // Geef true terug om aan te geven dat de uitzondering is afgehandeld
            return true;
        }
    }
}

Laten we de bovenstaande code ontleden:

  • We definiëren een klasse genaamd GlobalExceptionHandler die de IExceptionHandler interface implementeert. De IExceptionHandler interface wordt gebruikt om uitzonderingen globaal in de applicatie af te handelen.

  • De GlobalExceptionHandler klasse bevat een constructor die de ILogger<GlobalExceptionHandler> afhankelijkheid initialiseerd. De ILogger wordt gebruikt voor het loggen van informatie en fouten.

  • De methode TryHandleAsync wordt gebruikt om uitzonderingen asynchroon af te handelen. Deze methode accepteert de HttpContext, Exception en CancellationToken als parameters.

  • We loggen de uitzonderingsdetails met behulp van de ILogger afhankelijkheid.

  • We maken een ErrorResponse object aan om de foutmelding te vertegenwoordigen die door de API wordt geretourneerd. Het ErrorResponse object bevat de foutmelding, titel en statuscode.

  • We bepalen de statuscode op basis van het type uitzondering. Als de uitzondering een BadHttpRequestException is, stellen we de statuscode in op BadRequest. Als de uitzondering een NoBookFoundException of BookDoesNotExistException is, stellen we de statuscode in op NotFound. Anders stellen we de statuscode in op InternalServerError.

  • We stellen de statuscode van de respons in met behulp van de eigenschap httpContext.Response.StatusCode.

  • We schrijven de foutrespons als JSON met behulp van de methode httpContext.Response.WriteAsJsonAsync.

  • We geven true terug om aan te geven dat de uitzondering succesvol is afgehandeld.

Nu we de uitzonderingsklassen hebben aangemaakt, laten we de GlobalExceptionHandler registreren in de dependency injection-container. Aangezien we een Extensiemethode hebben gemaakt voor het registreren van services in de dependency injection-container, voegen we de GlobalExceptionHandler toe aan de klasse ServiceExtensions.

Update de klasse ServiceExtensions in de map Extensions als volgt:


// Extensies/ServiceExtensies.cs
//...
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();

builder.Services.AddProblemDetails();

//...

De methode AddExceptionHandler registreert de GlobalExceptionHandler in de dependency injection-container. De methode AddProblemDetails registreert de klasse ProblemDetails in de dependency injection-container.

Nu we de GlobalExceptionHandler hebben geregistreerd in de dependency injection-container, kunnen we deze gebruiken om uitzonderingen globaal af te handelen in onze applicatie. In de volgende sectie zullen we de API-eindpunten maken om te communiceren met de boekgegevens.

Hoe u de API-eindpunten kunt maken

In de context van minimale API’s in ASP.NET Core zijn er veel manieren om uw eindpunten in te stellen.

U kunt ze rechtstreeks definiëren in uw bestand Program.cs. Maar naarmate uw project groeit en u meer eindpunten of functionaliteit moet toevoegen, is het handig om uw code beter te organiseren. Een manier om dit te bereiken is door een aparte klasse te maken om alle eindpunten af te handelen.

Zoals hierboven besproken, gebruiken minimale API’s geen controllers of views zoals traditionele ASP.NET Core-toepassingen. In plaats daarvan gebruiken ze methoden zoals MapGet, MapPost, MapPut en MapDelete om HTTP-methoden en routes voor API-eindpunten te definiëren.

Om te beginnen, navigeer naar de map Eindpunten en maak een nieuw bestand met de naam BookEindpunten.cs. Voeg de volgende code toe aan het bestand:


// Eindpunten/BookEndpoints.cs



namespace bookapi_minimal.Endpoints
{
     public static class BookEndPoint
    {
        public static IEndpointRouteBuilder MapBookEndPoint(this IEndpointRouteBuilder app)
        {


            return app;
        }
    }
}

De klasse BookEndpoints bevat een methode MapBookEndPoint die een object van het type IEndpointRouteBuilder retourneert. Het IEndpointRouteBuilder-object wordt gebruikt om de HTTP-methoden en routes voor de API-eindpunten te definiëren. In de volgende secties zullen we de API-eindpunten definiëren voor het creëren, lezen, bijwerken en verwijderen van boeken.

Hoe maak je het eindpunt AddBookAsync Books

In deze sectie zullen we het eindpunt AddBookAsync creëren. Dit eindpunt zal een object van het type Book accepteren als JSON-payload en het toevoegen aan de database. We zullen de methode MapPost gebruiken om de HTTP-methode en route voor dit eindpunt te definiëren.

Voeg de volgende code toe aan de klasse BookEndpoints:


// Eindpunten/BookEndpoints.cs


//...
   // Eindpunt om een nieuw boek toe te voegen
      app.MapPost("/books", async (CreateBookRequest createBookRequest, IBookService bookService) =>
        {
        var result = await bookService.AddBookAsync(createBookRequest);
        return Results.Created($"/books/{result.Id}", result); 
        });


//...
  • Routedefinitie: De MapPost-methode definieert de route voor het eindpunt als /boeken.

  • Aanvraagmodel: Het eindpunt accepteert een object van het type CreateBookRequest als JSON-payload. Het object CreateBookRequest bevat de gegevens die nodig zijn om een nieuw boek te creëren.

  • Reactiemodel: Het eindpunt retourneert een Book-object als JSON-payload. Het Book-object bevat de gegevens voor het nieuw aangemaakte boek.

  • Teruggegeven waarde: Het eindpunt retourneert een Created-resultaat. Het Created-resultaat bevat de locatie van het nieuw aangemaakte boek en het Book-object.

Hoe de GetBookAsync Boek Eindpunt te Creëren

In dit gedeelte zullen we het GetBookAsync-eindpunt creëren. Dit eindpunt zal een boek-ID accepteren als een queryparameter en het boek met de gespecificeerde ID retourneren. We zullen de MapGet-methode gebruiken om de HTTP-methode en route voor dit eindpunt te definiëren.

Voeg de volgende code toe aan de klasse BookEndpoints:


// Eindpunten/BookEindpunten.cs

// ...
    // Eindpunt om alle boeken op te halen
    app.MapGet("/books", async (IBookService bookService) =>
     {
    var result = await bookService.GetBooksAsync();
    return Results.Ok(result);
});


//...
  • Route Definitie: De MapGet methode definieert de route voor het eindpunt als /boeken.

  • Verzoek Model: Het eindpunt accepteert een Boek object als een JSON-payload. Het Boek object bevat de gegevens die nodig zijn om een nieuw boek te maken.

  • Antwoord Model: Het eindpunt geeft een Boek object terug als een JSON-payload. Het Boek object bevat de gegevens voor het nieuw gemaakte boek.

  • Teruggegeven Waarde: Het eindpunt geeft een Ok resultaat terug. Het Ok resultaat bevat het Boek object.

Hoe de GetBoekByIdAsync Boek Eindpunt te maken

In deze sectie zullen we het GetBookByIdAsync eindpunt creëren. Dit eindpunt zal een boek-ID accepteren als een route parameter en het boek met het gespecificeerde ID retourneren. We zullen de MapGet methode gebruiken om de HTTP-methode en route voor dit eindpunt te definiëren.

Voeg de volgende code toe aan de BookEndpoints klasse:


// Eindpunten/BookEndpoints.cs
//...
// Eindpunt om een boek op te halen op basis van 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();
});

//...
  • Routedefinitie: De MapGet methode definieert de route voor het eindpunt als /boeken/{id:guid}. De {id:guid} parameter specificeert dat de id parameter een GUID moet zijn.

  • Aanvraagmodel: Het eindpunt accepteert een Boek object als een JSON-payload. Het Boek object bevat de gegevens die nodig zijn om een nieuw boek te maken.

  • Reactiemodel: Het eindpunt retourneert een Boek object als een JSON-payload. Het Boek object bevat de gegevens voor het nieuw gecreëerde boek.

  • Retourwaarde: Het eindpunt retourneert een Ok-resultaat als het boek wordt gevonden. Het resultaat Niet gevonden wordt geretourneerd als het boek niet wordt gevonden.

Hoe u het eindpunt UpdateBookAsync Boek kunt aanmaken

In dit gedeelte zullen we het eindpunt UpdateBookAsync aanmaken. Dit eindpunt zal een boek-ID accepteren als een routeparameter en een Boek-object als JSON-payload en het boek met de gespecificeerde ID bijwerken. We zullen de methode MapPut gebruiken om de HTTP-methode en route voor dit eindpunt te definiëren.

Voeg de volgende code toe aan de klasse BookEndpoints:


// Eindpunten/BookEndpoints.cs

//...
   // Eindpunt om een boek bij te werken op basis van 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();
});

//...
  • Routedefinitie: De MapPut-methode definieert de route voor het eindpunt als /boeken/{id:guid}. De parameter {id:guid} specificeert dat de parameter id een GUID moet zijn.

  • Aanvraagmodel: Het eindpunt accepteert een Book-object als JSON-payload. Het Book-object bevat de gegevens die nodig zijn om een nieuw boek te maken.

  • Reactiemodel: Het eindpunt retourneert een Book-object als JSON-payload. Het Book-object bevat de gegevens voor het nieuw aangemaakte boek.

  • Teruggegeven Waarde: Het eindpunt retourneert een Ok-resultaat als het boek gevonden is. Het resultaat NotFound wordt geretourneerd als het boek niet gevonden is.

Hoe de DeleteBookAsync-boek Eindpunt te Creëren

In deze sectie zullen we het DeleteBookAsync eindpunt creëren. Dit eindpunt zal een boek-ID accepteren als een route parameter en het boek met het gespecificeerde ID verwijderen. We zullen de MapDelete methode gebruiken om de HTTP-methode en route voor dit eindpunt te definiëren.

Voeg de volgende code toe aan de BookEndpoints klasse:


// Eindpunten/BookEndpoints.cs

//...
   // Eindpunt om een boek te verwijderen op basis van ID
 app.MapDelete("/books/{id:guid}", async (Guid id, IBookService bookService) =>
{
var result = await bookService.DeleteBookAsync(id);
   return result ? Results.NoContent() : Results.NotFound();
});


//...
  • Routedefinitie: De MapDelete methode definieert de route voor het eindpunt als /boeken/{id:guid}. De {id:guid} parameter specificeert dat de id parameter een GUID moet zijn.

  • Verzoekmodel: Het eindpunt accepteert een Boek object als een JSON-payload. Het Boek object bevat de gegevens die nodig zijn om een nieuw boek te creëren.

  • Reactiemodel: Het eindpunt retourneert een Boek object als een JSON-payload. Het Boek object bevat de gegevens voor het nieuw gecreëerde boek.
  • Retourwaarde: Het eindpunt retourneert een GeenInhoud-resultaat als het boek succesvol is verwijderd. Het NietGevonden-resultaat wordt geretourneerd als het boek niet gevonden is.

Nu hebben we alle methoden voor de boekeindpunten gedefinieerd. Dus uw eindpuntenklasse zou er als volgt uit moeten zien:

// Eindpunten/BoekEindpunten.cs
using bookapi_minimal.Contracts;
using bookapi_minimal.Interfaces;

namespace bookapi_minimal.Endpoints
{
     public static class BookEndPoint
    {
        public static IEndpointRouteBuilder MapBookEndPoint(this IEndpointRouteBuilder app)
        {
            // Definieer de eindpunten

            // Eindpunt om een nieuw boek toe te voegen
            app.MapPost("/books", async (CreateBookRequest createBookRequest, IBookService bookService) =>
            {
                var result = await bookService.AddBookAsync(createBookRequest);
                return Results.Created($"/books/{result.Id}", result); 
            });


               // Eindpunt om alle boeken op te halen
            app.MapGet("/books", async (IBookService bookService) =>
            {
                var result = await bookService.GetBooksAsync();
                return Results.Ok(result);
            });

            // Eindpunt om een boek op te halen op basis van 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();
            });


            // Eindpunt om een boek bij te werken op basis van 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();
            });

            // Eindpunt om een boek op basis van ID te verwijderen
            app.MapDelete("/books/{id:guid}", async (Guid id, IBookService bookService) =>
            {
                var result = await bookService.DeleteBookAsync(id);
                return result ? Results.NoContent() : Results.NotFound();
            });

            return app;
        }
    }
}

Gefeliciteerd! U heeft alle eindpunten voor de boek-API aangemaakt. De eindpunten behandelen de CRUD-bewerkingen voor boeken en retourneren de passende reacties op basis van het verzoek en de gegevens.

Hoe de eindpunten te registreren

Na het definiëren van de API-eindpunten voor de boek-API, is de volgende stap om deze eindpunten te registreren in het Program.cs-bestand. We zullen de MapBookEndpoints-methode gebruiken om de boekeindpunten te registreren.

We moeten ook onze Program.cs-klasse opruimen om ervoor te zorgen dat deze georganiseerd en onderhoudbaar blijft.

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


    // Stel het pad voor de Swagger JSON en UI in.
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);

});
var app = builder.Build();

// Configureer de HTTP-verzoek-pijplijn.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseExceptionHandler();


app.MapGroup("/api/v1/")
   .WithTags(" Book endpoints")
   .MapBookEndPoint();

app.Run();

Laten we de belangrijkste onderdelen van het Program.cs-bestand uiteenzetten:

  • AddApplicationServices: Deze methode registreert de benodigde services voor de API. Het is een extensiemethode die we eerder hebben gemaakt om services toe te voegen aan de dependency injection-container.

  • AddSwaggerGen: Deze methode registreert de Swagger-generator, die wordt gebruikt om de Swagger-documentatie voor de API te maken. We specificeren de titel, versie en beschrijving van de API in het Swagger-document.

  • MapGroup: Deze methode groepeert de eindpunten. Het neemt een pad als parameter en retourneert een IEndpointRouteBuilder-object. We gebruiken de methode WithTags om tags toe te voegen aan de eindpunten en de methode MapBookEndpoints om de boekeindpunten te registreren.

  • Run: Deze methode start de toepassing.

Om Swagger-documentatie in te schakelen, moet je de eigenschap GenerateDocumentationFile toevoegen aan je .csproj-bestand. In dit voorbeeld is het bestand genaamd bookapi-minimal.csproj, maar de naam kan variëren afhankelijk van je project.

Voeg de volgende regel toe aan je .csproj-bestand:

<PropertyGroup>
  <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

Aan het einde zou bookapi-minimal.csproj er als volgt uit moeten zien:


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

Nu we de boekeindpunten hebben geregistreerd in het bestand Program.cs, kunnen we de toepassing uitvoeren en de API-eindpunten testen met Swagger.

Wanneer u de toepassing uitvoert, zou u de Swagger-documentatie moeten zien op de volgende URL: https://localhost:5001/swagger/index.html. De Swagger-documentatie biedt informatie over de API-eindpunten, verzoek- en antwoordmodellen, en stelt u in staat om de eindpunten rechtstreeks vanuit de browser te testen. U zou iets dergelijks moeten zien:

Gefeliciteerd! U heeft de bedrijfslogica geïmplementeerd voor de boekenservice, aangepaste uitzonderingen gemaakt, API-eindpunten gedefinieerd, en de eindpunten geregistreerd in het Program.cs bestand. U heeft ook Swagger-documentatie ingeschakeld om de API-eindpunten te testen.

Hoe u zaaidata aan de database toevoegt

Nog een belangrijke stap is om bij het starten van de toepassing zaaidata aan de database toe te voegen. Deze zaaidata zal de database vullen, zodat u uw API-eindpunten kunt testen zonder handmatig gegevens toe te voegen.

Laten we wat zaaidata toevoegen voordat we migraties uitvoeren en onze API-eindpunten testen.

Om dit te bereiken, zullen we een nieuwe klasse maken in onze Configuratie map genaamd BookTypeConfigurations en de volgende code toevoegen:



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)
        {
            // Configureer de tabelnaam
            builder.ToTable("Books");

            // Configureer de primaire sleutel
            builder.HasKey(x => x.Id);

            // Configureer eigenschappen
            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();

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

Laten we de bovenstaande code uitsplitsen:

In Entity Framework Core kun je de interface IEntityTypeConfiguration gebruiken om het entiteitstype en seed-gegevens voor de database te configureren. De klasse BookTypeConfigurations implementeert de interface IEntityTypeConfiguration<BookModel> en biedt de configuratie voor de entiteit BookModel.

  • Configure Methode: Deze methode wordt gebruikt om het entiteitstype BookModel te configureren. Het definieert de tabelnaam, primaire sleutel en eigenschappen voor de entiteit BookModel.

    • Tabelnaam: De methode ToTable specificeert de naam van de tabel die in de database moet worden aangemaakt. In dit geval is de tabelnaam ingesteld op “Books”.

    • Primaire Sleutel: De methode HasKey specificeert de primaire sleutel voor de entiteit BookModel. De primaire sleutel is ingesteld op de eigenschap Id.

    • Eigenschappen: De methode Property configureert de eigenschappen van de entiteit BookModel. Het specificeert het gegevenstype, lengte en beperkingen voor elke eigenschap.

  • Seedgegevens: De HasData methode voegt initiële gegevens toe aan de database. Het maakt drie BookModel objecten aan met voorbeeldgegevens voor het testen van de API-eindpunten.

Nu we de klasse BookTypeConfigurations hebben aangemaakt, moeten we deze configuratie registreren in de klasse ApplicationContext. Dit zorgt ervoor dat de configuratie wordt toegepast wanneer de database wordt aangemaakt of gemigreerd.

We zijn eindelijk bijna klaar om onze API te testen. Maar voordat we dat doen, moeten we migraties uitvoeren om de database te maken en de seed-gegevens toe te passen.

Onthoud dat we onze databaseverbindingssnaren in het appsettings.json bestand hebben toegevoegd? Laten we nu een migratie uitvoeren en later onze database bijwerken zodat de migratie van kracht wordt.

Hoe voer je een migratie uit

Migraties stellen je in staat om het databaseschema bij te werken op basis van wijzigingen in je modelklassen. In Entity Framework Core kun je de dotnet ef migrations add opdracht gebruiken om een nieuwe migratie te maken die deze wijzigingen weerspiegelt.

Voer de volgende opdracht uit in de terminal om een migratie uit te voeren:

dotnet ef migrations add InitialCreate

Als de opdracht succesvol is, zou je een uitvoer moeten zien die vergelijkbaar is met deze:

Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'

Je zult nu een nieuwe map genaamd Migraties in je project zien. Deze map bevat de migratiebestanden die zijn gemaakt op basis van de wijzigingen in je modelklassen. Deze migratiebestanden bevatten de SQL-opdrachten die nodig zijn om het databaseschema bij te werken.

Hoe de Database bijwerken

Na het maken van de migratie, moet je de migratie toepassen om het databaseschema bij te werken. Je kunt de dotnet ef database update opdracht gebruiken om de migratie toe te passen en de database bij te werken. Zorg ervoor dat de SQL Server actief is.

Voer de volgende opdracht uit in de terminal:


dotnet ef database update

Dit zal het databaseschema bijwerken op basis van de wijzigingen in je modelklassen. Zorg ervoor dat er geen fouten zijn in je databaseverbindingssnaar.

Hoe de API-eindpunten testen

Nu kunnen we onze endpoints testen met behulp van Swagger. Voer de toepassing uit door het volgende commando in de terminal uit te voeren:


dotnet run

Dit zal onze toepassing uitvoeren. U kunt uw browser openen en naar https://localhost:5001/swagger/index.html navigeren om toegang te krijgen tot de Swagger-documentatie. U zou een lijst met API-eindpunten, verzoek- en reactiemodellen moeten zien, en de mogelijkheid om de eindpunten rechtstreeks vanuit de browser te testen.

Als uw poortnummer verschilt van 5001, maakt u zich geen zorgen – het zal nog steeds werken. Het poortnummer kan veranderen afhankelijk van het type machine dat u gebruikt, maar het zal nog steeds hetzelfde resultaat opleveren.

Hoe u het eindpunt Alle boeken ophalen kunt testen

Volg deze stappen om het eindpunt Alle boeken ophalen te testen:

  1. Klik in de Swagger-documentatie op het eindpunt GET /api/v1/books.

  2. Klik op de knop Uitproberen.

  3. Klik op de knop Uitvoeren.

Dit zal een verzoek naar de API sturen om alle boeken in de database op te halen.

U zou de reactie van de API moeten zien, waarin de lijst met boeken staat die in de database zijn toegevoegd.

De onderstaande afbeelding toont de reactie van de API:

Hoe de eindpunt Get Book by ID te testen

Om het eindpunt Get Book by ID te testen, volg deze stappen:

  1. In de Swagger-documentatie, klik op het eindpunt GET /api/v1/books/{id}.

  2. Voer het ID van een boek in het veld id in. U kunt een van de boek-IDs gebruiken die in de database zijn ingevoerd.

  3. Klik op de knop Probeer het uit.

Dit zal een verzoek naar de API sturen om het boek met het gespecificeerde ID op te halen. U zou de respons van de API moeten zien, die het boek met het gespecificeerde ID zal bevatten.

De onderstaande afbeelding toont de respons van de API:

Hoe de eindpunt Add Book te testen

Om het eindpunt Add Book te testen, volg deze stappen:

  1. In de Swagger-documentatie, klik op het eindpunt POST /api/v1/books.

  2. Klik op de knop Probeer het uit.

  3. Voer de boekgegevens in het verzoeklichaam in.

  4. Klik op de Uitvoeren knop.

Dit zal een verzoek naar de API sturen om een nieuw boek aan de database toe te voegen.

Je zou de respons van de API moeten zien, waarin het zojuist aangemaakte boek wordt vermeld.

De onderstaande afbeelding toont de respons van de API:

Hoe de Boek Bijwerken Eindpunt te Testen

Om het Boek Bijwerken eindpunt te testen, volg deze stappen:

  1. In de Swagger-documentatie, klik op het PUT /api/v1/books/{id} eindpunt.

  2. Vul de ID van een boek in het id veld in. Je kunt de id van een van de zojuist toegevoegde boeken gebruiken.

  3. Klik op de Probeer het uit knop.

Dit zal een verzoek naar de API sturen om het boek met de gespecificeerde ID bij te werken.

Je zou de respons van de API moeten zien, waarin het bijgewerkte boek wordt vermeld.

De onderstaande afbeelding toont de respons van de API:

Hoe de Boek Verwijderen Eindpunt te Testen

Om het Boek Verwijderen eindpunt te testen, volg deze stappen:

  1. In de Swagger-documentatie, klik op het DELETE /api/v1/books/{id} eindpunt.

  2. Voer het ID van een boek in het veld id in. U kunt een van de ID’s van de boeken die we zojuist hebben toegevoegd of de gezaaide gegevens gebruiken.

  3. Klik op de knop Probeer het uit.

Dit zal een verzoek naar de API sturen om het boek met het gespecificeerde ID te verwijderen.

De onderstaande afbeelding toont de reactie van de API:

Gefeliciteerd! U heeft alle CRUD-operaties voor boeken geïmplementeerd en de API-eindpunten getest met Swagger, waarbij is geverifieerd dat ze werken zoals verwacht. U kunt nu voortbouwen op deze basis om meer functies en functionaliteiten aan uw API toe te voegen.

Conclusie

Deze handleiding onderzocht hoe u een minimale API kunt maken in ASP.NET Core met .NET 8. We hebben een uitgebreide boeken-API gebouwd die CRUD-operaties ondersteunt, aangepaste uitzonderingen geïmplementeerd, API-eindpunten gedefinieerd en geregistreerd, en Swagger-documentatie ingeschakeld voor eenvoudig testen.

Door deze tutorial te volgen, heeft u een solide basis gelegd voor het bouwen van minimale API’s met ASP.NET Core. U kunt deze kennis nu toepassen en robuuste API’s maken voor verschillende domeinen en industrieën.

Ik hoop dat u deze tutorial zowel nuttig als informatief vond. Bedankt voor het lezen!

Voel u vrij om contact met mij op te nemen via sociale media: