Creare un'API Web con ASP.NET Core e MongoDB

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Di Pratik Khandelwal e Scott Addie

Questa esercitazione crea un'API Web che esegue operazioni Create, Read, Update e Delete (CRUD) in un database NoSQL mongoDB .

In questa esercitazione apprenderai a:

  • Configurare MongoDB
  • Creare un database MongoDB
  • Definire una raccolta e uno schema MongoDB
  • Eseguire operazioni CRUD di MongoDB da un'API Web
  • Personalizzare la JSserializzazione ON

Prerequisiti

Configurare MongoDB

Abilitare l'accesso alla shell MongoDB e MongoDB da qualsiasi punto del computer di sviluppo (Windows/Linux/macOS):

  1. Scaricare e installare MongoDB Shell:

    • macOS/Linux: scegliere una directory in cui estrarre la shell mongoDB. Aggiungere il percorso risultante per mongosh alla PATH variabile di ambiente.
    • Windows: MongoDB Shell (mongosh.exe) è installato in C:\Users<user>\AppData\Local\Programs\mongosh. Aggiungere il percorso risultante per mongosh.exe alla PATH variabile di ambiente.
  2. Scaricare e installare MongoDB:

    • macOS/Linux: verificare che la directory in cui MongoDB sia installato, in genere in /usr/local/mongodb. Aggiungere il percorso risultante per mongodb alla PATH variabile di ambiente.
    • Windows: MongoDB è installato in C:\Programmi\MongoDB per impostazione predefinita. Aggiungere C:\Programmi\MongoDB\Server\<version_number>\bin alla PATH variabile di ambiente.
  3. Scegliere una directory di Archiviazione dati: selezionare una directory nel computer di sviluppo per l'archiviazione dei dati. Creare la directory se non esiste. La shell mongoDB non crea nuove directory:

    • macOS/Linux: ad esempio. /usr/local/var/mongodb
    • Windows: ad esempio. C:\\BooksData
  4. Nella shell dei comandi del sistema operativo (non nella shell mongoDB) usare il comando seguente per connettersi a MongoDB sulla porta predefinita 27017. Sostituire <data_directory_path> con la directory scelta nel passaggio precedente.

    mongod --dbpath <data_directory_path>
    

Usare la shell MongoDB installata in precedenza nei passaggi seguenti per creare un database, creare raccolte e archiviare documenti. Per altre informazioni sui comandi della shell mongoDB, vedere mongosh.

  1. Aprire un'istanza della shell dei comandi mongoDB avviando mongosh.exe.

  2. Nella shell dei comandi connettersi al database di test predefinito eseguendo il comando seguente:

    mongosh
    
  3. Eseguire il comando seguente nella shell dei comandi:

    use BookStore
    

    Se non esiste già, viene creato un database denominato BookStore . Se il database esiste, la connessione viene aperta per le transazioni.

  4. Creare una raccolta Books tramite il comando seguente:

    db.createCollection('Books')
    

    Viene visualizzato il risultato seguente:

    { "ok" : 1 }
    
  5. Definire uno schema per la raccolta Books e inserire due documenti usando il comando seguente:

    db.Books.insertMany([{ "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Name": "Clean Code", "Price": 43.15, "Category": "Computers","Author": "Robert C. Martin" }])
    

    Viene visualizzato un risultato simile al seguente:

    {
        "acknowledged" : true,
        "insertedIds" : [
            ObjectId("61a6058e6c43f32854e51f51"),
            ObjectId("61a6058e6c43f32854e51f52")
         ]
     }
    

    Nota

    I ObjectIdvalori visualizzati nel risultato precedente non corrispondono a quelli visualizzati nella shell dei comandi.

  6. Visualizzare i documenti nel database usando il comando seguente:

    db.Books.find().pretty()
    

    Viene visualizzato un risultato simile al seguente:

    {
         "_id" : ObjectId("61a6058e6c43f32854e51f51"),
         "Name" : "Design Patterns",
         "Price" : 54.93,
         "Category" : "Computers",
         "Author" : "Ralph Johnson"
     }
     {
         "_id" : ObjectId("61a6058e6c43f32854e51f52"),
         "Name" : "Clean Code",
         "Price" : 43.15,
         "Category" : "Computers",
         "Author" : "Robert C. Martin"
     }
    

    Lo schema aggiunge una proprietà _id generata automaticamente di tipo ObjectId per ogni documento.

Creare il progetto per l'API Web ASP.NET Core

  1. Passare a File>Nuovo>Progetto.

  2. Selezionare il tipo di progetto API Web core ASP.NET e selezionare Avanti.

  3. Assegnare al progetto il nome BookStoreApi e selezionare Avanti.

  4. Selezionare il framework .NET 8.0 (supporto a lungo termine) e selezionare Crea.

  5. Nella finestra Console di Gestione pacchetti passare alla radice del progetto. Eseguire il comando seguente per installare il driver .NET per MongoDB:

    Install-Package MongoDB.Driver
    

Aggiungere un modello di entità

  1. Aggiungere una directory Models alla radice del progetto.

  2. Aggiungere una classe Book alla directory Models con il codice seguente:

    using MongoDB.Bson;
    using MongoDB.Bson.Serialization.Attributes;
    
    namespace BookStoreApi.Models;
    
    public class Book
    {
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public string? Id { get; set; }
    
        [BsonElement("Name")]
        public string BookName { get; set; } = null!;
    
        public decimal Price { get; set; }
    
        public string Category { get; set; } = null!;
    
        public string Author { get; set; } = null!;
    }
    

    Nella classe precedente la Id proprietà è:

    • Obbligatorio per eseguire il mapping dell'oggetto Common Language Runtime (CLR) all'insieme MongoDB.
    • Annotato con [BsonId] per impostare questa proprietà sulla chiave primaria del documento.
    • Annotato con [BsonRepresentation(BsonType.ObjectId)] per consentire il passaggio del parametro come tipo string invece di una struttura ObjectId . Mongo gestisce la conversione da string a ObjectId.

    La BookName proprietà viene annotata con l'attributo [BsonElement] . Il valore dell'attributo Name rappresenta il nome della proprietà nella raccolta MongoDB.

Aggiungere un modello di configurazione

  1. Aggiungere i valori di configurazione del database seguenti a appsettings.json:

    {
        "BookStoreDatabase": {
            "ConnectionString": "mongodb://localhost:27017",
            "DatabaseName": "BookStore",
            "BooksCollectionName": "Books"
        },
        "Logging": {
            "LogLevel": {
                "Default": "Information",
                "Microsoft.AspNetCore": "Warning"
            }
        },
        "AllowedHosts": "*"
    }
    
  2. Aggiungere una classe BookStoreDatabaseSettings alla directory Models con il codice seguente:

    namespace BookStoreApi.Models;
    
    public class BookStoreDatabaseSettings
    {
        public string ConnectionString { get; set; } = null!;
    
        public string DatabaseName { get; set; } = null!;
    
        public string BooksCollectionName { get; set; } = null!;
    }
    

    La classe precedente BookStoreDatabaseSettings viene usata per archiviare i appsettings.json valori delle proprietà del BookStoreDatabase file. I nomi delle JSproprietà ON e C# sono denominati in modo identico per semplificare il processo di mapping.

  3. Aggiungere il codice evidenziato seguente a Program.cs:

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    

    Nel codice precedente, l'istanza di configurazione a cui viene associata la appsettings.json sezione del BookStoreDatabase file viene registrata nel contenitore Inserimento dipendenze . Ad esempio, la BookStoreDatabaseSettings proprietà dell'oggetto ConnectionString viene popolata con la BookStoreDatabase:ConnectionString proprietà in appsettings.json.

  4. Aggiungere il codice seguente all'inizio di Program.cs per risolvere il BookStoreDatabaseSettings riferimento:

    using BookStoreApi.Models;
    

Aggiungere un servizio di operazioni CRUD

  1. Aggiungere una directory Services alla radice del progetto.

  2. Aggiungere una classe BooksService alla directory Services con il codice seguente:

    using BookStoreApi.Models;
    using Microsoft.Extensions.Options;
    using MongoDB.Driver;
    
    namespace BookStoreApi.Services;
    
    public class BooksService
    {
        private readonly IMongoCollection<Book> _booksCollection;
    
        public BooksService(
            IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
        {
            var mongoClient = new MongoClient(
                bookStoreDatabaseSettings.Value.ConnectionString);
    
            var mongoDatabase = mongoClient.GetDatabase(
                bookStoreDatabaseSettings.Value.DatabaseName);
    
            _booksCollection = mongoDatabase.GetCollection<Book>(
                bookStoreDatabaseSettings.Value.BooksCollectionName);
        }
    
        public async Task<List<Book>> GetAsync() =>
            await _booksCollection.Find(_ => true).ToListAsync();
    
        public async Task<Book?> GetAsync(string id) =>
            await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync();
    
        public async Task CreateAsync(Book newBook) =>
            await _booksCollection.InsertOneAsync(newBook);
    
        public async Task UpdateAsync(string id, Book updatedBook) =>
            await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook);
    
        public async Task RemoveAsync(string id) =>
            await _booksCollection.DeleteOneAsync(x => x.Id == id);
    }
    

    Nel codice precedente, un'istanza BookStoreDatabaseSettings viene recuperata dall'inserimento delle dipendenze tramite l'inserimento del costruttore. Questa tecnica consente di accedere ai valori di appsettings.json configurazione aggiunti nella sezione Aggiungere un modello di configurazione.

  3. Aggiungere il codice evidenziato seguente a Program.cs:

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    
    builder.Services.AddSingleton<BooksService>();
    

    Nel codice precedente la classe BooksService è registrata con l'inserimento di dipendenze per supportare l'inserimento del costruttore nelle classi che la utilizzano. La durata del servizio singleton è più appropriata perché BooksService assume una dipendenza diretta a MongoClient. In base alle linee guida per il riutilizzo di Mongo Client ufficiali, MongoClient deve essere registrato nell'inserimento di dipendenze con una durata del servizio singleton.

  4. Aggiungere il codice seguente all'inizio di Program.cs per risolvere il BooksService riferimento:

    using BookStoreApi.Services;
    

La BooksService classe usa i membri seguenti MongoDB.Driver per eseguire operazioni CRUD sul database:

  • MongoClient: legge l'istanza del server per l'esecuzione di operazioni di database. Il costruttore di questa classe viene fornito nel stringa di connessione MongoDB:

    public BooksService(
        IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
    {
        var mongoClient = new MongoClient(
            bookStoreDatabaseSettings.Value.ConnectionString);
    
        var mongoDatabase = mongoClient.GetDatabase(
            bookStoreDatabaseSettings.Value.DatabaseName);
    
        _booksCollection = mongoDatabase.GetCollection<Book>(
            bookStoreDatabaseSettings.Value.BooksCollectionName);
    }
    
  • IMongoDatabase: rappresenta il database Mongo per l'esecuzione delle operazioni. Questa esercitazione usa il metodo getCollection<TDocument>(collection) generico sull'interfaccia per ottenere l'accesso ai dati in una raccolta specifica. Eseguire operazioni CRUD sulla raccolta dopo la chiamata di questo metodo. Nella chiamata del metodo GetCollection<TDocument>(collection):

    • collection rappresenta il nome della raccolta.
    • TDocument rappresenta il tipo di oggetto CLR archiviato nella raccolta.

GetCollection<TDocument>(collection) restituisce un oggetto MongoCollection che rappresenta la raccolta. In questa esercitazione, vengono richiamati i metodi seguenti sulla raccolta:

  • DeleteOneAsync: elimina un singolo documento corrispondente ai criteri di ricerca specificati.
  • Trova<TDocument>: restituisce tutti i documenti nella raccolta che corrispondono ai criteri di ricerca specificati.
  • InsertOneAsync: inserisce l'oggetto fornito come nuovo documento nell'insieme.
  • ReplaceOneAsync: sostituisce il singolo documento corrispondente ai criteri di ricerca specificati con l'oggetto specificato.

Aggiungere un controller

Aggiungere una classe BooksController alla directory Controllers con il codice seguente:

using BookStoreApi.Models;
using BookStoreApi.Services;
using Microsoft.AspNetCore.Mvc;

namespace BookStoreApi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
    private readonly BooksService _booksService;

    public BooksController(BooksService booksService) =>
        _booksService = booksService;

    [HttpGet]
    public async Task<List<Book>> Get() =>
        await _booksService.GetAsync();

    [HttpGet("{id:length(24)}")]
    public async Task<ActionResult<Book>> Get(string id)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        return book;
    }

    [HttpPost]
    public async Task<IActionResult> Post(Book newBook)
    {
        await _booksService.CreateAsync(newBook);

        return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook);
    }

    [HttpPut("{id:length(24)}")]
    public async Task<IActionResult> Update(string id, Book updatedBook)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        updatedBook.Id = book.Id;

        await _booksService.UpdateAsync(id, updatedBook);

        return NoContent();
    }

    [HttpDelete("{id:length(24)}")]
    public async Task<IActionResult> Delete(string id)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        await _booksService.RemoveAsync(id);

        return NoContent();
    }
}

Il controller dell'API Web precedente:

  • Usa la BooksService classe per eseguire operazioni CRUD.
  • Contiene metodi di azione per supportare le richieste HTTP GET, POST, PUT e DELETE.
  • Chiama CreatedAtAction nel metodo dell'azione Create per restituire una risposta HTTP 201. Il codice di stato 201 è la risposta standard per un metodo HTTP POST che crea una nuova risorsa nel server. Location aggiunge anche un'intestazione CreatedAtAction alla risposta. L'intestazione Location specifica l'URI del libro appena creato.

Testare l'API Web

  1. Compilare ed eseguire l'app.

  2. Passare a https://localhost:<port>/api/books, dove <port> è il numero di porta assegnato automaticamente per l'app, per testare il metodo di azione senza Get parametri del controller, selezionare Prova esegui>. Viene visualizzata una JSrisposta ON simile alla seguente:

    [
      {
        "id": "61a6058e6c43f32854e51f51",
        "bookName": "Design Patterns",
        "price": 54.93,
        "category": "Computers",
        "author": "Ralph Johnson"
      },
      {
        "id": "61a6058e6c43f32854e51f52",
        "bookName": "Clean Code",
        "price": 43.15,
        "category": "Computers",
        "author": "Robert C. Martin"
      }
    ]
    
  3. Passare a https://localhost:<port>/api/books/{id here} per testare il metodo dell'azione Get in overload del controller. Viene visualizzata una JSrisposta ON simile alla seguente:

    {
      "id": "61a6058e6c43f32854e51f52",
      "bookName": "Clean Code",
      "price": 43.15,
      "category": "Computers",
      "author": "Robert C. Martin"
    }
    

Configurare le opzioni di JSserializzazione ON

Esistono due dettagli da modificare sulle JSrisposte ON restituite nella sezione Testare l'API Web:

  • La notazione a cammello predefinita per i nomi di proprietà deve essere modificata in modo da adottare la convenzione Pascal dei nomi di proprietà dell'oggetto CLR.
  • La proprietà bookName deve essere restituita come Name.

Per soddisfare i requisiti precedenti, apportare le modifiche seguenti:

  1. In Program.cs concatenare il codice evidenziato seguente alla chiamata del metodo AddControllers:

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    
    builder.Services.AddSingleton<BooksService>();
    
    builder.Services.AddControllers()
        .AddJsonOptions(
            options => options.JsonSerializerOptions.PropertyNamingPolicy = null);
    

    Con la modifica precedente, i nomi delle proprietà nella risposta ON serializzata JSdell'API Web corrispondono ai nomi delle proprietà corrispondenti nel tipo di oggetto CLR. Ad esempio, la Book proprietà della Author classe serializza come Author anziché author.

  2. In Models/Book.csannotare la BookName proprietà con l'attributo [JsonPropertyName] :

    [BsonElement("Name")]
    [JsonPropertyName("Name")]
    public string BookName { get; set; } = null!;
    

    Il [JsonPropertyName] valore dell'attributo di rappresenta il nome della Name proprietà nella risposta ON serializzata JSdell'API Web.

  3. Aggiungere il codice seguente all'inizio di Models/Book.cs per risolvere il riferimento all'attributo [JsonProperty] :

    using System.Text.Json.Serialization;
    
  4. Ripetere i passaggi definiti nella sezione Testare l'API Web. Si noti la differenza nei nomi delle JSproprietà ON.

Aggiungere il supporto per l'autenticazione a un'API Web

ASP.NET Core Identity aggiunge funzionalità di accesso dell'interfaccia utente alle app Web core ASP.NET. Per proteggere le API Web e i contratti a pagina singola, usare una delle opzioni seguenti:

Duende Identity Server è un framework OpenID Connessione e OAuth 2.0 per ASP.NET Core. Duende Identity Server abilita le funzionalità di sicurezza seguenti:

  • Autenticazione come servizio (AaaS)
  • Single Sign-On/off (SSO) su più tipi di applicazione
  • Controllo di accesso per le API
  • Gateway federativo

Importante

Duende Software potrebbe richiedere il pagamento di una tariffa di licenza per l'uso in produzione di Duende Identity Server. Per altre informazioni, vedere Eseguire la migrazione da ASP.NET Core 5.0 a 6.0.

Per altre informazioni, vedere la documentazione di Duende Server (sito Web duende Identity Software).

Risorse aggiuntive

Questa esercitazione crea un'API Web che esegue operazioni Create, Read, Update e Delete (CRUD) in un database NoSQL mongoDB .

In questa esercitazione apprenderai a:

  • Configurare MongoDB
  • Creare un database MongoDB
  • Definire una raccolta e uno schema MongoDB
  • Eseguire operazioni CRUD di MongoDB da un'API Web
  • Personalizzare la JSserializzazione ON

Prerequisiti

Configurare MongoDB

Abilitare l'accesso a MongoDB e Mongo DB Shell da qualsiasi punto del computer di sviluppo:

  1. In Windows MongoDB viene installato in C:\Programmi\MongoDB per impostazione predefinita. Aggiungere C:\Programmi\MongoDB\Server\<version_number>\bin alla PATH variabile di ambiente.

  2. Scaricare la shell mongoDB e scegliere una directory in cui estrarla. Aggiungere il percorso risultante per mongosh.exe alla PATH variabile di ambiente.

  3. Scegliere una directory nel computer di sviluppo per l'archiviazione dei dati. Ad esempio, C:\BooksData in Windows. Creare la directory se non esiste. La shell mongo non consente di creare nuove directory.

  4. Nella shell dei comandi del sistema operativo (non nella shell mongoDB) usare il comando seguente per connettersi a MongoDB sulla porta predefinita 27017. Sostituire <data_directory_path> con la directory scelta nel passaggio precedente.

    mongod --dbpath <data_directory_path>
    

Usare la shell MongoDB installata in precedenza nei passaggi seguenti per creare un database, creare raccolte e archiviare documenti. Per altre informazioni sui comandi della shell mongoDB, vedere mongosh.

  1. Aprire un'istanza della shell dei comandi mongoDB avviando mongosh.exe.

  2. Nella shell dei comandi connettersi al database di test predefinito eseguendo il comando seguente:

    mongosh
    
  3. Eseguire il comando seguente nella shell dei comandi:

    use BookStore
    

    Se non esiste già, viene creato un database denominato BookStore . Se il database esiste, la connessione viene aperta per le transazioni.

  4. Creare una raccolta Books tramite il comando seguente:

    db.createCollection('Books')
    

    Viene visualizzato il risultato seguente:

    { "ok" : 1 }
    
  5. Definire uno schema per la raccolta Books e inserire due documenti usando il comando seguente:

    db.Books.insertMany([{ "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Name": "Clean Code", "Price": 43.15, "Category": "Computers","Author": "Robert C. Martin" }])
    

    Viene visualizzato un risultato simile al seguente:

    {
        "acknowledged" : true,
        "insertedIds" : [
            ObjectId("61a6058e6c43f32854e51f51"),
            ObjectId("61a6058e6c43f32854e51f52")
         ]
     }
    

    Nota

    I ObjectIdvalori visualizzati nel risultato precedente non corrispondono a quelli visualizzati nella shell dei comandi.

  6. Visualizzare i documenti nel database usando il comando seguente:

    db.Books.find().pretty()
    

    Viene visualizzato un risultato simile al seguente:

    {
         "_id" : ObjectId("61a6058e6c43f32854e51f51"),
         "Name" : "Design Patterns",
         "Price" : 54.93,
         "Category" : "Computers",
         "Author" : "Ralph Johnson"
     }
     {
         "_id" : ObjectId("61a6058e6c43f32854e51f52"),
         "Name" : "Clean Code",
         "Price" : 43.15,
         "Category" : "Computers",
         "Author" : "Robert C. Martin"
     }
    

    Lo schema aggiunge una proprietà _id generata automaticamente di tipo ObjectId per ogni documento.

Creare il progetto per l'API Web ASP.NET Core

  1. Passare a File>Nuovo>Progetto.

  2. Selezionare il tipo di progetto API Web core ASP.NET e selezionare Avanti.

  3. Assegnare al progetto il nome BookStoreApi e selezionare Avanti.

  4. Selezionare il framework .NET 7.0 (Supporto termini standard) e selezionare Crea.

  5. Nel menu Strumenti selezionare Gestione pacchetti NuGet>Console di Gestione pacchetti.

  6. Nella finestra Console di Gestione pacchetti passare alla radice del progetto. Eseguire il comando seguente per installare il driver .NET per MongoDB:

    Install-Package MongoDB.Driver
    

Aggiungere un modello di entità

  1. Aggiungere una directory Models alla radice del progetto.

  2. Aggiungere una classe Book alla directory Models con il codice seguente:

    using MongoDB.Bson;
    using MongoDB.Bson.Serialization.Attributes;
    
    namespace BookStoreApi.Models;
    
    public class Book
    {
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public string? Id { get; set; }
    
        [BsonElement("Name")]
        public string BookName { get; set; } = null!;
    
        public decimal Price { get; set; }
    
        public string Category { get; set; } = null!;
    
        public string Author { get; set; } = null!;
    }
    

    Nella classe precedente la Id proprietà è:

    • Obbligatorio per eseguire il mapping dell'oggetto Common Language Runtime (CLR) all'insieme MongoDB.
    • Annotato con [BsonId] per impostare questa proprietà sulla chiave primaria del documento.
    • Annotato con [BsonRepresentation(BsonType.ObjectId)] per consentire il passaggio del parametro come tipo string invece di una struttura ObjectId . Mongo gestisce la conversione da string a ObjectId.

    La BookName proprietà viene annotata con l'attributo [BsonElement] . Il valore dell'attributo Name rappresenta il nome della proprietà nella raccolta MongoDB.

Aggiungere un modello di configurazione

  1. Aggiungere i valori di configurazione del database seguenti a appsettings.json:

    {
      "BookStoreDatabase": {
        "ConnectionString": "mongodb://localhost:27017",
        "DatabaseName": "BookStore",
        "BooksCollectionName": "Books"
      },
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*"
    }
    
  2. Aggiungere una classe BookStoreDatabaseSettings alla directory Models con il codice seguente:

    namespace BookStoreApi.Models;
    
    public class BookStoreDatabaseSettings
    {
        public string ConnectionString { get; set; } = null!;
    
        public string DatabaseName { get; set; } = null!;
    
        public string BooksCollectionName { get; set; } = null!;
    }
    

    La classe precedente BookStoreDatabaseSettings viene usata per archiviare i appsettings.json valori delle proprietà del BookStoreDatabase file. I nomi delle JSproprietà ON e C# sono denominati in modo identico per semplificare il processo di mapping.

  3. Aggiungere il codice evidenziato seguente a Program.cs:

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    

    Nel codice precedente, l'istanza di configurazione a cui viene associata la appsettings.json sezione del BookStoreDatabase file viene registrata nel contenitore Inserimento dipendenze . Ad esempio, la BookStoreDatabaseSettings proprietà dell'oggetto ConnectionString viene popolata con la BookStoreDatabase:ConnectionString proprietà in appsettings.json.

  4. Aggiungere il codice seguente all'inizio di Program.cs per risolvere il BookStoreDatabaseSettings riferimento:

    using BookStoreApi.Models;
    

Aggiungere un servizio di operazioni CRUD

  1. Aggiungere una directory Services alla radice del progetto.

  2. Aggiungere una classe BooksService alla directory Services con il codice seguente:

    using BookStoreApi.Models;
    using Microsoft.Extensions.Options;
    using MongoDB.Driver;
    
    namespace BookStoreApi.Services;
    
    public class BooksService
    {
        private readonly IMongoCollection<Book> _booksCollection;
    
        public BooksService(
            IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
        {
            var mongoClient = new MongoClient(
                bookStoreDatabaseSettings.Value.ConnectionString);
    
            var mongoDatabase = mongoClient.GetDatabase(
                bookStoreDatabaseSettings.Value.DatabaseName);
    
            _booksCollection = mongoDatabase.GetCollection<Book>(
                bookStoreDatabaseSettings.Value.BooksCollectionName);
        }
    
        public async Task<List<Book>> GetAsync() =>
            await _booksCollection.Find(_ => true).ToListAsync();
    
        public async Task<Book?> GetAsync(string id) =>
            await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync();
    
        public async Task CreateAsync(Book newBook) =>
            await _booksCollection.InsertOneAsync(newBook);
    
        public async Task UpdateAsync(string id, Book updatedBook) =>
            await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook);
    
        public async Task RemoveAsync(string id) =>
            await _booksCollection.DeleteOneAsync(x => x.Id == id);
    }
    

    Nel codice precedente, un'istanza BookStoreDatabaseSettings viene recuperata dall'inserimento delle dipendenze tramite l'inserimento del costruttore. Questa tecnica consente di accedere ai valori di appsettings.json configurazione aggiunti nella sezione Aggiungere un modello di configurazione.

  3. Aggiungere il codice evidenziato seguente a Program.cs:

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    
    builder.Services.AddSingleton<BooksService>();
    

    Nel codice precedente la classe BooksService è registrata con l'inserimento di dipendenze per supportare l'inserimento del costruttore nelle classi che la utilizzano. La durata del servizio singleton è più appropriata perché BooksService assume una dipendenza diretta a MongoClient. In base alle linee guida per il riutilizzo di Mongo Client ufficiali, MongoClient deve essere registrato nell'inserimento di dipendenze con una durata del servizio singleton.

  4. Aggiungere il codice seguente all'inizio di Program.cs per risolvere il BooksService riferimento:

    using BookStoreApi.Services;
    

La BooksService classe usa i membri seguenti MongoDB.Driver per eseguire operazioni CRUD sul database:

  • MongoClient: legge l'istanza del server per l'esecuzione di operazioni di database. Al costruttore di questa classe viene passata la stringa di connessione MongoDB:

    public BooksService(
        IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
    {
        var mongoClient = new MongoClient(
            bookStoreDatabaseSettings.Value.ConnectionString);
    
        var mongoDatabase = mongoClient.GetDatabase(
            bookStoreDatabaseSettings.Value.DatabaseName);
    
        _booksCollection = mongoDatabase.GetCollection<Book>(
            bookStoreDatabaseSettings.Value.BooksCollectionName);
    }
    
  • IMongoDatabase: rappresenta il database Mongo per l'esecuzione delle operazioni. Questa esercitazione usa il metodo getCollection<TDocument>(collection) generico sull'interfaccia per ottenere l'accesso ai dati in una raccolta specifica. Eseguire operazioni CRUD sulla raccolta dopo la chiamata di questo metodo. Nella chiamata del metodo GetCollection<TDocument>(collection):

    • collection rappresenta il nome della raccolta.
    • TDocument rappresenta il tipo di oggetto CLR archiviato nella raccolta.

GetCollection<TDocument>(collection) restituisce un oggetto MongoCollection che rappresenta la raccolta. In questa esercitazione, vengono richiamati i metodi seguenti sulla raccolta:

  • DeleteOneAsync: elimina un singolo documento corrispondente ai criteri di ricerca specificati.
  • Trova<TDocument>: restituisce tutti i documenti nella raccolta che corrispondono ai criteri di ricerca specificati.
  • InsertOneAsync: inserisce l'oggetto fornito come nuovo documento nell'insieme.
  • ReplaceOneAsync: sostituisce il singolo documento corrispondente ai criteri di ricerca specificati con l'oggetto specificato.

Aggiungere un controller

Aggiungere una classe BooksController alla directory Controllers con il codice seguente:

using BookStoreApi.Models;
using BookStoreApi.Services;
using Microsoft.AspNetCore.Mvc;

namespace BookStoreApi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
    private readonly BooksService _booksService;

    public BooksController(BooksService booksService) =>
        _booksService = booksService;

    [HttpGet]
    public async Task<List<Book>> Get() =>
        await _booksService.GetAsync();

    [HttpGet("{id:length(24)}")]
    public async Task<ActionResult<Book>> Get(string id)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        return book;
    }

    [HttpPost]
    public async Task<IActionResult> Post(Book newBook)
    {
        await _booksService.CreateAsync(newBook);

        return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook);
    }

    [HttpPut("{id:length(24)}")]
    public async Task<IActionResult> Update(string id, Book updatedBook)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        updatedBook.Id = book.Id;

        await _booksService.UpdateAsync(id, updatedBook);

        return NoContent();
    }

    [HttpDelete("{id:length(24)}")]
    public async Task<IActionResult> Delete(string id)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        await _booksService.RemoveAsync(id);

        return NoContent();
    }
}

Il controller dell'API Web precedente:

  • Usa la BooksService classe per eseguire operazioni CRUD.
  • Contiene metodi di azione per supportare le richieste HTTP GET, POST, PUT e DELETE.
  • Chiama CreatedAtAction nel metodo dell'azione Create per restituire una risposta HTTP 201. Il codice di stato 201 è la risposta standard per un metodo HTTP POST che crea una nuova risorsa nel server. Location aggiunge anche un'intestazione CreatedAtAction alla risposta. L'intestazione Location specifica l'URI del libro appena creato.

Testare l'API Web

  1. Compilare ed eseguire l'app.

  2. Passare a https://localhost:<port>/api/books, dove <port> è il numero di porta assegnato automaticamente per l'app, per testare il metodo di azione senza Get parametri del controller. Viene visualizzata una JSrisposta ON simile alla seguente:

    [
      {
        "id": "61a6058e6c43f32854e51f51",
        "bookName": "Design Patterns",
        "price": 54.93,
        "category": "Computers",
        "author": "Ralph Johnson"
      },
      {
        "id": "61a6058e6c43f32854e51f52",
        "bookName": "Clean Code",
        "price": 43.15,
        "category": "Computers",
        "author": "Robert C. Martin"
      }
    ]
    
  3. Passare a https://localhost:<port>/api/books/{id here} per testare il metodo dell'azione Get in overload del controller. Viene visualizzata una JSrisposta ON simile alla seguente:

    {
      "id": "61a6058e6c43f32854e51f52",
      "bookName": "Clean Code",
      "price": 43.15,
      "category": "Computers",
      "author": "Robert C. Martin"
    }
    

Configurare le opzioni di JSserializzazione ON

Esistono due dettagli da modificare sulle JSrisposte ON restituite nella sezione Testare l'API Web:

  • La notazione a cammello predefinita per i nomi di proprietà deve essere modificata in modo da adottare la convenzione Pascal dei nomi di proprietà dell'oggetto CLR.
  • La proprietà bookName deve essere restituita come Name.

Per soddisfare i requisiti precedenti, apportare le modifiche seguenti:

  1. In Program.cs concatenare il codice evidenziato seguente alla chiamata del metodo AddControllers:

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    
    builder.Services.AddSingleton<BooksService>();
    
    builder.Services.AddControllers()
        .AddJsonOptions(
            options => options.JsonSerializerOptions.PropertyNamingPolicy = null);
    

    Con la modifica precedente, i nomi delle proprietà nella risposta ON serializzata JSdell'API Web corrispondono ai nomi delle proprietà corrispondenti nel tipo di oggetto CLR. Ad esempio, la Book proprietà della Author classe serializza come Author anziché author.

  2. In Models/Book.csannotare la BookName proprietà con l'attributo [JsonPropertyName] :

    [BsonElement("Name")]
    [JsonPropertyName("Name")]
    public string BookName { get; set; } = null!;
    

    Il [JsonPropertyName] valore dell'attributo di rappresenta il nome della Name proprietà nella risposta ON serializzata JSdell'API Web.

  3. Aggiungere il codice seguente all'inizio di Models/Book.cs per risolvere il riferimento all'attributo [JsonProperty] :

    using System.Text.Json.Serialization;
    
  4. Ripetere i passaggi definiti nella sezione Testare l'API Web. Si noti la differenza nei nomi delle JSproprietà ON.

Aggiungere il supporto per l'autenticazione a un'API Web

ASP.NET Core Identity aggiunge funzionalità di accesso dell'interfaccia utente alle app Web core ASP.NET. Per proteggere le API Web e i contratti a pagina singola, usare una delle opzioni seguenti:

Duende Identity Server è un framework OpenID Connessione e OAuth 2.0 per ASP.NET Core. Duende Identity Server abilita le funzionalità di sicurezza seguenti:

  • Autenticazione come servizio (AaaS)
  • Single Sign-On/off (SSO) su più tipi di applicazione
  • Controllo di accesso per le API
  • Gateway federativo

Importante

Duende Software potrebbe richiedere il pagamento di una tariffa di licenza per l'uso in produzione di Duende Identity Server. Per altre informazioni, vedere Eseguire la migrazione da ASP.NET Core 5.0 a 6.0.

Per altre informazioni, vedere la documentazione di Duende Server (sito Web duende Identity Software).

Risorse aggiuntive

Questa esercitazione crea un'API Web che esegue operazioni Create, Read, Update e Delete (CRUD) in un database NoSQL mongoDB .

In questa esercitazione apprenderai a:

  • Configurare MongoDB
  • Creare un database MongoDB
  • Definire una raccolta e uno schema MongoDB
  • Eseguire operazioni CRUD di MongoDB da un'API Web
  • Personalizzare la JSserializzazione ON

Prerequisiti

Configurare MongoDB

Abilitare l'accesso a MongoDB e Mongo DB Shell da qualsiasi punto del computer di sviluppo:

  1. In Windows MongoDB viene installato in C:\Programmi\MongoDB per impostazione predefinita. Aggiungere C:\Programmi\MongoDB\Server\<version_number>\bin alla PATH variabile di ambiente.

  2. Scaricare la shell mongoDB e scegliere una directory in cui estrarla. Aggiungere il percorso risultante per mongosh.exe alla PATH variabile di ambiente.

  3. Scegliere una directory nel computer di sviluppo per l'archiviazione dei dati. Ad esempio, C:\BooksData in Windows. Creare la directory se non esiste. La shell mongo non consente di creare nuove directory.

  4. Nella shell dei comandi del sistema operativo (non nella shell mongoDB) usare il comando seguente per connettersi a MongoDB sulla porta predefinita 27017. Sostituire <data_directory_path> con la directory scelta nel passaggio precedente.

    mongod --dbpath <data_directory_path>
    

Usare la shell MongoDB installata in precedenza nei passaggi seguenti per creare un database, creare raccolte e archiviare documenti. Per altre informazioni sui comandi della shell mongoDB, vedere mongosh.

  1. Aprire un'istanza della shell dei comandi mongoDB avviando mongosh.exe.

  2. Nella shell dei comandi connettersi al database di test predefinito eseguendo il comando seguente:

    mongosh
    
  3. Eseguire il comando seguente nella shell dei comandi:

    use BookStore
    

    Se non esiste già, viene creato un database denominato BookStore . Se il database esiste, la connessione viene aperta per le transazioni.

  4. Creare una raccolta Books tramite il comando seguente:

    db.createCollection('Books')
    

    Viene visualizzato il risultato seguente:

    { "ok" : 1 }
    
  5. Definire uno schema per la raccolta Books e inserire due documenti usando il comando seguente:

    db.Books.insertMany([{ "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Name": "Clean Code", "Price": 43.15, "Category": "Computers","Author": "Robert C. Martin" }])
    

    Viene visualizzato un risultato simile al seguente:

    {
        "acknowledged" : true,
        "insertedIds" : [
            ObjectId("61a6058e6c43f32854e51f51"),
            ObjectId("61a6058e6c43f32854e51f52")
         ]
     }
    

    Nota

    I ObjectIdvalori visualizzati nel risultato precedente non corrispondono a quelli visualizzati nella shell dei comandi.

  6. Visualizzare i documenti nel database usando il comando seguente:

    db.Books.find().pretty()
    

    Viene visualizzato un risultato simile al seguente:

    {
         "_id" : ObjectId("61a6058e6c43f32854e51f51"),
         "Name" : "Design Patterns",
         "Price" : 54.93,
         "Category" : "Computers",
         "Author" : "Ralph Johnson"
     }
     {
         "_id" : ObjectId("61a6058e6c43f32854e51f52"),
         "Name" : "Clean Code",
         "Price" : 43.15,
         "Category" : "Computers",
         "Author" : "Robert C. Martin"
     }
    

    Lo schema aggiunge una proprietà _id generata automaticamente di tipo ObjectId per ogni documento.

Creare il progetto per l'API Web ASP.NET Core

  1. Passare a File>Nuovo>Progetto.

  2. Selezionare il tipo di progetto API Web core ASP.NET e selezionare Avanti.

  3. Assegnare al progetto il nome BookStoreApi e selezionare Avanti.

  4. Selezionare il framework .NET 6.0 (supporto a lungo termine) e selezionare Crea.

  5. Nella finestra Console di Gestione pacchetti passare alla radice del progetto. Eseguire il comando seguente per installare il driver .NET per MongoDB:

    Install-Package MongoDB.Driver
    

Aggiungere un modello di entità

  1. Aggiungere una directory Models alla radice del progetto.

  2. Aggiungere una classe Book alla directory Models con il codice seguente:

    using MongoDB.Bson;
    using MongoDB.Bson.Serialization.Attributes;
    
    namespace BookStoreApi.Models;
    
    public class Book
    {
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public string? Id { get; set; }
    
        [BsonElement("Name")]
        public string BookName { get; set; } = null!;
    
        public decimal Price { get; set; }
    
        public string Category { get; set; } = null!;
    
        public string Author { get; set; } = null!;
    }
    

    Nella classe precedente la Id proprietà è:

    • Obbligatorio per eseguire il mapping dell'oggetto Common Language Runtime (CLR) all'insieme MongoDB.
    • Annotato con [BsonId] per impostare questa proprietà sulla chiave primaria del documento.
    • Annotato con [BsonRepresentation(BsonType.ObjectId)] per consentire il passaggio del parametro come tipo string invece di una struttura ObjectId . Mongo gestisce la conversione da string a ObjectId.

    La BookName proprietà viene annotata con l'attributo [BsonElement] . Il valore dell'attributo Name rappresenta il nome della proprietà nella raccolta MongoDB.

Aggiungere un modello di configurazione

  1. Aggiungere i valori di configurazione del database seguenti a appsettings.json:

    {
        "BookStoreDatabase": {
            "ConnectionString": "mongodb://localhost:27017",
            "DatabaseName": "BookStore",
            "BooksCollectionName": "Books"
        },
        "Logging": {
            "LogLevel": {
                "Default": "Information",
                "Microsoft.AspNetCore": "Warning"
            }
        },
        "AllowedHosts": "*"
    }
    
  2. Aggiungere una classe BookStoreDatabaseSettings alla directory Models con il codice seguente:

    namespace BookStoreApi.Models;
    
    public class BookStoreDatabaseSettings
    {
        public string ConnectionString { get; set; } = null!;
    
        public string DatabaseName { get; set; } = null!;
    
        public string BooksCollectionName { get; set; } = null!;
    }
    

    La classe precedente BookStoreDatabaseSettings viene usata per archiviare i appsettings.json valori delle proprietà del BookStoreDatabase file. I nomi delle JSproprietà ON e C# sono denominati in modo identico per semplificare il processo di mapping.

  3. Aggiungere il codice evidenziato seguente a Program.cs:

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    

    Nel codice precedente, l'istanza di configurazione a cui viene associata la appsettings.json sezione del BookStoreDatabase file viene registrata nel contenitore Inserimento dipendenze . Ad esempio, la BookStoreDatabaseSettings proprietà dell'oggetto ConnectionString viene popolata con la BookStoreDatabase:ConnectionString proprietà in appsettings.json.

  4. Aggiungere il codice seguente all'inizio di Program.cs per risolvere il BookStoreDatabaseSettings riferimento:

    using BookStoreApi.Models;
    

Aggiungere un servizio di operazioni CRUD

  1. Aggiungere una directory Services alla radice del progetto.

  2. Aggiungere una classe BooksService alla directory Services con il codice seguente:

    using BookStoreApi.Models;
    using Microsoft.Extensions.Options;
    using MongoDB.Driver;
    
    namespace BookStoreApi.Services;
    
    public class BooksService
    {
        private readonly IMongoCollection<Book> _booksCollection;
    
        public BooksService(
            IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
        {
            var mongoClient = new MongoClient(
                bookStoreDatabaseSettings.Value.ConnectionString);
    
            var mongoDatabase = mongoClient.GetDatabase(
                bookStoreDatabaseSettings.Value.DatabaseName);
    
            _booksCollection = mongoDatabase.GetCollection<Book>(
                bookStoreDatabaseSettings.Value.BooksCollectionName);
        }
    
        public async Task<List<Book>> GetAsync() =>
            await _booksCollection.Find(_ => true).ToListAsync();
    
        public async Task<Book?> GetAsync(string id) =>
            await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync();
    
        public async Task CreateAsync(Book newBook) =>
            await _booksCollection.InsertOneAsync(newBook);
    
        public async Task UpdateAsync(string id, Book updatedBook) =>
            await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook);
    
        public async Task RemoveAsync(string id) =>
            await _booksCollection.DeleteOneAsync(x => x.Id == id);
    }
    

    Nel codice precedente, un'istanza BookStoreDatabaseSettings viene recuperata dall'inserimento delle dipendenze tramite l'inserimento del costruttore. Questa tecnica consente di accedere ai valori di appsettings.json configurazione aggiunti nella sezione Aggiungere un modello di configurazione.

  3. Aggiungere il codice evidenziato seguente a Program.cs:

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    
    builder.Services.AddSingleton<BooksService>();
    

    Nel codice precedente la classe BooksService è registrata con l'inserimento di dipendenze per supportare l'inserimento del costruttore nelle classi che la utilizzano. La durata del servizio singleton è più appropriata perché BooksService assume una dipendenza diretta a MongoClient. In base alle linee guida per il riutilizzo di Mongo Client ufficiali, MongoClient deve essere registrato nell'inserimento di dipendenze con una durata del servizio singleton.

  4. Aggiungere il codice seguente all'inizio di Program.cs per risolvere il BooksService riferimento:

    using BookStoreApi.Services;
    

La BooksService classe usa i membri seguenti MongoDB.Driver per eseguire operazioni CRUD sul database:

  • MongoClient: legge l'istanza del server per l'esecuzione di operazioni di database. Al costruttore di questa classe viene passata la stringa di connessione MongoDB:

    public BooksService(
        IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
    {
        var mongoClient = new MongoClient(
            bookStoreDatabaseSettings.Value.ConnectionString);
    
        var mongoDatabase = mongoClient.GetDatabase(
            bookStoreDatabaseSettings.Value.DatabaseName);
    
        _booksCollection = mongoDatabase.GetCollection<Book>(
            bookStoreDatabaseSettings.Value.BooksCollectionName);
    }
    
  • IMongoDatabase: rappresenta il database Mongo per l'esecuzione delle operazioni. Questa esercitazione usa il metodo getCollection<TDocument>(collection) generico sull'interfaccia per ottenere l'accesso ai dati in una raccolta specifica. Eseguire operazioni CRUD sulla raccolta dopo la chiamata di questo metodo. Nella chiamata del metodo GetCollection<TDocument>(collection):

    • collection rappresenta il nome della raccolta.
    • TDocument rappresenta il tipo di oggetto CLR archiviato nella raccolta.

GetCollection<TDocument>(collection) restituisce un oggetto MongoCollection che rappresenta la raccolta. In questa esercitazione, vengono richiamati i metodi seguenti sulla raccolta:

  • DeleteOneAsync: elimina un singolo documento corrispondente ai criteri di ricerca specificati.
  • Trova<TDocument>: restituisce tutti i documenti nella raccolta che corrispondono ai criteri di ricerca specificati.
  • InsertOneAsync: inserisce l'oggetto fornito come nuovo documento nell'insieme.
  • ReplaceOneAsync: sostituisce il singolo documento corrispondente ai criteri di ricerca specificati con l'oggetto specificato.

Aggiungere un controller

Aggiungere una classe BooksController alla directory Controllers con il codice seguente:

using BookStoreApi.Models;
using BookStoreApi.Services;
using Microsoft.AspNetCore.Mvc;

namespace BookStoreApi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
    private readonly BooksService _booksService;

    public BooksController(BooksService booksService) =>
        _booksService = booksService;

    [HttpGet]
    public async Task<List<Book>> Get() =>
        await _booksService.GetAsync();

    [HttpGet("{id:length(24)}")]
    public async Task<ActionResult<Book>> Get(string id)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        return book;
    }

    [HttpPost]
    public async Task<IActionResult> Post(Book newBook)
    {
        await _booksService.CreateAsync(newBook);

        return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook);
    }

    [HttpPut("{id:length(24)}")]
    public async Task<IActionResult> Update(string id, Book updatedBook)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        updatedBook.Id = book.Id;

        await _booksService.UpdateAsync(id, updatedBook);

        return NoContent();
    }

    [HttpDelete("{id:length(24)}")]
    public async Task<IActionResult> Delete(string id)
    {
        var book = await _booksService.GetAsync(id);

        if (book is null)
        {
            return NotFound();
        }

        await _booksService.RemoveAsync(id);

        return NoContent();
    }
}

Il controller dell'API Web precedente:

  • Usa la BooksService classe per eseguire operazioni CRUD.
  • Contiene metodi di azione per supportare le richieste HTTP GET, POST, PUT e DELETE.
  • Chiama CreatedAtAction nel metodo dell'azione Create per restituire una risposta HTTP 201. Il codice di stato 201 è la risposta standard per un metodo HTTP POST che crea una nuova risorsa nel server. Location aggiunge anche un'intestazione CreatedAtAction alla risposta. L'intestazione Location specifica l'URI del libro appena creato.

Testare l'API Web

  1. Compilare ed eseguire l'app.

  2. Passare a https://localhost:<port>/api/books, dove <port> è il numero di porta assegnato automaticamente per l'app, per testare il metodo di azione senza Get parametri del controller. Viene visualizzata una JSrisposta ON simile alla seguente:

    [
      {
        "id": "61a6058e6c43f32854e51f51",
        "bookName": "Design Patterns",
        "price": 54.93,
        "category": "Computers",
        "author": "Ralph Johnson"
      },
      {
        "id": "61a6058e6c43f32854e51f52",
        "bookName": "Clean Code",
        "price": 43.15,
        "category": "Computers",
        "author": "Robert C. Martin"
      }
    ]
    
  3. Passare a https://localhost:<port>/api/books/{id here} per testare il metodo dell'azione Get in overload del controller. Viene visualizzata una JSrisposta ON simile alla seguente:

    {
      "id": "61a6058e6c43f32854e51f52",
      "bookName": "Clean Code",
      "price": 43.15,
      "category": "Computers",
      "author": "Robert C. Martin"
    }
    

Configurare le opzioni di JSserializzazione ON

Esistono due dettagli da modificare sulle JSrisposte ON restituite nella sezione Testare l'API Web:

  • La notazione a cammello predefinita per i nomi di proprietà deve essere modificata in modo da adottare la convenzione Pascal dei nomi di proprietà dell'oggetto CLR.
  • La proprietà bookName deve essere restituita come Name.

Per soddisfare i requisiti precedenti, apportare le modifiche seguenti:

  1. In Program.cs concatenare il codice evidenziato seguente alla chiamata del metodo AddControllers:

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.Configure<BookStoreDatabaseSettings>(
        builder.Configuration.GetSection("BookStoreDatabase"));
    
    builder.Services.AddSingleton<BooksService>();
    
    builder.Services.AddControllers()
        .AddJsonOptions(
            options => options.JsonSerializerOptions.PropertyNamingPolicy = null);
    

    Con la modifica precedente, i nomi delle proprietà nella risposta ON serializzata JSdell'API Web corrispondono ai nomi delle proprietà corrispondenti nel tipo di oggetto CLR. Ad esempio, la Book proprietà della Author classe serializza come Author anziché author.

  2. In Models/Book.csannotare la BookName proprietà con l'attributo [JsonPropertyName] :

    [BsonElement("Name")]
    [JsonPropertyName("Name")]
    public string BookName { get; set; } = null!;
    

    Il [JsonPropertyName] valore dell'attributo di rappresenta il nome della Name proprietà nella risposta ON serializzata JSdell'API Web.

  3. Aggiungere il codice seguente all'inizio di Models/Book.cs per risolvere il riferimento all'attributo [JsonProperty] :

    using System.Text.Json.Serialization;
    
  4. Ripetere i passaggi definiti nella sezione Testare l'API Web. Si noti la differenza nei nomi delle JSproprietà ON.

Aggiungere il supporto per l'autenticazione a un'API Web

ASP.NET Core Identity aggiunge funzionalità di accesso dell'interfaccia utente alle app Web core ASP.NET. Per proteggere le API Web e i contratti a pagina singola, usare una delle opzioni seguenti:

Duende Identity Server è un framework OpenID Connessione e OAuth 2.0 per ASP.NET Core. Duende Identity Server abilita le funzionalità di sicurezza seguenti:

  • Autenticazione come servizio (AaaS)
  • Single Sign-On/off (SSO) su più tipi di applicazione
  • Controllo di accesso per le API
  • Gateway federativo

Importante

Duende Software potrebbe richiedere il pagamento di una tariffa di licenza per l'uso in produzione di Duende Identity Server. Per altre informazioni, vedere Eseguire la migrazione da ASP.NET Core 5.0 a 6.0.

Per altre informazioni, vedere la documentazione di Duende Server (sito Web duende Identity Software).

Risorse aggiuntive

Questa esercitazione crea un'API Web che esegue operazioni Create, Read, Update e Delete (CRUD) in un database NoSQL mongoDB .

In questa esercitazione apprenderai a:

  • Configurare MongoDB
  • Creare un database MongoDB
  • Definire una raccolta e uno schema MongoDB
  • Eseguire operazioni CRUD di MongoDB da un'API Web
  • Personalizzare la JSserializzazione ON

Visualizzare o scaricare il codice di esempio (procedura per il download)

Prerequisiti

Configurare MongoDB

Se si usa Windows, MongoDB è installato in C:\Programmi\MongoDB per impostazione predefinita. Aggiungere C:\Programmi\MongoDB\Server\<version_number>\bin alla Path variabile di ambiente. Questa modifica consente l'accesso MongoDB da qualsiasi posizione nel computer di sviluppo.

Usare la shell mongo nelle procedure seguenti per creare un database, creare le raccolte e archiviare i documenti. Per altre informazioni sui comandi della shell mongo, vedere Working with the mongo Shell (Utilizzo della shell mongo).

  1. Scegliere una directory nel computer di sviluppo per archiviare i dati. Ad esempio, C:\BooksData in Windows. Creare la directory se non esiste. La shell mongo non consente di creare nuove directory.

  2. Aprire una shell dei comandi. Eseguire il comando seguente per connettersi a MongoDB sulla porta predefinita 27017. Ricordare di sostituire <data_directory_path> con la directory scelta nel passaggio precedente.

    mongod --dbpath <data_directory_path>
    
  3. Aprire un'altra istanza della shell dei comandi. Connettersi al database di test predefinito eseguendo il comando seguente:

    mongo
    
  4. Eseguire il comando seguente in una shell dei comandi:

    use BookstoreDb
    

    Se non esiste già, viene creato un database denominato BookstoreDb . Se il database esiste, la connessione viene aperta per le transazioni.

  5. Creare una raccolta Books tramite il comando seguente:

    db.createCollection('Books')
    

    Viene visualizzato il risultato seguente:

    { "ok" : 1 }
    
  6. Definire uno schema per la raccolta Books e inserire due documenti usando il comando seguente:

    db.Books.insertMany([{'Name':'Design Patterns','Price':54.93,'Category':'Computers','Author':'Ralph Johnson'}, {'Name':'Clean Code','Price':43.15,'Category':'Computers','Author':'Robert C. Martin'}])
    

    Viene visualizzato il risultato seguente:

    {
      "acknowledged" : true,
      "insertedIds" : [
        ObjectId("5bfd996f7b8e48dc15ff215d"),
        ObjectId("5bfd996f7b8e48dc15ff215e")
      ]
    }
    

    Nota

    L'ID illustrato in questo articolo non corrisponde agli ID quando si esegue questo campione.

  7. Visualizzare i documenti nel database usando il comando seguente:

    db.Books.find({}).pretty()
    

    Viene visualizzato il risultato seguente:

    {
      "_id" : ObjectId("5bfd996f7b8e48dc15ff215d"),
      "Name" : "Design Patterns",
      "Price" : 54.93,
      "Category" : "Computers",
      "Author" : "Ralph Johnson"
    }
    {
      "_id" : ObjectId("5bfd996f7b8e48dc15ff215e"),
      "Name" : "Clean Code",
      "Price" : 43.15,
      "Category" : "Computers",
      "Author" : "Robert C. Martin"
    }
    

    Lo schema aggiunge una proprietà _id generata automaticamente di tipo ObjectId per ogni documento.

Il database è pronto. È possibile iniziare a creare l'API Web ASP.NET Core.

Creare il progetto per l'API Web ASP.NET Core

  1. Passare a File>Nuovo>Progetto.

  2. Selezionare il tipo di progetto Applicazione Web ASP.NET Core e selezionare Avanti.

  3. Assegnare al progetto il nome BooksApi e selezionare Crea.

  4. Selezionare il framework di destinazione .NET Core e ASP.NET Core 3.0. Selezionare il modello di progetto API e scegliere Crea.

  5. Visitare la raccolta NuGet: MongoDB.Driver per determinare la versione stabile più recente del driver .NET per MongoDB. Nella finestra Console di Gestione pacchetti passare alla radice del progetto. Eseguire il comando seguente per installare il driver .NET per MongoDB:

    Install-Package MongoDB.Driver -Version {VERSION}
    

Aggiungere un modello di entità

  1. Aggiungere una directory Models alla radice del progetto.

  2. Aggiungere una classe Book alla directory Models con il codice seguente:

    using MongoDB.Bson;
    using MongoDB.Bson.Serialization.Attributes;
    
    namespace BooksApi.Models
    {
        public class Book
        {
            [BsonId]
            [BsonRepresentation(BsonType.ObjectId)]
            public string Id { get; set; }
    
            [BsonElement("Name")]
            public string BookName { get; set; }
    
            public decimal Price { get; set; }
    
            public string Category { get; set; }
    
            public string Author { get; set; }
        }
    }
    

    Nella classe precedente la Id proprietà è:

    • Obbligatorio per eseguire il mapping dell'oggetto Common Language Runtime (CLR) all'insieme MongoDB.
    • Annotato con [BsonId] per impostare questa proprietà sulla chiave primaria del documento.
    • Annotato con [BsonRepresentation(BsonType.ObjectId)] per consentire il passaggio del parametro come tipo string invece di una struttura ObjectId . Mongo gestisce la conversione da string a ObjectId.

    La BookName proprietà viene annotata con l'attributo [BsonElement] . Il valore dell'attributo Name rappresenta il nome della proprietà nella raccolta MongoDB.

Aggiungere un modello di configurazione

  1. Aggiungere i valori di configurazione del database seguenti a appsettings.json:

    {
      "BookstoreDatabaseSettings": {
        "BooksCollectionName": "Books",
        "ConnectionString": "mongodb://localhost:27017",
        "DatabaseName": "BookstoreDb"
      },
      "Logging": {
        "IncludeScopes": false,
        "Debug": {
          "LogLevel": {
            "Default": "Warning"
          }
        },
        "Console": {
          "LogLevel": {
            "Default": "Warning"
          }
        }
      }
    }
    
  2. Aggiungere un BookstoreDatabaseSettings.cs file alla directory Models con il codice seguente:

    namespace BooksApi.Models
    {
        public class BookstoreDatabaseSettings : IBookstoreDatabaseSettings
        {
            public string BooksCollectionName { get; set; }
            public string ConnectionString { get; set; }
            public string DatabaseName { get; set; }
        }
    
        public interface IBookstoreDatabaseSettings
        {
            string BooksCollectionName { get; set; }
            string ConnectionString { get; set; }
            string DatabaseName { get; set; }
        }
    }
    

    La classe precedente BookstoreDatabaseSettings viene usata per archiviare i appsettings.json valori delle proprietà del BookstoreDatabaseSettings file. I nomi delle JSproprietà ON e C# sono denominati in modo identico per semplificare il processo di mapping.

  3. Aggiungere il codice evidenziato seguente a Startup.ConfigureServices:

    public void ConfigureServices(IServiceCollection services)
    {
        // requires using Microsoft.Extensions.Options
        services.Configure<BookstoreDatabaseSettings>(
            Configuration.GetSection(nameof(BookstoreDatabaseSettings)));
    
        services.AddSingleton<IBookstoreDatabaseSettings>(sp =>
            sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value);
    
        services.AddControllers();
    }
    

    Nel codice precedente:

    • L'istanza di configurazione a cui viene associata la appsettings.json sezione del BookstoreDatabaseSettings file viene registrata nel contenitore inserimento delle dipendenze . Ad esempio, la proprietà di un BookstoreDatabaseSettings oggetto viene popolata con la BookstoreDatabaseSettings:ConnectionString proprietà in appsettings.json.ConnectionString
    • L'interfaccia IBookstoreDatabaseSettings è registrata nell'inserimento di dipendenze con una durata del servizio singleton. Quando avviene l'inserimento, l'istanza dell'interfaccia restituisce un oggetto BookstoreDatabaseSettings.
  4. Aggiungere il codice seguente all'inizio di Startup.cs per risolvere i BookstoreDatabaseSettings riferimenti e IBookstoreDatabaseSettings :

    using BooksApi.Models;
    

Aggiungere un servizio di operazioni CRUD

  1. Aggiungere una directory Services alla radice del progetto.

  2. Aggiungere una classe BookService alla directory Services con il codice seguente:

    using BooksApi.Models;
    using MongoDB.Driver;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace BooksApi.Services
    {
        public class BookService
        {
            private readonly IMongoCollection<Book> _books;
    
            public BookService(IBookstoreDatabaseSettings settings)
            {
                var client = new MongoClient(settings.ConnectionString);
                var database = client.GetDatabase(settings.DatabaseName);
    
                _books = database.GetCollection<Book>(settings.BooksCollectionName);
            }
    
            public List<Book> Get() =>
                _books.Find(book => true).ToList();
    
            public Book Get(string id) =>
                _books.Find<Book>(book => book.Id == id).FirstOrDefault();
    
            public Book Create(Book book)
            {
                _books.InsertOne(book);
                return book;
            }
    
            public void Update(string id, Book bookIn) =>
                _books.ReplaceOne(book => book.Id == id, bookIn);
    
            public void Remove(Book bookIn) =>
                _books.DeleteOne(book => book.Id == bookIn.Id);
    
            public void Remove(string id) => 
                _books.DeleteOne(book => book.Id == id);
        }
    }
    

    Nel codice precedente un'istanza di IBookstoreDatabaseSettings viene recuperata dall'inserimento di dipendenze tramite l'inserimento del costruttore. Questa tecnica consente di accedere ai valori di appsettings.json configurazione aggiunti nella sezione Aggiungere un modello di configurazione.

  3. Aggiungere il codice evidenziato seguente a Startup.ConfigureServices:

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<BookstoreDatabaseSettings>(
            Configuration.GetSection(nameof(BookstoreDatabaseSettings)));
    
        services.AddSingleton<IBookstoreDatabaseSettings>(sp =>
            sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value);
    
        services.AddSingleton<BookService>();
    
        services.AddControllers();
    }
    

    Nel codice precedente la classe BookService è registrata con l'inserimento di dipendenze per supportare l'inserimento del costruttore nelle classi che la utilizzano. La durata del servizio singleton è più appropriata perché BookService assume una dipendenza diretta a MongoClient. In base alle linee guida per il riutilizzo di Mongo Client ufficiali, MongoClient deve essere registrato nell'inserimento di dipendenze con una durata del servizio singleton.

  4. Aggiungere il codice seguente all'inizio di Startup.cs per risolvere il BookService riferimento:

    using BooksApi.Services;
    

La BookService classe usa i membri seguenti MongoDB.Driver per eseguire operazioni CRUD sul database:

  • MongoClient: legge l'istanza del server per l'esecuzione di operazioni di database. Al costruttore di questa classe viene passata la stringa di connessione MongoDB:

    public BookService(IBookstoreDatabaseSettings settings)
    {
        var client = new MongoClient(settings.ConnectionString);
        var database = client.GetDatabase(settings.DatabaseName);
    
        _books = database.GetCollection<Book>(settings.BooksCollectionName);
    }
    
  • IMongoDatabase: rappresenta il database Mongo per l'esecuzione delle operazioni. Questa esercitazione usa il metodo getCollection<TDocument>(collection) generico sull'interfaccia per ottenere l'accesso ai dati in una raccolta specifica. Eseguire operazioni CRUD sulla raccolta dopo la chiamata di questo metodo. Nella chiamata del metodo GetCollection<TDocument>(collection):

    • collection rappresenta il nome della raccolta.
    • TDocument rappresenta il tipo di oggetto CLR archiviato nella raccolta.

GetCollection<TDocument>(collection) restituisce un oggetto MongoCollection che rappresenta la raccolta. In questa esercitazione, vengono richiamati i metodi seguenti sulla raccolta:

  • DeleteOne: elimina un singolo documento corrispondente ai criteri di ricerca specificati.
  • Trova<TDocument>: restituisce tutti i documenti nella raccolta che corrispondono ai criteri di ricerca specificati.
  • InsertOne: inserisce l'oggetto fornito come nuovo documento nell'insieme.
  • ReplaceOne: sostituisce il singolo documento corrispondente ai criteri di ricerca specificati con l'oggetto specificato.

Aggiungere un controller

Aggiungere una classe BooksController alla directory Controllers con il codice seguente:

using BooksApi.Models;
using BooksApi.Services;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace BooksApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BooksController : ControllerBase
    {
        private readonly BookService _bookService;

        public BooksController(BookService bookService)
        {
            _bookService = bookService;
        }

        [HttpGet]
        public ActionResult<List<Book>> Get() =>
            _bookService.Get();

        [HttpGet("{id:length(24)}", Name = "GetBook")]
        public ActionResult<Book> Get(string id)
        {
            var book = _bookService.Get(id);

            if (book == null)
            {
                return NotFound();
            }

            return book;
        }

        [HttpPost]
        public ActionResult<Book> Create(Book book)
        {
            _bookService.Create(book);

            return CreatedAtRoute("GetBook", new { id = book.Id.ToString() }, book);
        }

        [HttpPut("{id:length(24)}")]
        public IActionResult Update(string id, Book bookIn)
        {
            var book = _bookService.Get(id);

            if (book == null)
            {
                return NotFound();
            }

            _bookService.Update(id, bookIn);

            return NoContent();
        }

        [HttpDelete("{id:length(24)}")]
        public IActionResult Delete(string id)
        {
            var book = _bookService.Get(id);

            if (book == null)
            {
                return NotFound();
            }

            _bookService.Remove(id);

            return NoContent();
        }
    }
}

Il controller dell'API Web precedente:

  • Usa la BookService classe per eseguire operazioni CRUD.
  • Contiene metodi di azione per supportare le richieste HTTP GET, POST, PUT e DELETE.
  • Chiama CreatedAtRoute nel metodo dell'azione Create per restituire una risposta HTTP 201. Il codice di stato 201 è la risposta standard per un metodo HTTP POST che crea una nuova risorsa nel server. Location aggiunge anche un'intestazione CreatedAtRoute alla risposta. L'intestazione Location specifica l'URI del libro appena creato.

Testare l'API Web

  1. Compilare ed eseguire l'app.

  2. Passare a https://localhost:<port>/api/books per testare il metodo dell'azione Get senza parametri del controller. Viene visualizzata la risposta ON seguente JS:

    [
      {
        "id":"5bfd996f7b8e48dc15ff215d",
        "bookName":"Design Patterns",
        "price":54.93,
        "category":"Computers",
        "author":"Ralph Johnson"
      },
      {
        "id":"5bfd996f7b8e48dc15ff215e",
        "bookName":"Clean Code",
        "price":43.15,
        "category":"Computers",
        "author":"Robert C. Martin"
      }
    ]
    
  3. Passare a https://localhost:<port>/api/books/{id here} per testare il metodo dell'azione Get in overload del controller. Viene visualizzata la risposta ON seguente JS:

    {
      "id":"{ID}",
      "bookName":"Clean Code",
      "price":43.15,
      "category":"Computers",
      "author":"Robert C. Martin"
    }
    

Configurare le opzioni di JSserializzazione ON

Esistono due dettagli da modificare sulle JSrisposte ON restituite nella sezione Testare l'API Web:

  • La notazione a cammello predefinita per i nomi di proprietà deve essere modificata in modo da adottare la convenzione Pascal dei nomi di proprietà dell'oggetto CLR.
  • La proprietà bookName deve essere restituita come Name.

Per soddisfare i requisiti precedenti, apportare le modifiche seguenti:

  1. Json.NET è stato rimosso dal framework condiviso di ASP.NET. Aggiungere un riferimento al pacchetto a Microsoft.AspNetCore.Mvc.NewtonsoftJson.

  2. In Startup.ConfigureServices concatenare il codice evidenziato seguente alla chiamata del metodo AddControllers:

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<BookstoreDatabaseSettings>(
            Configuration.GetSection(nameof(BookstoreDatabaseSettings)));
    
        services.AddSingleton<IBookstoreDatabaseSettings>(sp =>
            sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value);
    
        services.AddSingleton<BookService>();
    
        services.AddControllers()
            .AddNewtonsoftJson(options => options.UseMemberCasing());
    }
    

    Con la modifica precedente, i nomi delle proprietà nella risposta ON serializzata JSdell'API Web corrispondono ai nomi delle proprietà corrispondenti nel tipo di oggetto CLR. Ad esempio, la proprietà Author della classe Book viene serializzata come Author.

  3. In Models/Book.csannotare la BookName proprietà con l'attributo seguente [JsonProperty] :

    [BsonElement("Name")]
    [JsonProperty("Name")]
    public string BookName { get; set; }
    

    Il [JsonProperty] valore dell'attributo di rappresenta il nome della Name proprietà nella risposta ON serializzata JSdell'API Web.

  4. Aggiungere il codice seguente all'inizio di Models/Book.cs per risolvere il riferimento all'attributo [JsonProperty] :

    using Newtonsoft.Json;
    
  5. Ripetere i passaggi definiti nella sezione Testare l'API Web. Si noti la differenza nei nomi delle JSproprietà ON.

Aggiungere il supporto per l'autenticazione a un'API Web

ASP.NET Core Identity aggiunge funzionalità di accesso dell'interfaccia utente alle app Web core ASP.NET. Per proteggere le API Web e i contratti a pagina singola, usare una delle opzioni seguenti:

Duende IdentityServer è un framework OpenID Connessione e OAuth 2.0 per ASP.NET Core. Duende IdentityServer abilita le funzionalità di sicurezza seguenti:

  • Autenticazione come servizio (AaaS)
  • Single Sign-On/off (SSO) su più tipi di applicazione
  • Controllo di accesso per le API
  • Gateway federativo

Per altre informazioni, vedere Panoramica di Duende IdentityServer.

Per altre informazioni su altri provider di autenticazione, vedere Opzioni di autenticazione del sistema operativo della community per ASP.NET Core

Passaggi successivi

Per altre informazioni sulla creazione di API Web ASP.NET Core, vedere le risorse seguenti: