Le API minimali sono una funzionalità entusiasmante introdotta in .NET 6, progettata per rivoluzionare il modo in cui crei le API.

Immagina di costruire API robuste con un codice minimo e zero boilerplate—nessuna lotta con i controller, il routing o il middleware. Questo è ciò che le API minimali ti permettono di fare. L’idea con queste API è di semplificare il processo di sviluppo, rendendolo incredibilmente facile ed efficiente.

In questo articolo, esploreremo il mondo delle API minimali in .NET 8 e ti guideremo nella creazione di un’API completamente funzionante per una libreria. Imparerai come ottenere tutti i libri, recuperare un libro tramite il suo ID, aggiungere nuovi libri e persino eliminare libri. Iniziamo.

Indice dei Contenuti

Prerequisiti

Prima di iniziare, assicurati di avere i seguenti prerequisiti installati sulla tua macchina:

In alternativa, puoi usare Visual Studio 2022, che include il supporto integrato per .NET 8. Ma in questo articolo, utilizzeremo Visual Studio Code. È leggero, facile da usare e multipiattaforma.

Utilizzeremo Swagger UI per testare la nostra API. Swagger UI è uno strumento potente che ti permette di interagire con la tua API direttamente dal browser. Fornisce un’interfaccia utente intuitiva per testare i tuoi endpoint API, facilitando il test e il debug della tua API.

Quando crei un nuovo progetto, installerà automaticamente i pacchetti necessari e configurerà il progetto per utilizzare Swagger UI. .NET 8 include Swagger UI di default, quindi che tu crei la tua applicazione in Visual Studio o con .NET, Swagger UI sarà configurato per te.

Esegui la tua applicazione e Swagger UI si aprirà automaticamente nel tuo browser – ma poiché stiamo usando VS Code, dobbiamo cliccare sul numero di porta nel nostro terminale.

Puoi trovare il codice sorgente per questo progetto su GitHub.

Introduzione alle API Minime

Immagina di lavorare in un codebase con numerosi endpoint, rendendolo piuttosto grande e complesso. Tradizionalmente, costruire un’API in ASP.NET Core comporta l’uso di controller, routing, middleware e una quantità significativa di codice boilerplate. Ma ci sono due approcci per costruire un’API in ASP.NET Core: il modo tradizionale e il modo minimale.

Il modo tradizionale è familiare alla maggior parte degli sviluppatori, coinvolgendo controller e un’ampia infrastruttura di codice. Il modo minimale, introdotto in .NET 6, ti permette di creare API con un codice minimo e zero boilerplate. Questo approccio semplifica il processo di sviluppo, permettendoti di concentrarti sulla scrittura della logica aziendale piuttosto che affrontare il codice infrastrutturale.

Le API minime sono leggere, veloci e perfette per costruire API di piccole e medie dimensioni. Sono ideali per la prototipazione, la costruzione di microservizi o la creazione di API semplici che non richiedono molta complessità. In questo manuale, esploreremo il mondo delle API minime in .NET 6 e impareremo a creare un’API di libreria completamente funzionante da zero.

Come Creare un’API Minima

Creare un’API minima è semplice quando si utilizza il dotnet CLI, poiché il template predefinito è già un’API minima. Ma se usi Visual Studio, dovrai rimuovere il codice boilerplate che viene con il template del progetto.

Iniziamo utilizzando il dotnet CLI per creare un progetto di API minimale.


dotnet new webapi  -n BookStoreApi

Il comando dotnet new webapi crea un nuovo progetto di API minimale chiamato BookStoreApi. Questo progetto contiene i file e le cartelle necessari per iniziare.

Esploriamo la struttura del progetto:

  • Program.cs: Il punto di ingresso dell’applicazione, dove viene configurato l’host.

  • bookapi-minimal.sln: Il file della soluzione che contiene il progetto.

  • bookapi-minimal.http: Un file che contiene richieste HTTP di esempio per testare l’API.

  • bookapi-minimal.csproj: Il file del progetto che contiene la configurazione del progetto.

  • appsettings.json: Il file di configurazione che archivia le impostazioni dell’applicazione.

  • appsettings.Development.json: Il file di configurazione per l’ambiente di sviluppo.

Quando apri il file program.cs, noterai che il codice è minimale. Il file Program.cs contiene il seguente codice:


var builder = WebApplication.CreateBuilder(args);

// Aggiungi servizi al container.
// Ulteriori informazioni sulla configurazione di Swagger/OpenAPI su https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configura il pipeline delle richieste HTTP.
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);
}

Se non capisci ancora completamente il codice, non preoccuparti—lo esamineremo in dettaglio nelle sezioni successive. Il punto chiave è che le API minimali richiedono pochissimo codice, il che è uno dei loro principali vantaggi.

Il codice predefinito configura un’API semplice per le previsioni meteorologiche che puoi utilizzare per testare la tua configurazione. Genera un elenco di previsioni meteorologiche e le restituisce quando effettui una richiesta GET all’endpoint /weatherforecast. Inoltre, il codice include Swagger UI per aiutarti a testare l’API.

Presta particolare attenzione al metodo app.MapGet, che mappa una rotta a una funzione di gestione. In questo caso, mappa la rotta /weatherforecast a una funzione che restituisce un elenco di previsioni meteorologiche. Utilizzeremo metodi simili per creare i nostri endpoint nelle sezioni successive.

Prima di iniziare a creare la struttura delle cartelle del nostro progetto, comprendiamo i metodi HTTP sia nelle API basate su Controller che nelle API Minimali.

Metodi HTTP nelle API basate su Controller e nelle API Minimali

In un approccio basato su Controller, che è il modo tradizionale di creare web API, devi creare una classe di controller e definire metodi per ciascun metodo HTTP. Ad esempio:

  • Per creare un metodo GET, si utilizza l’attributo [HttpGet].

  • Per creare un metodo POST, si utilizza l’attributo [HttpPost].

  • Per creare un metodo PUT, si utilizza l’attributo [HttpPut].

  • Per creare un metodo DELETE, si utilizza l’attributo [HttpDelete].

Questo è come vengono creati gli endpoint in un approccio basato su Controller.

Invece, le API Minimal utilizzano metodi come app.MapGet, app.MapPost, app.MapPut e app.MapDelete per creare endpoint. Questa è la principale differenza tra i due approcci: le API basate su Controller utilizzano attributi per definire gli endpoint, mentre le API Minimal utilizzano metodi.

Ora che sai come gestire le richieste HTTP sia nelle API basate su Controller che nelle API Minimal, creiamo la struttura della cartella del nostro progetto.

Prima di creare la nostra struttura di cartelle del progetto, eseguiamo prima ciò che abbiamo. Come abbiamo appreso in precedenza, quando si crea un progetto con Visual Studio o .NET CLI, viene fornito con un progetto WeatherForecast predefinito che possiamo eseguire e vedere sull’UI. Eseguiamolo per assicurarci che tutto funzioni prima di procedere con la creazione della nostra cartella del progetto.

Esegui questo comando:


dotnet run

Dovresti vedere il seguente output:

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

Questo significa che l’applicazione è in esecuzione e in ascolto su http://localhost:5228. Come ho menzionato sopra, poiché stiamo utilizzando il dotnet CLI e Visual Studio Code, l’applicazione non aprirà automaticamente il browser per noi. Dobbiamo farlo manualmente.

Apri il tuo browser e naviga su http://localhost:5228/swagger/index.html per vedere la risposta predefinita dall’API.

Dovresti vedere qualcosa del genere:

Ora la prossima cosa che dobbiamo fare è trovare un modo per strutturare il nostro progetto e creare i file e le cartelle necessari per iniziare.

File del Progetto API Minimale

Per organizzare il nostro progetto, creeremo una gerarchia di cartelle strutturata. Questo ci aiuterà a mantenere il nostro codice pulito e manutenibile. Ecco la struttura delle cartelle che utilizzeremo:

  • AppContext: Contiene il contesto del database e le configurazioni correlate.

  • Configurazioni: Contiene le configurazioni di Entity Framework Core e i dati iniziali per il database.

  • Contratti: Contiene gli Oggetti di Trasferimento Dati (DTO) utilizzati nella nostra applicazione.

  • Endpoint: Dove definiamo e configuriamo i nostri endpoint API minimale.

  • Eccezioni: Contiene le classi di eccezione personalizzate utilizzate nel progetto.

  • Estensioni: Contiene i metodi di estensione che utilizzeremo in tutto il progetto.

  • Modelli: Contiene i modelli di logica aziendale.

  • Servizi: Contiene le classi di servizio che implementano la logica aziendale.

  • Interfacce: Contiene le definizioni delle interfacce utilizzate per mappare i nostri servizi.

In Visual Studio Code, puoi creare questa struttura delle cartelle nel seguente modo:

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

Dopo averla impostata, la struttura della cartella del tuo progetto dovrebbe assomigliare a questa:

Ora che la struttura del nostro progetto è impostata, possiamo procedere e iniziare a scrivere il nostro codice. Cominciamo creando i nostri modelli.

Come Creare i Modelli

In questa sezione, creeremo i modelli per la nostra applicazione. I modelli sono i mattoni fondamentali della nostra applicazione, rappresentano i dati con cui la nostra applicazione lavorerà. Per il nostro esempio, creeremo un modello per un libro.

Per iniziare, crea una cartella chiamata Models nella directory del tuo progetto. All’interno di questa cartella, crea un file chiamato BookModel.cs e aggiungi il seguente codice:

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

Questa classe BookModel definisce le proprietà che rappresentano i dettagli di un libro, come il titolo, autore, descrizione, categoria, lingua e pagine totali. Ogni proprietà è progettata per contenere informazioni specifiche sul libro, semplificando la gestione e la manipolazione dei dati del libro all’interno della nostra applicazione.

Ora che abbiamo creato il nostro modello, creiamo il nostro contesto del database.

Come Creare il Contesto del Database

Il contesto del database è una classe che rappresenta una sessione con il database. È responsabile dell’interazione con il database ed esecuzione delle operazioni del database. Nella nostra applicazione, useremo Entity Framework Core per interagire con il database.

Installa i Pacchetti Richiesti

Prima di creare il nostro contesto del database, dobbiamo installare i seguenti pacchetti:

Potete installare questi pacchetti utilizzando i seguenti comandi:

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

Verifica Installazione Pacchetti

Per verificare che i pacchetti siano installati, aprire il file bookapi-minimal.csproj nella directory principale del vostro progetto. Dovreste vedere i pacchetti installati elencati come segue:

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

Questo conferma che i pacchetti sono stati installati con successo.

Ora creiamo il nostro contesto del database.

Nella cartella AppContext, crea un nuovo file chiamato ApplicationContext.cs e aggiungi il seguente codice:

// AppContext/ApplicationContext.cs

using bookapi_minimal.Models;
using Microsoft.EntityFrameworkCore;

namespace bookapi_minimal.AppContext
{

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

        // Schema predefinito per il contesto del database
        private const string DefaultSchema = "bookapi";


       // DbSet per rappresentare la raccolta di libri nel nostro database
        public DbSet<BookModel> Books { get; set; }

        // Costruttore per configurare il contesto del database

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

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

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

        }

    }
}

Analizziamo il codice sopra:

  • Definiamo una classe chiamata ApplicationContext che eredita da DbContext. La classe DbContext fa parte di Entity Framework Core e rappresenta una sessione con il database.

  • Il costruttore accetta un’istanza di DbContextOptions<ApplicationContext>. Questo costruttore viene utilizzato per configurare le opzioni del contesto del database.

  • Definiamo una proprietà chiamata Books di tipo DbSet<BookModel>. Questa proprietà rappresenta la raccolta di libri nel nostro database.

  • Sovrascriviamo il metodo OnModelCreating per configurare lo schema del database e applicare eventuali configurazioni definite nella nostra applicazione.

Ora che abbiamo creato il nostro contesto del database, creiamo il nostro metodo di estensione e registriamo il nostro contesto del database nel contenitore di iniezione delle dipendenze.

Creare un Metodo di Estensione

Prima di creare il metodo di estensione, comprendiamo cos’è un metodo di estensione nel contesto di ASP.NET Core.

Un metodo di estensione è un metodo statico che aggiunge nuove funzionalità a un tipo esistente senza modificare il tipo originale. In ASP.NET Core, i metodi di estensione sono comunemente utilizzati per estendere la funzionalità dell’interfaccia IServiceCollection, che viene utilizzata per registrare i servizi nel contenitore di iniezione delle dipendenze.

I servizi sono componenti che forniscono funzionalità a un’applicazione, come l’accesso al database, la registrazione e la configurazione. Creando un metodo di estensione per l’interfaccia IServiceCollection, è possibile semplificare il processo di registrazione dei propri servizi nel contenitore di iniezione delle dipendenze.

Invece di mettere tutto nel file Program.cs, creeremo un metodo di estensione per registrare i nostri servizi nel contenitore di iniezione delle dipendenze. Questo ci aiuterà a mantenere il nostro codice pulito e organizzato.

Nella cartella Extensions, crea un nuovo file chiamato ServiceExtensions.cs e aggiungi il seguente codice:

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

            // Aggiunta del contesto del database
            builder.Services.AddDbContext<ApplicationContext>(configure =>
            {
                configure.UseSqlServer(builder.Configuration.GetConnectionString("sqlConnection"));
            });

            // Aggiunta dei validatori dall'assembly corrente
            builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
        }
    }
}

Rompiamo il codice sopra:

  • Definiamo una classe statica chiamata ServiceExtensions che contiene un metodo di estensione chiamato AddApplicationServices. Questo metodo estende l’interfaccia IHostApplicationBuilder, che viene utilizzata per configurare la pipeline di elaborazione delle richieste dell’applicazione.

  • Il metodo AddApplicationServices accetta un’istanza di IHostApplicationBuilder come parametro. Questo parametro viene utilizzato per accedere alla configurazione e ai servizi dell’applicazione.

  • Aggiungiamo il ApplicationContext al contenitore di iniezione delle dipendenze e lo configuriamo per utilizzare SQL Server come fornitore di database. Recuperiamo la stringa di connessione dal file appsettings.json utilizzando il metodo GetConnectionString.

  • Aggiungiamo validators dall’attuale assembly utilizzando il metodo AddValidatorsFromAssembly. Questo metodo scansiona l’assembly attuale per classi che implementano l’interfaccia IValidator e le registra nel contenitore di iniezione delle dipendenze.

Successivamente, dobbiamo aggiungere la stringa di connessione al file appsettings.json. Aggiungi il seguente codice al tuo file appsettings.json:

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

Assicurati di sostituire your_password con la tua attuale password di SQL Server.

Il tuo file appsettings.json dovrebbe apparire così:


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

Congratulazioni! Hai creato con successo il contesto del database, il metodo di estensione e la stringa di connessione per la tua applicazione. Nella prossima sezione, creeremo un Contratto.

Come Creare un Contratto

I contratti sono Oggetti di Trasferimento Dati (DTO) che definiscono la struttura dei dati scambiati tra il client e il server. Nella nostra applicazione, creeremo contratti per rappresentare i dati inviati e ricevuti dai nostri endpoint API.

Ecco i contratti che creeremo:

  • CreateBookRequest: Questo rappresenta i dati inviati quando si crea un nuovo libro.

  • UpdateBookRequest: tHI Rappresenta i dati inviati quando si aggiorna un libro esistente.

  • BookResponse: Rappresenta i dati restituiti quando si recupera un libro.

  • ErrorResponse: Rappresenta la risposta di errore restituita quando si verifica un’eccezione.

  • ApiResponse: Rappresenta la risposta restituita dall’API.

Nella cartella Contracts, crea un nuovo file chiamato CreateBookRequest e aggiungi il seguente codice:

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

Nella cartella Contracts, crea un nuovo file chiamato UpdateBookRequest e aggiungi il seguente codice:


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

    }
}

Nella cartella Contratti, crea un nuovo file chiamato BookResponse e aggiungi il seguente codice:

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

Nella cartella Contratti, crea un nuovo file chiamato ErrorResponse e aggiungi il seguente codice:



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

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

    }

}

Nella cartella Contratti, crea un nuovo file chiamato ApiResponse e aggiungi il seguente codice:

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

Questi contratti ci aiutano a definire la struttura dei dati scambiati tra il client e il server, facilitando il lavoro con i dati nella nostra applicazione.

Nella prossima sezione, creeremo servizi per implementare la logica di business della nostra applicazione.

Come Aggiungere Servizi

I servizi sono componenti che forniscono funzionalità a un’applicazione. Nella nostra applicazione, creeremo servizi per implementare la logica di business. Creeremo servizi per gestire le operazioni CRUD per i libri, convalidare i dati dei libri e gestire le eccezioni.

In ASP.NET Core, i servizi vengono registrati nel contenitore di injection delle dipendenze e possono essere iniettati in altri componenti, come controller e endpoint, ma questa è una API minimale quindi inietteremo direttamente i servizi negli endpoint.

Creiamo un’interfaccia per i nostri servizi. Nella cartella Interfaces, crea un nuovo file chiamato IBookService.cs e aggiungi il seguente codice:

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

Analizziamo il codice sopra: Abbiamo definito un’interfaccia chiamata IBookService che contiene metodi per gestire le operazioni CRUD per i libri. L’interfaccia definisce i seguenti metodi:

  • AddBookAsync: Aggiunge un nuovo libro al database.

  • GetBookByIdAsync: Recupera un libro tramite il suo ID.

  • GetBooksAsync: Recupera tutti i libri dal database.

  • UpdateBookAsync: Aggiorna un libro esistente.

Stiamo utilizzando il Contratto che abbiamo creato in precedenza nella cartella Contracts. L’interfaccia IBookService definisce la struttura dei metodi che verranno implementati dalle classi di servizio. Questo ci aiuta a separare l’interfaccia dall’implementazione, rendendo più facile mantenere e testare il nostro codice.

Ora che abbiamo creato l’interfaccia, creiamo la classe di servizio che implementa l’interfaccia.

Come Implementare il Servizio Libri

Questo servizio implementerà l’interfaccia IBookService e fornirà la logica di business per la nostra applicazione. Nella cartella Services, crea un nuovo file chiamato BookService.cs. Il tuo file iniziale dovrebbe apparire così:


// Services/BookService.cs

namespace bookapi_minimal.Services
{
    public class BookService
    {

    }
}

La prima cosa che dobbiamo fare è aggiungere l’interfaccia alla classe BookService. Aggiorna la classe BookService per implementare l’interfaccia IBookService come segue:



// Services/BookService.cs



using bookapi_minimal.Interfaces;

namespace bookapi_minimal.Services
{
    public class BookService:IBookService
    {

    }
}

Quando fai questo, il tuo VS Code potrebbe mostrare un errore perché non abbiamo implementato i metodi dell’interfaccia. Procediamo e implementiamo i metodi nella classe BookService.

In VS Code puoi usare la scorciatoia Ctrl + . per implementare i metodi dell’interfaccia. Poi vedrai il seguente codice generato per te:


using bookapi_minimal.Contracts;
using bookapi_minimal.Interfaces;

namespace bookapi_minimal.Services
{
     // Classe di servizio per la gestione dei libri
   public class BookService : IBookService
   {
       // Metodo per aggiungere un nuovo libro al database
       public Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest)
       {
           throw new NotImplementedException();
       }

      // Metodo per eliminare un libro dal database
       public Task<bool> DeleteBookAsync(Guid id)
       {
           throw new NotImplementedException();
       }

       // Metodo per ottenere un libro dal database tramite il suo ID

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

      // Metodo per ottenere tutti i libri dal database
       public Task<IEnumerable<BookResponse>> GetBooksAsync()
       {
           throw new NotImplementedException();
       }

       // Metodo per aggiornare un libro nel database
       public Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest updateBookRequest)
       {
           throw new NotImplementedException();
       }
   }
}

Ora puoi vedere che i metodi nell’interfaccia sono stati implementati nella classe BookService. Implementeremo la logica di business per ogni metodo nella sezione successiva.

Prima di farlo, aggiungiamo le dipendenze necessarie alla classe BookService. Dobbiamo iniettare le dipendenze ApplicationContext e ILogger nella classe BookService. ApplicationContext viene utilizzato per interagire con il database, mentre ILogger viene utilizzato per il logging.

Per iniettare le dipendenze, aggiorna la classe BookService come segue:


// Services/BookService.cs

// ...
 private readonly ApplicationContext _context; // Contesto del database
  private readonly ILogger<BookService> _logger; // Logger per registrare informazioni ed errori

//..

Dato che abbiamo aggiunto le dipendenze, dobbiamo aggiornare il costruttore di BookService per accettare le dipendenze. Aggiorna il costruttore di BookService come segue:


// Services/BookService.cs

// ...

  // Costruttore per inizializzare il contesto del database e il logger
 public BookService(ApplicationContext context, ILogger<BookService> logger)
 {
            _context = context;
            _logger = logger;
}

// ...

Ora che abbiamo aggiunto le dipendenze e aggiornato il costruttore, possiamo implementare la logica di business per ogni metodo nella classe BookService.

Creiamo la logica per le operazioni di CREATE, READ, UPDATE e DELETE nella classe BookService.

Come Implementare il Metodo AddBookAsync

Come ho già detto, utilizzeremo il metodo AddBookAsync per aggiungere un nuovo libro al database. In questo metodo, creeremo una nuova entità libro, mapperemo i dati dall’oggetto CreateBookRequest all’entità libro e salveremo l’entità libro nel database. Restituiremo inoltre l’entità libro come oggetto BookResponse.

Aggiorna il metodo AddBookAsync nella classe BookService come segue:

// Services/BookService.cs

// ...
 /// <summary>
        /// Aggiungi un nuovo libro
        /// </summary>
        /// <param name="createBookRequest">Richiesta di libro da aggiungere</param>
        /// <returns>Dettagli del libro creato</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
                };

                // Aggiungi il libro al database
                _context.Books.Add(book);
                await _context.SaveChangesAsync();
                _logger.LogInformation("Book added successfully.");

                // Restituisci i dettagli del libro creato
                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 questo codice, stiamo creando una nuova entità libro dall’oggetto CreateBookRequest, mappando i dati dall’oggetto CreateBookRequest all’entità libro, salvando l’entità libro nel database e restituendo l’entità libro come oggetto BookResponse.

Stiamo anche registrando informazioni ed errori utilizzando la dipendenza ILogger. Se si verifica un’eccezione durante il processo, registriamo il messaggio di errore e rilanciamo l’eccezione.

Ora che abbiamo implementato il metodo AddBookAsync, implementiamo il metodo GetBookByIdAsync.

Come Implementare il Metodo GetBookByIdAsync

Il metodo GetBookByIdAsync viene utilizzato per recuperare un libro dal database tramite il suo ID. In questo metodo, interrogheremo il database per il libro con l’ID specificato, mapperemo l’entità libro a un oggetto BookResponse e restituiremo l’oggetto BookResponse.

Aggiorna il metodo GetBookByIdAsync nella classe BookService come segue:


// Servizi/BookService.cs

//... 

    /// <summary>
        /// Ottieni un libro tramite il suo ID
        /// </summary>
        /// <param name="id">ID del libro</param>
        /// <returns>Dettagli del libro</returns>
        public async Task<BookResponse>  GetBookByIdAsync(Guid id)
        {
            try
            {
                // Trova il libro tramite il suo ID
                var book = await _context.Books.FindAsync(id);
                if (book == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return null;
                }

                // Restituisci i dettagli del libro
                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 questo codice, stiamo interrogando il database per il libro con l’ID specificato, mappando l’entità del libro a un oggetto BookResponse e restituendo l’oggetto BookResponse. Stiamo anche registrando informazioni ed errori utilizzando la dipendenza ILogger.

Se il libro con l’ID specificato non viene trovato, registriamo un messaggio di avviso e restituiamo null. Se si verifica un’eccezione durante il processo, registriamo il messaggio di errore e rilanciamo l’eccezione.

Ora che abbiamo implementato il metodo GetBookByIdAsync, implementiamo il metodo GetBooksAsync.

Come Implementare il Metodo GetBooksAsync

Il metodo GetBooksAsync viene utilizzato per recuperare tutti i libri dal database. In questo metodo, interrogheremo il database per tutti i libri, mapperemo ogni entità libro a un oggetto BookResponse e restituiremo un elenco di oggetti BookResponse.

Aggiorna il metodo GetBooksAsync nella classe BookService come segue:



// Services/BookService.cs

//... 


  /// <summary>
        /// Ottieni tutti i libri
        /// </summary>
        /// <returns>Elenco di tutti i libri</returns>
        public async Task<IEnumerable<BookResponse>> GetBooksAsync()
        {
            try
            {
                // Ottieni tutti i libri dal database
                var books = await _context.Books.ToListAsync();

                // Restituisci i dettagli di tutti i libri
                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;
            }
        }
//...

Qui, stiamo interrogando il database per tutti i libri, mappando ogni entità libro a un oggetto BookResponse e restituendo un elenco di oggetti BookResponse. Stiamo anche registrando informazioni ed errori utilizzando la dipendenza ILogger. Se si verifica un’eccezione durante il processo, registriamo il messaggio di errore e rilanciamo l’eccezione.

Ora che abbiamo implementato il metodo GetBooksAsync, implementiamo il metodo UpdateBookAsync.

Come Implementare il Metodo UpdateBookAsync

Il metodo UpdateBookAsync viene utilizzato per aggiornare un libro esistente nel database. In questo metodo, interrogheremo il database per trovare il libro con l’ID specificato, aggiorneremo l’entità del libro con i dati provenienti dall’oggetto UpdateBookRequest, salveremo l’entità del libro aggiornata nel database e restituiremo l’entità del libro aggiornata come oggetto BookResponse.

Aggiorna il metodo UpdateBookAsync nella classe BookService come segue:

// Services/BookService.cs
 //...
 /// <summary>
        /// Aggiorna un libro esistente
        /// </summary>
        /// <param name="id">ID del libro da aggiornare</param>
        /// <param name="book">Modello del libro aggiornato</param>
        /// <returns>Dettagli del libro aggiornato</returns>
        public async Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest book)
        {
            try
            {
                // Trova il libro esistente tramite il suo ID
                var existingBook = await _context.Books.FindAsync(id);
                if (existingBook == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return null;
                }

                // Aggiorna i dettagli del libro
                existingBook.Title = book.Title;
                existingBook.Author = book.Author;
                existingBook.Description = book.Description;
                existingBook.Category = book.Category;
                existingBook.Language = book.Language;
                existingBook.TotalPages = book.TotalPages;

                // Salva le modifiche nel database
                await _context.SaveChangesAsync();
                _logger.LogInformation("Book updated successfully.");

                // Restituisci i dettagli del libro aggiornato
                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;
            }
        }
//...

Qui, stiamo interrogando il database per il libro con l’ID specificato, aggiornando l’entità del libro con i dati provenienti dall’oggetto UpdateBookRequest, salvando l’entità del libro aggiornata nel database e restituendo l’entità del libro aggiornata come oggetto BookResponse. Stiamo anche registrando informazioni ed errori utilizzando la dipendenza ILogger.

Se il libro con l’ID specificato non viene trovato, registriamo un messaggio di avviso e restituiamo null. Se si verifica un’eccezione durante il processo, registriamo il messaggio di errore e rilanciamo l’eccezione.

Ora che abbiamo implementato il metodo UpdateBookAsync, implementiamo il metodo DeleteBookAsync.

Come Implementare il Metodo DeleteBookAsync

Il metodo DeleteBookAsync viene utilizzato per eliminare un libro esistente dal database. In questo metodo, interrogheremo il database per il libro con l’ID specificato, rimuoveremo l’entità del libro dal database e restituiremo un valore booleano che indica se il libro è stato eliminato con successo.

Aggiorna il metodo DeleteBookAsync nella classe BookService come segue:

// Servizi/BookService.cs

 //...


/// <summary>
        /// Elimina un libro tramite il suo ID
        /// </summary>
        /// <param name="id">ID del libro da eliminare</param>
        /// <returns>True se il libro è stato eliminato, false altrimenti</returns>
        public async Task<bool> DeleteBookAsync(Guid id)
        {
            try
            {
                // Trova il libro tramite il suo ID
                var book = await _context.Books.FindAsync(id);
                if (book == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return false;
                }

                // Rimuove il libro dal 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 questo codice, stiamo interrogando il database per il libro con l’ID specificato, rimuovendo l’entità del libro dal database e restituendo un valore booleano che indica se il libro è stato eliminato con successo. Stiamo inoltre registrando informazioni ed errori utilizzando la dipendenza ILogger.

Se il libro con l’ID specificato non viene trovato, registriamo un messaggio di avviso e restituiamo false. Se si verifica un’eccezione durante il processo, registriamo il messaggio di errore e rilanciamo l’eccezione.

Ora hai implementato con successo la logica aziendale per i metodi AddBookAsync, GetBookByIdAsync, GetBooksAsync, UpdateBookAsync e DeleteBookAsync nella classe BookService. Questi metodi gestiscono le operazioni CRUD per i libri, convalidano i dati dei libri e gestiscono le eccezioni. A questo punto, la tua classe BookService dovrebbe apparire così:



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; // Contesto del database
        private readonly ILogger<BookService> _logger; // Logger per il logging delle informazioni e degli errori
          // Costruttore per inizializzare il contesto del database e il logger
        public BookService(ApplicationContext context, ILogger<BookService> logger)
        {
            _context = context;
            _logger = logger;
        }

           /// Aggiungi un nuovo libro
        /// </summary>
        /// <param name="createBookRequest">Richiesta di libro da aggiungere</param>
        /// <returns>Dettagli del libro creato</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
                };

                // Aggiungi il libro al database
                _context.Books.Add(book);
                await _context.SaveChangesAsync();
                _logger.LogInformation("Book added successfully.");

                // Restituisci i dettagli del libro creato
                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>
        /// Ottieni un libro tramite il suo ID
        /// </summary>
        /// <param name="id">ID del libro</param>
        /// <returns>Dettagli del libro</returns>
        public async Task<BookResponse>  GetBookByIdAsync(Guid id)
        {
            try
            {
                // Trova il libro tramite il suo ID
                var book = await _context.Books.FindAsync(id);
                if (book == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return null;
                }

                // Restituisci i dettagli del libro
                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>
        /// Ottieni tutti i libri
        /// </summary>
        /// <returns>Elenco di tutti i libri</returns>
        public async Task<IEnumerable<BookResponse>> GetBooksAsync()
        {
            try
            {
                // Ottieni tutti i libri dal database
                var books = await _context.Books.ToListAsync();

                // Restituisci i dettagli di tutti i libri
                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>
        /// Aggiorna un libro esistente
        /// </summary>
        /// <param name="id">ID del libro da aggiornare</param>
        /// <param name="book">Modello del libro aggiornato</param>
        /// <returns>Dettagli del libro aggiornato</returns>
        public async Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest book)
        {
            try
            {
                // Trova il libro esistente tramite il suo ID
                var existingBook = await _context.Books.FindAsync(id);
                if (existingBook == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return null;
                }

                // Aggiorna i dettagli del libro
                existingBook.Title = book.Title;
                existingBook.Author = book.Author;
                existingBook.Description = book.Description;
                existingBook.Category = book.Category;
                existingBook.Language = book.Language;
                existingBook.TotalPages = book.TotalPages;

                // Salva le modifiche nel database
                await _context.SaveChangesAsync();
                _logger.LogInformation("Book updated successfully.");

                // Restituisci i dettagli del libro aggiornato
                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>
        /// Elimina un libro tramite il suo ID
        /// </summary>
        /// <param name="id">ID del libro da eliminare</param>
        /// <returns>True se il libro è stato eliminato, false altrimenti</returns>
        public async Task<bool> DeleteBookAsync(Guid id)
        {
            try
            {
                // Trova il libro tramite il suo ID
                var book = await _context.Books.FindAsync(id);
                if (book == null)
                {
                    _logger.LogWarning($"Book with ID {id} not found.");
                    return false;
                }

                // Rimuovi il libro dal 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;
            }
        }

    }
}

Complimenti! Hai implementato con successo la logica aziendale per i metodi AddBookAsync, GetBookByIdAsync, GetBooksAsync, UpdateBookAsync e DeleteBookAsync nella classe BookService.

C’è una cosa che dobbiamo fare: dobbiamo registrare il servizio nel nostro metodo di estensione. Procediamo a farlo.

Nel tuo file ServiceExtensions.cs, aggiungi il seguente codice:


// Extensions/ServiceExtensions.cs

//..

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

Questo registrerà la classe BookService come un servizio scoped. Questo significa che il servizio verrà creato una volta per richiesta e smaltito dopo che la richiesta è completata.

Ora che abbiamo il servizio funzionante, procediamo a creare le classi di eccezione.

Come Creare Eccezioni

Gestire correttamente le eccezioni è fondamentale per garantire la stabilità e la affidabilità di un’applicazione. Nel contesto di ASP.NET Core, ci sono due principali tipi di eccezioni:

  • Eccezioni di Sistema: Queste sono eccezioni lanciate dal runtime .NET o dal sistema sottostante.

  • Eccezioni dell’Applicazione: Queste sono eccezioni lanciate dal codice dell’applicazione per gestire errori o condizioni specifiche.

In ASP.NET Core con .NET 8, è stata introdotta una nuova funzionalità chiamata gestione delle eccezioni globali. Questa funzionalità consente di gestire le eccezioni globalmente nella tua applicazione, facilitando la gestione degli errori e fornendo un’esperienza utente coerente.

Nella nostra applicazione, creeremo classi di eccezione personalizzate per gestire errori e condizioni specifiche. Utilizzeremo anche la funzionalità di gestione delle eccezioni globali per gestire le eccezioni globalmente, garantendo un approccio uniforme alla gestione degli errori in tutta l’applicazione.

Creeremo le seguenti classi di eccezione:

  • NoBookFoundException: Lanciata quando un libro con l’ID specificato non viene trovato.

  • BookDoesNotExistException: Lanciata quando un libro con l’ID specificato non esiste.

  • GlobalExceptionHandler: Gestisce le eccezioni globalmente nell’applicazione.

Nella cartella Exceptions, crea un nuovo file chiamato NoBookFoundException.cs e aggiungi il seguente codice:


// Exceptions/NoBookFoundException.cs

namespace bookapi_minimal.Exceptions
{

    public class NoBookFoundException : Exception
    {

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

In questo codice, stiamo creando una classe di eccezione personalizzata denominata NoBookFoundException che eredita dalla classe Exception. La classe NoBookFoundException viene utilizzata per gestire lo scenario in cui non vengono trovati libri nel database. Stiamo inoltre fornendo un messaggio di errore personalizzato per l’eccezione.

Nella cartella Exceptions, creare un nuovo file denominato BookDoesNotExistException.cs e aggiungere il seguente codice:

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 questo codice, stiamo creando una classe di eccezione personalizzata denominata BookDoesNotExistException che eredita dalla classe Exception. La classe BookDoesNotExistException viene utilizzata per gestire lo scenario in cui un libro con l’ID specificato non esiste nel database. Stiamo inoltre fornendo un messaggio di errore personalizzato per l’eccezione.

Nella cartella Exceptions, creare un nuovo file denominato GlobalExceptionHandler.cs e aggiungere il seguente codice:

// Eccezioni/GlobalExceptionHandler.cs

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

namespace bookapi_minimal.Exceptions
{

   // Classe gestore di eccezioni globali che implementa IExceptionHandler
    public class GlobalExceptionHandler : IExceptionHandler
    {
        private readonly ILogger<GlobalExceptionHandler> _logger;

        // Costruttore per inizializzare il logger
        public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
        {
            _logger = logger;
        }

        // Metodo per gestire le eccezioni in modo asincrono
        public async ValueTask<bool> TryHandleAsync(
            HttpContext httpContext,
            Exception exception,
            CancellationToken cancellationToken)
        {
            // Registrare i dettagli dell'eccezione
            _logger.LogError(exception, "An error occurred while processing your request");

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

            // Determinare il codice di stato in base al tipo di eccezione
            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;
            }

            // Impostare il codice di stato della risposta
            httpContext.Response.StatusCode = errorResponse.StatusCode;

            // Scrivere la risposta di errore come JSON
            await httpContext.Response.WriteAsJsonAsync(errorResponse, cancellationToken);

            // Restituire true per indicare che l'eccezione è stata gestita
            return true;
        }
    }
}

Analizziamo il codice sopra:

  • Definiamo una classe chiamata GlobalExceptionHandler che implementa l’interfaccia IExceptionHandler. L’interfaccia IExceptionHandler viene utilizzata per gestire le eccezioni globalmente nell’applicazione.

  • La classe GlobalExceptionHandler contiene un costruttore che inizializza la dipendenza ILogger<GlobalExceptionHandler>. L’ILogger viene utilizzato per registrare informazioni ed errori.

  • Il metodo TryHandleAsync viene utilizzato per gestire le eccezioni in modo asincrono. Questo metodo accetta HttpContext, Exception e CancellationToken come parametri.

  • Registriamo i dettagli dell’eccezione utilizzando la dipendenza ILogger.

  • Creiamo un oggetto ErrorResponse per rappresentare la risposta di errore restituita dall’API. L’oggetto ErrorResponse contiene il messaggio di errore, il titolo e il codice di stato.

  • Determiniamo il codice di stato in base al tipo di eccezione. Se l’eccezione è una BadHttpRequestException, impostiamo il codice di stato su BadRequest. Se l’eccezione è una NoBookFoundException o BookDoesNotExistException, impostiamo il codice di stato su NotFound. Altrimenti, impostiamo il codice di stato su InternalServerError.

  • Impostiamo il codice di stato della risposta utilizzando la proprietà httpContext.Response.StatusCode.

  • Scriviamo la risposta di errore come JSON utilizzando il metodo httpContext.Response.WriteAsJsonAsync.

  • Restituiamo true per indicare che l’eccezione è stata gestita con successo.

Ora che abbiamo creato le classi di eccezione, registriamo il GlobalExceptionHandler nel contenitore di iniezione di dipendenze. Poiché abbiamo creato un metodo di estensione per registrare i servizi nel contenitore di iniezione di dipendenze, aggiungeremo il GlobalExceptionHandler alla classe ServiceExtensions.

Aggiorniamo la classe ServiceExtensions nella cartella Extensions come segue:


// Extensions/ServiceExtensions.cs
//...
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();

builder.Services.AddProblemDetails();

//...

Il metodo AddExceptionHandler registra il GlobalExceptionHandler nel contenitore di iniezione delle dipendenze. Il metodo AddProblemDetails registra la classe ProblemDetails nel contenitore di iniezione delle dipendenze.

Ora che abbiamo registrato il GlobalExceptionHandler nel contenitore di iniezione delle dipendenze, possiamo utilizzarlo per gestire le eccezioni globalmente nella nostra applicazione. Nella prossima sezione, creeremo gli endpoint API per interagire con i dati dei libri.

Come Creare gli Endpoint API

Nel contesto delle API minimali in ASP.NET Core, ci sono molti modi per configurare i tuoi endpoint.

Puoi definirli direttamente nel tuo file Program.cs. Ma man mano che il tuo progetto cresce e hai bisogno di aggiungere più endpoint o funzionalità, è utile organizzare meglio il tuo codice. Un modo per raggiungere questo obiettivo è creare una classe separata per gestire tutti gli endpoint.

Come abbiamo discusso sopra, le API minimali non utilizzano controllori o viste come le applicazioni ASP.NET Core tradizionali. Invece, utilizzano metodi come MapGet, MapPost, MapPut e MapDelete per definire metodi HTTP e percorsi per gli endpoint API.

Per iniziare, vai nella cartella Endpoints e crea un nuovo file chiamato BookEndpoints.cs. Aggiungi il seguente codice al file:


// Endpoints/BookEndpoints.cs



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


            return app;
        }
    }
}

La classe BookEndpoints contiene un metodo MapBookEndPoint che restituisce un oggetto IEndpointRouteBuilder. L’oggetto IEndpointRouteBuilder viene utilizzato per definire i metodi HTTP e le rotte per gli endpoint dell’API. Nelle sezioni seguenti, definiranno gli endpoint dell’API per creare, leggere, aggiornare e eliminare libri.

Come Creare l’Endpoint AddBookAsync per i Libri

In questa sezione, creeremo l’endpoint AddBookAsync. Questo endpoint accetterà un oggetto Book come payload JSON e lo aggiungerà al database. Utilizzeremo il metodo MapPost per definire il metodo HTTP e la rotta per questo endpoint.

Aggiungi il seguente codice alla classe BookEndpoints:


// Endpoints/BookEndpoints.cs


//...
   // Endpoint per aggiungere un nuovo libro
      app.MapPost("/books", async (CreateBookRequest createBookRequest, IBookService bookService) =>
        {
        var result = await bookService.AddBookAsync(createBookRequest);
        return Results.Created($"/books/{result.Id}", result); 
        });


//...
  • Definizione della Rotta: Il metodo MapPost definisce la rotta per l’endpoint come /books.

  • Modello di Richiesta: L’endpoint accetta un oggetto CreateBookRequest come payload JSON. L’oggetto CreateBookRequest contiene i dati necessari per creare un nuovo libro.

  • Modello di Risposta: L’endpoint restituisce un oggetto Book come payload JSON. L’oggetto Book contiene i dati del libro appena creato.

  • Valore di Ritorno: L’endpoint restituisce un risultato Created. Il risultato Created contiene la posizione del libro appena creato e l’oggetto Book.

Come Creare l’Endpoint del Libro GetBookAsync

In questa sezione, creeremo l’endpoint GetBookAsync. Questo endpoint accetterà un ID libro come parametro di query e restituirà il libro con l’ID specificato. Utilizzeremo il metodo MapGet per definire il metodo HTTP e la rotta per questo endpoint.

Aggiungi il seguente codice alla classe BookEndpoints:


// Endpoints/BookEndpoints.cs

// ...
    // Endpoint per ottenere tutti i libri
    app.MapGet("/books", async (IBookService bookService) =>
     {
    var result = await bookService.GetBooksAsync();
    return Results.Ok(result);
});


//...
  • Definizione della Rotta: Il metodo MapGet definisce la rotta per l’endpoint come /books.

  • Modello di Richiesta: L’endpoint accetta un oggetto Book come payload JSON. L’oggetto Book contiene i dati necessari per creare un nuovo libro.

  • Modello di Risposta: L’endpoint restituisce un oggetto Book come payload JSON. L’oggetto Book contiene i dati del nuovo libro creato.

  • Valore di Ritorno: L’endpoint restituisce un risultato Ok. Il risultato Ok contiene l’oggetto Book.

How to Create the GetBookByIdAsync Book Endpoint

In questa sezione, creeremo l’endpoint GetBookByIdAsync. Questo endpoint accetterà un ID libro come parametro di route e restituirà il libro con l’ID specificato. Utilizzeremo il metodo MapGet per definire il metodo HTTP e la route per questo endpoint.

Aggiungi il seguente codice alla classe BookEndpoints:


// Endpoints/BookEndpoints.cs
//...
// Endpoint per ottenere un libro per 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();
});

//...
  • Definizione della Route: Il metodo MapGet definisce la route per l’endpoint come /books/{id:guid}. Il parametro {id:guid} specifica che il parametro id dovrebbe essere un GUID.

  • Modello di Richiesta: L’endpoint accetta un oggetto Book come payload JSON. L’oggetto Book contiene i dati necessari per creare un nuovo libro.

  • Modello di Risposta: L’endpoint restituisce un oggetto Book come payload JSON. L’oggetto Book contiene i dati per il nuovo libro creato.

  • Valore di Ritorno: L’endpoint restituisce un risultato Ok se il libro viene trovato. Il risultato NotFound viene restituito se il libro non viene trovato.

Come Creare l’Endpoint UpdateBookAsync per il Libro

In questa sezione, creeremo l’endpoint UpdateBookAsync. Questo endpoint accetterà un ID libro come parametro di percorso e un oggetto Book come payload JSON e aggiornerà il libro con l’ID specificato. Utilizzeremo il metodo MapPut per definire il metodo HTTP e il percorso per questo endpoint.

Aggiungi il seguente codice alla classe BookEndpoints:


// Endpoints/BookEndpoints.cs

//...
   // Endpoint per aggiornare un libro per 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();
});

//...
  • Definizione del Percorso: Il metodo MapPut definisce il percorso per l’endpoint come /books/{id:guid}. Il parametro {id:guid} specifica che il parametro id dovrebbe essere un GUID.

  • Modello di Richiesta: L’endpoint accetta un oggetto Book come payload JSON. L’oggetto Book contiene i dati necessari per creare un nuovo libro.

  • Modello di Risposta: L’endpoint restituisce un oggetto Book come payload JSON. L’oggetto Book contiene i dati del libro appena creato.

  • Valore di Ritorno: L’endpoint restituisce un risultato Ok se il libro viene trovato. Il risultato NotFound viene restituito se il libro non viene trovato.

Come Creare l’Endpoint DeleteBookAsync per il Libro

In questa sezione, creeremo l’endpoint DeleteBookAsync. Questo endpoint accetterà un ID libro come parametro di route e eliminerà il libro con l’ID specificato. Utilizzeremo il metodo MapDelete per definire il metodo HTTP e la route per questo endpoint.

Aggiungi il seguente codice alla classe BookEndpoints:


// Endpoints/BookEndpoints.cs

//...
   // Endpoint per eliminare un libro per ID
 app.MapDelete("/books/{id:guid}", async (Guid id, IBookService bookService) =>
{
var result = await bookService.DeleteBookAsync(id);
   return result ? Results.NoContent() : Results.NotFound();
});


//...
  • Definizione della Route: Il metodo MapDelete definisce la route per l’endpoint come /books/{id:guid}. Il parametro {id:guid} specifica che il parametro id dovrebbe essere un GUID.

  • Modello di Richiesta: L’endpoint accetta un oggetto Book come payload JSON. L’oggetto Book contiene i dati necessari per creare un nuovo libro.

  • Modello di Risposta: L’endpoint restituisce un oggetto Book come payload JSON. L’oggetto Book contiene i dati per il nuovo libro creato.

  • Valore di Ritorno: L’endpoint restituisce un risultato NoContent se il libro viene eliminato con successo. Il risultato NotFound viene restituito se il libro non viene trovato.

Ora abbiamo definito tutti i metodi per gli endpoint del libro. Quindi la tua classe di endpoint dovrebbe apparire così:

// 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)
        {
            // Definisci gli endpoint

            // Endpoint per aggiungere un nuovo libro
            app.MapPost("/books", async (CreateBookRequest createBookRequest, IBookService bookService) =>
            {
                var result = await bookService.AddBookAsync(createBookRequest);
                return Results.Created($"/books/{result.Id}", result); 
            });


               // Endpoint per ottenere tutti i libri
            app.MapGet("/books", async (IBookService bookService) =>
            {
                var result = await bookService.GetBooksAsync();
                return Results.Ok(result);
            });

            // Endpoint per ottenere un libro per 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();
            });


            // Endpoint per aggiornare un libro per 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();
            });

            // Endpoint per eliminare un libro per 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;
        }
    }
}

Complimenti! Hai creato tutti gli endpoint per l’API del libro. Gli endpoint gestiscono le operazioni CRUD per i libri e restituiscono le risposte appropriate in base alla richiesta e ai dati.

Come Registrare gli Endpoint

Dopo aver definito i punti finali dell’API per l’API del libro, il passo successivo è registrare questi punti finali nel file Program.cs. Utilizzeremo il metodo MapBookEndpoints per registrare i punti finali del libro.

Dovremmo anche pulire la nostra classe Program.cs per assicurarci che rimanga organizzata e mantenibile.

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


    // Imposta il percorso dei commenti per il JSON di Swagger e l'interfaccia utente.
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);

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

// Configura la pipeline delle richieste HTTP.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

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


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

app.Run();

Rompiamo i componenti chiave del file Program.cs:

  • AddApplicationServices: Questo metodo registra i servizi necessari per l’API. È un metodo di estensione che abbiamo creato in precedenza per aggiungere servizi al contenitore di iniezione delle dipendenze.

  • AddSwaggerGen: Questo metodo registra il generatore di Swagger, che viene utilizzato per creare la documentazione di Swagger per l’API. Specificiamo il titolo, la versione e la descrizione dell’API nel documento di Swagger.

  • MapGroup: Questo metodo raggruppa gli endpoint. Prende un percorso come parametro e restituisce un oggetto IEndpointRouteBuilder. Utilizziamo il metodo WithTags per aggiungere tag agli endpoint e il metodo MapBookEndpoints per registrare gli endpoint del libro.

  • Run: Questo metodo avvia l’applicazione.

Per abilitare la documentazione Swagger, è necessario aggiungere la proprietà GenerateDocumentationFile al file .csproj. In questo esempio, il file si chiama bookapi-minimal.csproj, ma il nome può variare in base al progetto.

Aggiungi la seguente riga al file .csproj:

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

Alla fine, bookapi-minimal.csproj dovrebbe apparire così:


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

Ora che abbiamo registrato gli endpoint del libro nel file Program.cs, possiamo eseguire l’applicazione e testare gli endpoint dell’API utilizzando Swagger.

Quando esegui l’applicazione, dovresti vedere la documentazione di Swagger all’URL seguente: https://localhost:5001/swagger/index.html. La documentazione di Swagger fornisce informazioni sugli endpoint dell’API, i modelli di richiesta e risposta e ti permette di testare gli endpoint direttamente dal browser. Dovresti vedere qualcosa del genere:

Congratulazioni! Hai implementato la logica di business per il servizio di libri, creato eccezioni personalizzate, definito gli endpoint dell’API e registrato gli endpoint nel file Program.cs. Hai anche abilitato la documentazione di Swagger per testare gli endpoint dell’API.

Come Aggiungere Dati di Seed al Database

Un altro passo importante è popolare il database con dati iniziali all’avvio dell’applicazione. Questi dati di seed popoleranno il database, permettendoti di testare gli endpoint della tua API senza aggiungere manualmente i dati.

Aggiungiamo alcuni dati di seed prima di eseguire le migrazioni e testare i nostri endpoint dell’API.

Per fare questo, creeremo una nuova classe nella nostra cartella Configuration chiamata BookTypeConfigurations e aggiungeremo il seguente codice:



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)
        {
            // Configura il nome della tabella
            builder.ToTable("Books");

            // Configura la chiave primaria
            builder.HasKey(x => x.Id);

            // Configura le proprietà
            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();

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

Analizziamo il codice sopra:

In Entity Framework Core, puoi utilizzare l’interfaccia IEntityTypeConfiguration per configurare il tipo di entità e i dati di inizializzazione per il database. La classe BookTypeConfigurations implementa l’interfaccia IEntityTypeConfiguration<BookModel> e fornisce la configurazione per l’entità BookModel.

  • Metodo Configure: Questo metodo viene utilizzato per configurare il tipo di entità BookModel. Definisce il nome della tabella, la chiave primaria e le proprietà per l’entità BookModel.

    • Nome della Tabella: Il metodo ToTable specifica il nome della tabella da creare nel database. In questo caso, il nome della tabella è impostato su “Books”.

    • Chiave Primaria: Il metodo HasKey specifica la chiave primaria per l’entità BookModel. La chiave primaria è impostata sulla proprietà Id.

    • Proprietà: Il metodo Property configura le proprietà dell’entità BookModel. Specifica il tipo di dati, la lunghezza e i vincoli per ogni proprietà.

  • Dati di Inizializzazione: Il metodo HasData inizializza il database con dati iniziali. Crea tre oggetti BookModel con dati di esempio per testare gli endpoint dell’API.

Ora che abbiamo creato la classe BookTypeConfigurations, dobbiamo registrare questa configurazione nella classe ApplicationContext. Questo garantisce che la configurazione venga applicata quando il database viene creato o migrato.

Finalmente siamo quasi pronti per testare la nostra API. Ma prima di farlo, dobbiamo eseguire le migrazioni per creare il database e applicare i dati iniziali.

Ricordate che abbiamo aggiunto la stringa di connessione del nostro database nel file appsettings.json? Ora eseguiamo una migrazione e in seguito aggiorniamo il nostro database affinché la migrazione abbia effetto.

Come Eseguire una Migrazione

Le migrazioni vi permettono di aggiornare lo schema del database in base alle modifiche apportate alle vostre classi modello. In Entity Framework Core, potete utilizzare il comando dotnet ef migrations add per creare una nuova migrazione che rifletta queste modifiche.

Per eseguire una migrazione, eseguite il seguente comando nella terminale:

dotnet ef migrations add InitialCreate

Se il comando ha successo, dovreste vedere un output simile a questo:

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

Ora vedrete una nuova cartella chiamata Migrations nel vostro progetto. Questa cartella contiene i file di migrazione creati in base alle modifiche apportate alle vostre classi modello. Questi file di migrazione includono i comandi SQL necessari per aggiornare lo schema del database.

Come Aggiornare il Database

Dopo aver creato la migrazione, dovete applicare la migrazione per aggiornare lo schema del database. Potete utilizzare il comando dotnet ef database update per applicare la migrazione e aggiornare il database. Assicuratevi che il SQL Server sia in esecuzione.

Eseguite il seguente comando nella terminale:


dotnet ef database update

Questo aggiornerà lo schema del database in base alle modifiche apportate alle vostre classi modello. Assicuratevi che non ci siano errori nella vostra stringa di connessione del database.

Come Testare gli Endpoint dell’API

Ora possiamo testare i nostri endpoint utilizzando Swagger. Per farlo, esegui l’applicazione digitando il seguente comando nel terminale:


dotnet run

Questo avvierà la nostra applicazione. Puoi aprire il tuo browser e navigare su https://localhost:5001/swagger/index.html per accedere alla documentazione di Swagger. Dovresti vedere un elenco di endpoint API, modelli di richiesta e risposta e la possibilità di testare gli endpoint direttamente dal browser.

Se il tuo numero di porta è diverso da 5001, non preoccuparti – funzionerà comunque. La porta potrebbe cambiare a seconda del tipo di macchina che stai utilizzando, ma otterrai comunque lo stesso risultato.

Come Testare l’Endpoint Get All Books

Per testare l’endpoint Get All Books, segui questi passaggi:

  1. Nella documentazione di Swagger, clicca sull’endpoint GET /api/v1/books.

  2. Clicca sul pulsante Provalo.

  3. Clicca sul pulsante Esegui.

Questo invierà una richiesta all’API per recuperare tutti i libri nel database.

Dovresti vedere la risposta dell’API, che includerà l’elenco dei libri che sono stati inseriti nel database.

L’immagine seguente mostra la risposta dell’API:

Come Testare l’Endpoint Get Book by ID

Per testare l’endpoint Get Book by ID, segui questi passaggi:

  1. Nella documentazione Swagger, clicca sull’endpoint GET /api/v1/books/{id}.

  2. Inserisci l’ID di un libro nel campo id. Puoi utilizzare uno degli ID dei libri che è stato inserito nel database.

  3. Clicca sul pulsante Prova.

Questo invierà una richiesta all’API per recuperare il libro con l’ID specificato. Dovresti vedere la risposta dell’API, che includerà il libro con l’ID specificato.

L’immagine seguente mostra la risposta dell’API:

Come Testare l’Endpoint Add Book

Per testare l’endpoint Add Book, segui questi passaggi:

  1. Nella documentazione Swagger, clicca sull’endpoint POST /api/v1/books.

  2. Clicca sul pulsante Prova.

  3. Inserisci i dettagli del libro nel corpo della richiesta.

  4. Fai clic sul pulsante Esegui.

Questo invierà una richiesta all’API per aggiungere un nuovo libro al database.

Dovresti vedere la risposta dall’API, che includerà il libro appena creato.

L’immagine seguente mostra la risposta dall’API:

Come Testare l’Endpoint Aggiorna Libro

Per testare l’endpoint Aggiorna Libro, segui questi passaggi:

  1. Nella documentazione di Swagger, fai clic sull’endpoint PUT /api/v1/books/{id}.

  2. Inserisci l’ID di un libro nel campo id. Puoi usare l’id di uno dei libri che abbiamo appena aggiunto.

  3. Fai clic sul pulsante Provalo.

Questo invierà una richiesta all’API per aggiornare il libro con l’ID specificato.

Dovresti vedere la risposta dall’API, che includerà il libro aggiornato.

L’immagine seguente mostra la risposta dall’API:

Come Testare l’Endpoint Elimina Libro

Per testare l’endpoint Elimina Libro, segui questi passaggi:

  1. Nella documentazione di Swagger, fai clic sull’endpoint DELETE /api/v1/books/{id}.

  2. Inserisci l’ID di un libro nel campo id. Puoi utilizzare uno qualsiasi degli ID dei libri che abbiamo appena aggiunto o dei dati preimpostati.

  3. Fai clic sul pulsante Provalo.

Questo invierà una richiesta all’API per eliminare il libro con l’ID specificato.

L’immagine qui sotto mostra la risposta dall’API:

Congratulazioni! Hai implementato tutte le operazioni CRUD per i libri e testato i punti terminali dell’API utilizzando Swagger, verificando che funzionino come previsto. Ora puoi ampliare questa base per aggiungere ulteriori funzionalità e caratteristiche alla tua API.

Conclusione

Questo manuale ha esplorato come creare un’API minima in ASP.NET Core con .NET 8. Abbiamo costruito un’API di libri completa che supporta le operazioni CRUD, implementato eccezioni personalizzate, definito e registrato i punti terminali dell’API e abilitato la documentazione Swagger per facilitare i test.

Seguendo questo tutorial, hai acquisito una solida base per la creazione di API minime con ASP.NET Core. Ora puoi applicare queste conoscenze e creare API robuste per vari settori e industrie.

Spero che tu abbia trovato questo tutorial utile e informativo. Grazie per aver letto!

Non esitare a connetterti con me sui social media: