A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://docs.microsoft.com/aspnet/core/tutorials/first-mongo-app below:

Create a web API with ASP.NET Core and MongoDB

Important

This information relates to a pre-release product that may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.

For the current release, see the .NET 9 version of this article.

By Pratik Khandelwal and Scott Addie

This tutorial creates a web API that runs Create, Read, Update, and Delete (CRUD) operations on a MongoDB NoSQL database.

In this tutorial, you learn how to:

Prerequisites Configure MongoDB

Enable MongoDB and MongoDB Shell access from anywhere on the development machine (Windows/Linux/macOS):

  1. Download and Install MongoDB Shell:

  2. Download and Install MongoDB:

  3. Choose a Data Storage Directory: Select a directory on your development machine for storing data. Create the directory if it doesn't exist. The MongoDB Shell doesn't create new directories:

  4. In the OS command shell (not the MongoDB Shell), use the following command to connect to MongoDB on default port 27017. Replace <data_directory_path> with the directory chosen in the previous step.

    mongod --dbpath <data_directory_path>
    

Use the previously installed MongoDB Shell in the following steps to create a database, make collections, and store documents. For more information on MongoDB Shell commands, see mongosh.

  1. Open a MongoDB command shell instance by launching mongosh.exe, or by running the following command in the command shell:

    mongosh
    
  2. In the command shell connect to the default test database by running:

    use BookStore
    

    A database named BookStore is created if it doesn't already exist. If the database does exist, its connection is opened for transactions.

  3. Create a Books collection using following command:

    db.createCollection('Books')
    

    The following result is displayed:

    { "ok" : 1 }
    
  4. Define a schema for the Books collection and insert two documents using the following command:

    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" }])
    

    A result similar to the following is displayed:

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

    Note

    The ObjectIds shown in the preceding result won't match those shown in the command shell.

  5. View the documents in the database using the following command:

    db.Books.find().pretty()
    

    A result similar to the following is displayed:

    {
         "_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"
     }
    

    The schema adds an autogenerated _id property of type ObjectId for each document.

Create the ASP.NET Core web API project
  1. Go to File > New > Project.
  2. Select the ASP.NET Core Web API project type, and select Next.
  3. Name the project BookStoreApi, and select Next.
  4. In the Additional information dialog:
  1. In the Package Manager Console window, navigate to the project root. Run the following command to install the .NET driver for MongoDB:

    Install-Package MongoDB.Driver
    
  1. Run the following commands in a command shell:

    dotnet new webapi -o BookStoreApi --use-controllers
    code BookStoreApi
    

    The preceding commands generate a new ASP.NET Core web API project and then open the project in Visual Studio Code.

  2. Once the OmniSharp server starts up, a dialog asks Required assets to build and debug are missing from 'BookStoreApi'. Add them?. Select Yes.

  3. Open the Integrated Terminal and run the following command to install the .NET driver for MongoDB:

    dotnet add package MongoDB.Driver
    
Add an entity model
  1. Add a Models directory to the project root.

  2. Add a Book class to the Models directory with the following code:

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

    In the preceding class, the Id property is:

    The BookName property is annotated with the [BsonElement] attribute. The attribute's value of Name represents the property name in the MongoDB collection.

Add a configuration model
  1. Add the following database configuration values to appsettings.json:

    {
      "BookStoreDatabase": {
        "ConnectionString": "mongodb://localhost:27017",
        "DatabaseName": "BookStore",
        "BooksCollectionName": "Books"
      },
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*"
    }
    
  2. Add a BookStoreDatabaseSettings class to the Models directory with the following code:

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

    The preceding BookStoreDatabaseSettings class is used to store the appsettings.json file's BookStoreDatabase property values. The JSON and C# property names are named identically to ease the mapping process.

  3. Add the following highlighted code to Program.cs:

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

    In the preceding code, the configuration instance to which the appsettings.json file's BookStoreDatabase section binds is registered in the Dependency Injection (DI) container. For example, the BookStoreDatabaseSettings object's ConnectionString property is populated with the BookStoreDatabase:ConnectionString property in appsettings.json.

  4. Add the following code to the top of Program.cs to resolve the BookStoreDatabaseSettings reference:

    using BookStoreApi.Models;
    
Add a CRUD operations service
  1. Add a Services directory to the project root.

  2. Add a BooksService class to the Services directory with the following code:

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

    In the preceding code, a BookStoreDatabaseSettings instance is retrieved from DI via constructor injection. This technique provides access to the appsettings.json configuration values that were added in the Add a configuration model section.

  3. Add the following highlighted code to Program.cs:

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

    In the preceding code, the BooksService class is registered with DI to support constructor injection in consuming classes. The singleton service lifetime is most appropriate because BooksService takes a direct dependency on MongoClient. Per the official Mongo Client reuse guidelines, MongoClient should be registered in DI with a singleton service lifetime.

  4. Add the following code to the top of Program.cs to resolve the BooksService reference:

    using BookStoreApi.Services;
    

The BooksService class uses the following MongoDB.Driver members to run CRUD operations against the database:

GetCollection<TDocument>(collection) returns a MongoCollection object representing the collection. In this tutorial, the following methods are invoked on the collection:

Add a controller

Add a BooksController class to the Controllers directory with the following code:

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

The preceding web API controller:

Configure JSON serialization options

There are two details to change about the JSON responses returned in the Test the web API section:

To satisfy the preceding requirements, make the following changes:

  1. In Program.cs, chain the following highlighted code on to the AddControllers method call:

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

    With the preceding change, property names in the web API's serialized JSON response match their corresponding property names in the CLR object type. For example, the Book class's Author property serializes as Author instead of author.

  2. In Models/Book.cs, annotate the BookName property with the [JsonPropertyName] attribute:

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

    The [JsonPropertyName] attribute's value of Name represents the property name in the web API's serialized JSON response.

  3. Add the following code to the top of Models/Book.cs to resolve the [JsonProperty] attribute reference:

    using System.Text.Json.Serialization;
    
  4. Repeat the steps defined in the Test the web API section. Notice the difference in JSON property names.

Test the web API

This tutorial uses Endpoints Explorer and .http files to test the API.

  1. Build and run the app.

  2. In Endpoints Explorer, right-click the first GET endpoint /api/books, and select Generate request.

    The following content is added to the BookStoreApi.http file. If this is the first time that a request is generated, the file is created in the project root.

    @BookStoreApi_HostAddress = https://localhost:<port>
    
    GET {{BookStoreApi_HostAddress}}/api/books
    
    ###
    

    The port number should already be set to the port used by the app, for example, https://localhost:56874. If that's not the case you can find your port number in the output window when you launch the app.

  3. Select the Send request link above the new GET request line.

    The GET request is sent to the app and the response is displayed in the Response pane.

  4. The response body shows the JSON result containing the book entries similar to the following:

    [
      {
        "Id": "61a6058e6c43f32854e51f51",
        "Name": "Design Patterns",
        "Price": 54.93,
        "Category": "Computers",
        "Author": "Ralph Johnson"
      },
      {
        "Id": "61a6058e6c43f32854e51f52",
        "Name": "Clean Code",
        "Price": 43.15,
        "Category": "Computers",
        "Author": "Robert C. Martin"
      }
    ]
    
  5. To retrieve a single book, right-click the /api/books/{id}, params (string id) GET endpoint in the Endpoints Explorer, and select Generate request.

    The following content is appended to the BookStoreApi.http file:

    @id=string
    GET {{BookStoreApi_HostAddress}}/api/books/{{id}}
    
    ###
    
  6. Replace id variable with one of the IDs returned from the earlier request, for example:

    @id="61a6058e6c43f32854e51f52"
    GET {{BookStoreApi_HostAddress}}/api/books/{{id}}
    
    ###
    
  7. Select the Send request link above the new GET request line.

    The GET request is sent to the app and the response is displayed in the Response pane.

  8. The response body shows JSON similar to the following:

    {
      "Id": "61a6058e6c43f32854e51f52",
      "Name": "Clean Code",
      "Price": 43.15,
      "Category": "Computers",
      "Author": "Robert C. Martin"
    }
    
  9. To test the POST endpoint, right-click the /api/books POST endpoint and select Generate request.

    The following content is added to the BookStoreApi.http file:

    POST {{BookStoreApi_HostAddress}}/api/books
    Content-Type: application/json
    
    {
      //Book
    }
    
    ###
    
  10. Replace the Book comment with a book object as the JSON request body:

    POST {{BookStoreApi_HostAddress}}/api/books
    Content-Type: application/json
    
     {
       "Name": "The Pragmatic Programmer",
       "Price": 49.99,
       "Category": "Computers",
       "Author": "Andy Hunt"
     }
    
    ###
    
  11. Select the Send request link above the POST request line.

    The POST request is sent to the app, and the response is displayed in the Response pane. The response should include the newly created book with its assigned ID.

  12. Lastly, to delete a book, right-click the /api/books/{id}, params (string id) DELETE endpoint and select Generate request.

    The following content is appended to the BookStoreApi.http file:

    DELETE {{BookStoreApi_HostAddress}}/api/Books/{{id}}
    
    ###
    
  13. Replace the id variable with one of the IDs returned from the earlier request, and click Send request. For example:

    DELETE {{BookStoreApi_HostAddress}}/api/Books/67f417517ce1b36aeab71236
    
    ###
    

This tutorial uses the OpenAPI specification (openapi.json) and Swagger UI to test the API.

  1. Install Swagger UI by running the following command:
dotnet add package NSwag.AspNetCore

The previous command adds the NSwag.AspNetCore package, which contains tools to generate Swagger documents and UI. Because our project is using OpenAPI, we only use the NSwag package to generate the Swagger UI.

  1. Configure Swagger middleware

In Program.cs, add the following highlighted code:

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/openapi/v1.json", "v1");
    });
}

The previous code enables the Swagger middleware for serving the generated JSON document using the Swagger UI. Swagger is only enabled in a development environment. Enabling Swagger in a production environment could expose potentially sensitive details about the API's structure and implementation.

The app uses the OpenAPI document generated by OpenApi, located at /openapi/v1.json, to generate the UI. View the generated OpenAPI specification for the WeatherForecast API while the project is running by navigating to https://localhost:<port>/openapi/v1.json in your browser.

The OpenAPI specification is a document in JSON format that describes the structure and capabilities of your API, including endpoints, request/response formats, parameters, and more. It's essentially a blueprint of your API that can be used by various tools to understand and interact with your API.

  1. Build and run the app.

  2. Navigate to https://localhost:<port>/swagger in your browser. Swagger provides a UI to test all the API endpoints based on the OpenAPI document.

  3. Expand the GET /api/books endpoint and click the Try it out button.

  4. Click the Execute button to send the request to the API.

  5. The Response body section displays a JSON array with books similar to the following:

    [
      {
        "Id": "61a6058e6c43f32854e51f51",
        "Name": "Design Patterns",
        "Price": 54.93,
        "Category": "Computers",
        "Author": "Ralph Johnson"
      },
      {
        "Id": "61a6058e6c43f32854e51f52",
        "Name": "Clean Code",
        "Price": 43.15,
        "Category": "Computers",
        "Author": "Robert C. Martin"
      }
    ]
    
  6. Next, expand the GET /api/books/{id} endpoint and click Try it out.

  7. Enter one of the book IDs from the previous response in the id field, then click Execute.

  8. The Response body section displays the JSON object for the specified book. For example, the result for the ID 61a6058e6c43f32854e51f52 is similar to the following:

    {
      "Id": "61a6058e6c43f32854e51f52",
      "Name": "Clean Code",
      "Price": 43.15,
      "Category": "Computers",
      "Author": "Robert C. Martin"
    }
    
  9. To test creating a new book, expand the POST /api/books endpoint and click Try it out.

  10. Replace the default request body with a new book object:

    {
      "Name": "The Pragmatic Programmer",
      "Price": 49.99,
      "Category": "Computers",
      "Author": "Andy Hunt"
    }
    
  11. Click Execute to send the request.

  12. The response should have a status code of 201 (Created) and include the newly created book with its assigned ID in the response body.

  13. Lastly, to delete a book record, expand the DELETE /api/books/{id} endpoint, click Try it out, and enter one of the book IDs from the previous response in the id field. Click Execute to send the request.

  14. The response should have a status code of 204 (No Content), indicating that the book was successfully deleted.

Add authentication support to a web API

ASP.NET Core Identity adds user interface (UI) login functionality to ASP.NET Core web apps. To secure web APIs and SPAs, use one of the following:

Duende Identity Server is an OpenID Connect and OAuth 2.0 framework for ASP.NET Core. Duende Identity Server enables the following security features:

For more information, see the Duende Identity Server documentation (Duende Software website).

Additional resources

This tutorial creates a web API that runs Create, Read, Update, and Delete (CRUD) operations on a MongoDB NoSQL database.

In this tutorial, you learn how to:

Prerequisites Configure MongoDB

Enable MongoDB and MongoDB Shell access from anywhere on the development machine (Windows/Linux/macOS):

  1. Download and Install MongoDB Shell:

  2. Download and Install MongoDB:

  3. Choose a Data Storage Directory: Select a directory on your development machine for storing data. Create the directory if it doesn't exist. The MongoDB Shell doesn't create new directories:

  4. In the OS command shell (not the MongoDB Shell), use the following command to connect to MongoDB on default port 27017. Replace <data_directory_path> with the directory chosen in the previous step.

    mongod --dbpath <data_directory_path>
    

Use the previously installed MongoDB Shell in the following steps to create a database, make collections, and store documents. For more information on MongoDB Shell commands, see mongosh.

  1. Open a MongoDB command shell instance by launching mongosh.exe, or by running the following command in the command shell:

    mongosh
    
  2. In the command shell connect to the default test database by running:

    use BookStore
    

    A database named BookStore is created if it doesn't already exist. If the database does exist, its connection is opened for transactions.

  3. Create a Books collection using following command:

    db.createCollection('Books')
    

    The following result is displayed:

    { "ok" : 1 }
    
  4. Define a schema for the Books collection and insert two documents using the following command:

    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" }])
    

    A result similar to the following is displayed:

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

    Note

    The ObjectIds shown in the preceding result won't match those shown in the command shell.

  5. View the documents in the database using the following command:

    db.Books.find().pretty()
    

    A result similar to the following is displayed:

    {
         "_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"
     }
    

    The schema adds an autogenerated _id property of type ObjectId for each document.

Create the ASP.NET Core web API project
  1. Go to File > New > Project.

  2. Select the ASP.NET Core Web API project type, and select Next.

  3. Name the project BookStoreApi, and select Next.

  4. Select the .NET 8.0 (Long Term support) framework and select Create.

  5. In the Package Manager Console window, navigate to the project root. Run the following command to install the .NET driver for MongoDB:

    Install-Package MongoDB.Driver
    
  1. Run the following commands in a command shell:

    dotnet new webapi -o BookStoreApi
    code BookStoreApi
    

    The preceding commands generate a new ASP.NET Core web API project and then open the project in Visual Studio Code.

  2. Once the OmniSharp server starts up, a dialog asks Required assets to build and debug are missing from 'BookStoreApi'. Add them?. Select Yes.

  3. Open the Integrated Terminal and run the following command to install the .NET driver for MongoDB:

    dotnet add package MongoDB.Driver
    
Add an entity model
  1. Add a Models directory to the project root.

  2. Add a Book class to the Models directory with the following code:

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

    In the preceding class, the Id property is:

    The BookName property is annotated with the [BsonElement] attribute. The attribute's value of Name represents the property name in the MongoDB collection.

Add a configuration model
  1. Add the following database configuration values to appsettings.json:

    {
      "BookStoreDatabase": {
        "ConnectionString": "mongodb://localhost:27017",
        "DatabaseName": "BookStore",
        "BooksCollectionName": "Books"
      },
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*"
    }
    
  2. Add a BookStoreDatabaseSettings class to the Models directory with the following code:

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

    The preceding BookStoreDatabaseSettings class is used to store the appsettings.json file's BookStoreDatabase property values. The JSON and C# property names are named identically to ease the mapping process.

  3. Add the following highlighted code to Program.cs:

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

    In the preceding code, the configuration instance to which the appsettings.json file's BookStoreDatabase section binds is registered in the Dependency Injection (DI) container. For example, the BookStoreDatabaseSettings object's ConnectionString property is populated with the BookStoreDatabase:ConnectionString property in appsettings.json.

  4. Add the following code to the top of Program.cs to resolve the BookStoreDatabaseSettings reference:

    using BookStoreApi.Models;
    
Add a CRUD operations service
  1. Add a Services directory to the project root.

  2. Add a BooksService class to the Services directory with the following code:

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

    In the preceding code, a BookStoreDatabaseSettings instance is retrieved from DI via constructor injection. This technique provides access to the appsettings.json configuration values that were added in the Add a configuration model section.

  3. Add the following highlighted code to Program.cs:

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

    In the preceding code, the BooksService class is registered with DI to support constructor injection in consuming classes. The singleton service lifetime is most appropriate because BooksService takes a direct dependency on MongoClient. Per the official Mongo Client reuse guidelines, MongoClient should be registered in DI with a singleton service lifetime.

  4. Add the following code to the top of Program.cs to resolve the BooksService reference:

    using BookStoreApi.Services;
    

The BooksService class uses the following MongoDB.Driver members to run CRUD operations against the database:

GetCollection<TDocument>(collection) returns a MongoCollection object representing the collection. In this tutorial, the following methods are invoked on the collection:

Add a controller

Add a BooksController class to the Controllers directory with the following code:

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

The preceding web API controller:

Test the web API
  1. Build and run the app.

  2. Navigate to https://localhost:<port>/api/books, where <port> is the automatically assigned port number for the app, to test the controller's parameterless Get action method. A JSON response similar to the following is displayed:

    [
      {
        "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. Navigate to https://localhost:<port>/api/books/{id here} to test the controller's overloaded Get action method. A JSON response similar to the following is displayed:

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

There are two details to change about the JSON responses returned in the Test the web API section:

To satisfy the preceding requirements, make the following changes:

  1. In Program.cs, chain the following highlighted code on to the AddControllers method call:

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

    With the preceding change, property names in the web API's serialized JSON response match their corresponding property names in the CLR object type. For example, the Book class's Author property serializes as Author instead of author.

  2. In Models/Book.cs, annotate the BookName property with the [JsonPropertyName] attribute:

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

    The [JsonPropertyName] attribute's value of Name represents the property name in the web API's serialized JSON response.

  3. Add the following code to the top of Models/Book.cs to resolve the [JsonProperty] attribute reference:

    using System.Text.Json.Serialization;
    
  4. Repeat the steps defined in the Test the web API section. Notice the difference in JSON property names.

Add authentication support to a web API

ASP.NET Core Identity adds user interface (UI) login functionality to ASP.NET Core web apps. To secure web APIs and SPAs, use one of the following:

Duende Identity Server is an OpenID Connect and OAuth 2.0 framework for ASP.NET Core. Duende Identity Server enables the following security features:

For more information, see the Duende Identity Server documentation (Duende Software website).

Additional resources

This tutorial creates a web API that runs Create, Read, Update, and Delete (CRUD) operations on a MongoDB NoSQL database.

In this tutorial, you learn how to:

Prerequisites Configure MongoDB

Enable MongoDB and Mongo DB Shell access from anywhere on the development machine:

  1. On Windows, MongoDB is installed at C:\Program Files\MongoDB by default. Add C:\Program Files\MongoDB\Server\<version_number>\bin to the PATH environment variable.

  2. Download the MongoDB Shell and choose a directory to extract it to. Add the resulting path for mongosh.exe to the PATH environment variable.

  3. Choose a directory on the development machine for storing the data. For example, C:\BooksData on Windows. Create the directory if it doesn't exist. The mongo Shell doesn't create new directories.

  4. In the OS command shell (not the MongoDB Shell), use the following command to connect to MongoDB on default port 27017. Replace <data_directory_path> with the directory chosen in the previous step.

    mongod --dbpath <data_directory_path>
    

Use the previously installed MongoDB Shell in the following steps to create a database, make collections, and store documents. For more information on MongoDB Shell commands, see mongosh.

  1. Open a MongoDB command shell instance by launching mongosh.exe, or by running the following command in the command shell:

    mongosh
    
  2. In the command shell connect to the default test database by running:

    use BookStore
    

    A database named BookStore is created if it doesn't already exist. If the database does exist, its connection is opened for transactions.

  3. Create a Books collection using following command:

    db.createCollection('Books')
    

    The following result is displayed:

    { "ok" : 1 }
    
  4. Define a schema for the Books collection and insert two documents using the following command:

    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" }])
    

    A result similar to the following is displayed:

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

    Note

    The ObjectIds shown in the preceding result won't match those shown in the command shell.

  5. View the documents in the database using the following command:

    db.Books.find().pretty()
    

    A result similar to the following is displayed:

    {
         "_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"
     }
    

    The schema adds an autogenerated _id property of type ObjectId for each document.

Create the ASP.NET Core web API project
  1. Go to File > New > Project.

  2. Select the ASP.NET Core Web API project type, and select Next.

  3. Name the project BookStoreApi, and select Next.

  4. Select the .NET 7.0 (Standard Term Support) framework and select Create.

  5. From the Tools menu, select NuGet Package Manager > Package Manager Console.

  6. In the Package Manager Console window, navigate to the project root. Run the following command to install the .NET driver for MongoDB:

    Install-Package MongoDB.Driver
    
  1. Run the following commands in a command shell:

    dotnet new webapi -o BookStoreApi
    code BookStoreApi
    

    The preceding commands generate a new ASP.NET Core web API project and then open the project in Visual Studio Code.

  2. Once the OmniSharp server starts up , a dialog asks Required assets to build and debug are missing from 'BookStoreApi'. Add them?. Select Yes.

  3. Open the Integrated Terminal and run the following command to install the .NET driver for MongoDB:

    dotnet add package MongoDB.Driver
    
  1. Select File > New Solution > Web and Console > App from the sidebar.
  2. Select the ASP.NET Core > API C# project template, and select Next.
  3. Select .NET 7.0 from the Target Framework drop-down list, and select Next.
  4. Enter BookStoreApi for the Project Name, and select Create.
  5. In the Solution pad, right-click the project's Dependencies node and select Add Packages.
  6. Enter MongoDB.Driver in the search box, select the MongoDB.Driver package, and select Add Package.
  7. Select the Accept button in the License Acceptance dialog.
Add an entity model
  1. Add a Models directory to the project root.

  2. Add a Book class to the Models directory with the following code:

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

    In the preceding class, the Id property is:

    The BookName property is annotated with the [BsonElement] attribute. The attribute's value of Name represents the property name in the MongoDB collection.

Add a configuration model
  1. Add the following database configuration values to appsettings.json:

    {
      "BookStoreDatabase": {
        "ConnectionString": "mongodb://localhost:27017",
        "DatabaseName": "BookStore",
        "BooksCollectionName": "Books"
      },
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*"
    }
    
  2. Add a BookStoreDatabaseSettings class to the Models directory with the following code:

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

    The preceding BookStoreDatabaseSettings class is used to store the appsettings.json file's BookStoreDatabase property values. The JSON and C# property names are named identically to ease the mapping process.

  3. Add the following highlighted code to Program.cs:

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

    In the preceding code, the configuration instance to which the appsettings.json file's BookStoreDatabase section binds is registered in the Dependency Injection (DI) container. For example, the BookStoreDatabaseSettings object's ConnectionString property is populated with the BookStoreDatabase:ConnectionString property in appsettings.json.

  4. Add the following code to the top of Program.cs to resolve the BookStoreDatabaseSettings reference:

    using BookStoreApi.Models;
    
Add a CRUD operations service
  1. Add a Services directory to the project root.

  2. Add a BooksService class to the Services directory with the following code:

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

    In the preceding code, a BookStoreDatabaseSettings instance is retrieved from DI via constructor injection. This technique provides access to the appsettings.json configuration values that were added in the Add a configuration model section.

  3. Add the following highlighted code to Program.cs:

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

    In the preceding code, the BooksService class is registered with DI to support constructor injection in consuming classes. The singleton service lifetime is most appropriate because BooksService takes a direct dependency on MongoClient. Per the official Mongo Client reuse guidelines, MongoClient should be registered in DI with a singleton service lifetime.

  4. Add the following code to the top of Program.cs to resolve the BooksService reference:

    using BookStoreApi.Services;
    

The BooksService class uses the following MongoDB.Driver members to run CRUD operations against the database:

GetCollection<TDocument>(collection) returns a MongoCollection object representing the collection. In this tutorial, the following methods are invoked on the collection:

Add a controller

Add a BooksController class to the Controllers directory with the following code:

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

The preceding web API controller:

Test the web API
  1. Build and run the app.

  2. Navigate to https://localhost:<port>/api/books, where <port> is the automatically assigned port number for the app, to test the controller's parameterless Get action method. A JSON response similar to the following is displayed:

    [
      {
        "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. Navigate to https://localhost:<port>/api/books/{id here} to test the controller's overloaded Get action method. A JSON response similar to the following is displayed:

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

There are two details to change about the JSON responses returned in the Test the web API section:

To satisfy the preceding requirements, make the following changes:

  1. In Program.cs, chain the following highlighted code on to the AddControllers method call:

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

    With the preceding change, property names in the web API's serialized JSON response match their corresponding property names in the CLR object type. For example, the Book class's Author property serializes as Author instead of author.

  2. In Models/Book.cs, annotate the BookName property with the [JsonPropertyName] attribute:

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

    The [JsonPropertyName] attribute's value of Name represents the property name in the web API's serialized JSON response.

  3. Add the following code to the top of Models/Book.cs to resolve the [JsonProperty] attribute reference:

    using System.Text.Json.Serialization;
    
  4. Repeat the steps defined in the Test the web API section. Notice the difference in JSON property names.

Add authentication support to a web API

ASP.NET Core Identity adds user interface (UI) login functionality to ASP.NET Core web apps. To secure web APIs and SPAs, use one of the following:

Duende Identity Server is an OpenID Connect and OAuth 2.0 framework for ASP.NET Core. Duende Identity Server enables the following security features:

For more information, see the Duende Identity Server documentation (Duende Software website).

Additional resources

This tutorial creates a web API that runs Create, Read, Update, and Delete (CRUD) operations on a MongoDB NoSQL database.

In this tutorial, you learn how to:

Prerequisites Configure MongoDB

Enable MongoDB and Mongo DB Shell access from anywhere on the development machine:

  1. On Windows, MongoDB is installed at C:\Program Files\MongoDB by default. Add C:\Program Files\MongoDB\Server\<version_number>\bin to the PATH environment variable.

  2. Download the MongoDB Shell and choose a directory to extract it to. Add the resulting path for mongosh.exe to the PATH environment variable.

  3. Choose a directory on the development machine for storing the data. For example, C:\BooksData on Windows. Create the directory if it doesn't exist. The mongo Shell doesn't create new directories.

  4. In the OS command shell (not the MongoDB Shell), use the following command to connect to MongoDB on default port 27017. Replace <data_directory_path> with the directory chosen in the previous step.

    mongod --dbpath <data_directory_path>
    

Use the previously installed MongoDB Shell in the following steps to create a database, make collections, and store documents. For more information on MongoDB Shell commands, see mongosh.

  1. Open a MongoDB command shell instance by launching mongosh.exe, or by running the following command in the command shell:

    mongosh
    
  2. In the command shell connect to the default test database by running:

    use BookStore
    

    A database named BookStore is created if it doesn't already exist. If the database does exist, its connection is opened for transactions.

  3. Create a Books collection using following command:

    db.createCollection('Books')
    

    The following result is displayed:

    { "ok" : 1 }
    
  4. Define a schema for the Books collection and insert two documents using the following command:

    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" }])
    

    A result similar to the following is displayed:

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

    Note

    The ObjectIds shown in the preceding result won't match those shown in the command shell.

  5. View the documents in the database using the following command:

    db.Books.find().pretty()
    

    A result similar to the following is displayed:

    {
         "_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"
     }
    

    The schema adds an autogenerated _id property of type ObjectId for each document.

Create the ASP.NET Core web API project
  1. Go to File > New > Project.

  2. Select the ASP.NET Core Web API project type, and select Next.

  3. Name the project BookStoreApi, and select Next.

  4. Select the .NET 6.0 (Long-term support) framework and select Create.

  5. In the Package Manager Console window, navigate to the project root. Run the following command to install the .NET driver for MongoDB:

    Install-Package MongoDB.Driver
    
  1. Run the following commands in a command shell:

    dotnet new webapi -o BookStoreApi
    code BookStoreApi
    

    The preceding commands generate a new ASP.NET Core web API project and then open the project in Visual Studio Code.

  2. Once the OmniSharp server starts up , a dialog asks Required assets to build and debug are missing from 'BookStoreApi'. Add them?. Select Yes.

  3. Open the Integrated Terminal and run the following command to install the .NET driver for MongoDB:

    dotnet add package MongoDB.Driver
    
  1. Select File > New Solution > Web and Console > App from the sidebar.
  2. Select the ASP.NET Core > API C# project template, and select Next.
  3. Select .NET 6.0 from the Target Framework drop-down list, and select Next.
  4. Enter BookStoreApi for the Project Name, and select Create.
  5. In the Solution pad, right-click the project's Dependencies node and select Add Packages.
  6. Enter MongoDB.Driver in the search box, select the MongoDB.Driver package, and select Add Package.
  7. Select the Accept button in the License Acceptance dialog.
Add an entity model
  1. Add a Models directory to the project root.

  2. Add a Book class to the Models directory with the following code:

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

    In the preceding class, the Id property is:

    The BookName property is annotated with the [BsonElement] attribute. The attribute's value of Name represents the property name in the MongoDB collection.

Add a configuration model
  1. Add the following database configuration values to appsettings.json:

    {
        "BookStoreDatabase": {
            "ConnectionString": "mongodb://localhost:27017",
            "DatabaseName": "BookStore",
            "BooksCollectionName": "Books"
        },
        "Logging": {
            "LogLevel": {
                "Default": "Information",
                "Microsoft.AspNetCore": "Warning"
            }
        },
        "AllowedHosts": "*"
    }
    
  2. Add a BookStoreDatabaseSettings class to the Models directory with the following code:

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

    The preceding BookStoreDatabaseSettings class is used to store the appsettings.json file's BookStoreDatabase property values. The JSON and C# property names are named identically to ease the mapping process.

  3. Add the following highlighted code to Program.cs:

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

    In the preceding code, the configuration instance to which the appsettings.json file's BookStoreDatabase section binds is registered in the Dependency Injection (DI) container. For example, the BookStoreDatabaseSettings object's ConnectionString property is populated with the BookStoreDatabase:ConnectionString property in appsettings.json.

  4. Add the following code to the top of Program.cs to resolve the BookStoreDatabaseSettings reference:

    using BookStoreApi.Models;
    
Add a CRUD operations service
  1. Add a Services directory to the project root.

  2. Add a BooksService class to the Services directory with the following code:

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

    In the preceding code, a BookStoreDatabaseSettings instance is retrieved from DI via constructor injection. This technique provides access to the appsettings.json configuration values that were added in the Add a configuration model section.

  3. Add the following highlighted code to Program.cs:

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

    In the preceding code, the BooksService class is registered with DI to support constructor injection in consuming classes. The singleton service lifetime is most appropriate because BooksService takes a direct dependency on MongoClient. Per the official Mongo Client reuse guidelines, MongoClient should be registered in DI with a singleton service lifetime.

  4. Add the following code to the top of Program.cs to resolve the BooksService reference:

    using BookStoreApi.Services;
    

The BooksService class uses the following MongoDB.Driver members to run CRUD operations against the database:

GetCollection<TDocument>(collection) returns a MongoCollection object representing the collection. In this tutorial, the following methods are invoked on the collection:

Add a controller

Add a BooksController class to the Controllers directory with the following code:

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

The preceding web API controller:

Test the web API
  1. Build and run the app.

  2. Navigate to https://localhost:<port>/api/books, where <port> is the automatically assigned port number for the app, to test the controller's parameterless Get action method. A JSON response similar to the following is displayed:

    [
      {
        "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. Navigate to https://localhost:<port>/api/books/{id here} to test the controller's overloaded Get action method. A JSON response similar to the following is displayed:

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

There are two details to change about the JSON responses returned in the Test the web API section:

To satisfy the preceding requirements, make the following changes:

  1. In Program.cs, chain the following highlighted code on to the AddControllers method call:

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

    With the preceding change, property names in the web API's serialized JSON response match their corresponding property names in the CLR object type. For example, the Book class's Author property serializes as Author instead of author.

  2. In Models/Book.cs, annotate the BookName property with the [JsonPropertyName] attribute:

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

    The [JsonPropertyName] attribute's value of Name represents the property name in the web API's serialized JSON response.

  3. Add the following code to the top of Models/Book.cs to resolve the [JsonProperty] attribute reference:

    using System.Text.Json.Serialization;
    
  4. Repeat the steps defined in the Test the web API section. Notice the difference in JSON property names.

Add authentication support to a web API

ASP.NET Core Identity adds user interface (UI) login functionality to ASP.NET Core web apps. To secure web APIs and SPAs, use one of the following:

Duende Identity Server is an OpenID Connect and OAuth 2.0 framework for ASP.NET Core. Duende Identity Server enables the following security features:

For more information, see the Duende Identity Server documentation (Duende Software website).

Additional resources

This tutorial creates a web API that runs Create, Read, Update, and Delete (CRUD) operations on a MongoDB NoSQL database.

In this tutorial, you learn how to:

View or download sample code (how to download)

Prerequisites Configure MongoDB

If using Windows, MongoDB is installed at C:\Program Files\MongoDB by default. Add C:\Program Files\MongoDB\Server\<version_number>\bin to the Path environment variable. This change enables MongoDB access from anywhere on your development machine.

Use the mongo Shell in the following steps to create a database, make collections, and store documents. For more information on mongo Shell commands, see Working with the mongo Shell.

  1. Choose a directory on your development machine for storing the data. For example, C:\BooksData on Windows. Create the directory if it doesn't exist. The mongo Shell doesn't create new directories.

  2. Open a command shell. Run the following command to connect to MongoDB on default port 27017. Remember to replace <data_directory_path> with the directory you chose in the previous step.

    mongod --dbpath <data_directory_path>
    
  3. Open another command shell instance. Connect to the default test database by running the following command:

    mongo
    
  4. Run the following command in a command shell:

    use BookstoreDb
    

    A database named BookstoreDb is created if it doesn't already exist. If the database does exist, its connection is opened for transactions.

  5. Create a Books collection using following command:

    db.createCollection('Books')
    

    The following result is displayed:

    { "ok" : 1 }
    
  6. Define a schema for the Books collection and insert two documents using the following command:

    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'}])
    

    The following result is displayed:

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

    Note

    The ID's shown in this article will not match the IDs when you run this sample.

  7. View the documents in the database using the following command:

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

    The following result is displayed:

    {
      "_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"
    }
    

    The schema adds an autogenerated _id property of type ObjectId for each document.

The database is ready. You can start creating the ASP.NET Core web API.

Create the ASP.NET Core web API project
  1. Go to File > New > Project.

  2. Select the ASP.NET Core Web Application project type, and select Next.

  3. Name the project BooksApi, and select Create.

  4. Select the .NET Core target framework and ASP.NET Core 3.0. Select the API project template, and select Create.

  5. Visit the NuGet Gallery: MongoDB.Driver to determine the latest stable version of the .NET driver for MongoDB. In the Package Manager Console window, navigate to the project root. Run the following command to install the .NET driver for MongoDB:

    Install-Package MongoDB.Driver -Version {VERSION}
    
  1. Run the following commands in a command shell:

    dotnet new webapi -o BooksApi
    code BooksApi
    

    A new ASP.NET Core web API project targeting .NET Core is generated and opened in Visual Studio Code.

  2. After the status bar's OmniSharp flame icon turns green, a dialog asks Required assets to build and debug are missing from 'BooksApi'. Add them?. Select Yes.

  3. Visit the NuGet Gallery: MongoDB.Driver to determine the latest stable version of the .NET driver for MongoDB. Open Integrated Terminal and navigate to the project root. Run the following command to install the .NET driver for MongoDB:

    dotnet add BooksApi.csproj package MongoDB.Driver -v {VERSION}
    
  1. In Visual Studio for Mac earlier than version 8.6, select File > New Solution > .NET Core > App from the sidebar. In version 8.6 or later, select File > New Solution > Web and Console > App from the sidebar.
  2. Select the ASP.NET Core > API C# project template, and select Next.
  3. Select .NET Core 3.1 from the Target Framework drop-down list, and select Next.
  4. Enter BooksApi for the Project Name, and select Create.
  5. In the Solution pad, right-click the project's Dependencies node and select Add Packages.
  6. Enter MongoDB.Driver in the search box, select the MongoDB.Driver package, and select Add Package.
  7. Select the Accept button in the License Acceptance dialog.
Add an entity model
  1. Add a Models directory to the project root.

  2. Add a Book class to the Models directory with the following code:

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

    In the preceding class, the Id property is:

    The BookName property is annotated with the [BsonElement] attribute. The attribute's value of Name represents the property name in the MongoDB collection.

Add a configuration model
  1. Add the following database configuration values to appsettings.json:

    {
      "BookstoreDatabaseSettings": {
        "BooksCollectionName": "Books",
        "ConnectionString": "mongodb://localhost:27017",
        "DatabaseName": "BookstoreDb"
      },
      "Logging": {
        "IncludeScopes": false,
        "Debug": {
          "LogLevel": {
            "Default": "Warning"
          }
        },
        "Console": {
          "LogLevel": {
            "Default": "Warning"
          }
        }
      }
    }
    
  2. Add a BookstoreDatabaseSettings.cs file to the Models directory with the following code:

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

    The preceding BookstoreDatabaseSettings class is used to store the appsettings.json file's BookstoreDatabaseSettings property values. The JSON and C# property names are named identically to ease the mapping process.

  3. Add the following highlighted code to 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();
    }
    

    In the preceding code:

  4. Add the following code to the top of Startup.cs to resolve the BookstoreDatabaseSettings and IBookstoreDatabaseSettings references:

    using BooksApi.Models;
    
Add a CRUD operations service
  1. Add a Services directory to the project root.

  2. Add a BookService class to the Services directory with the following code:

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

    In the preceding code, an IBookstoreDatabaseSettings instance is retrieved from DI via constructor injection. This technique provides access to the appsettings.json configuration values that were added in the Add a configuration model section.

  3. Add the following highlighted code to 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();
    }
    

    In the preceding code, the BookService class is registered with DI to support constructor injection in consuming classes. The singleton service lifetime is most appropriate because BookService takes a direct dependency on MongoClient. Per the official Mongo Client reuse guidelines, MongoClient should be registered in DI with a singleton service lifetime.

  4. Add the following code to the top of Startup.cs to resolve the BookService reference:

    using BooksApi.Services;
    

The BookService class uses the following MongoDB.Driver members to run CRUD operations against the database:

GetCollection<TDocument>(collection) returns a MongoCollection object representing the collection. In this tutorial, the following methods are invoked on the collection:

Add a controller

Add a BooksController class to the Controllers directory with the following code:

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

The preceding web API controller:

Test the web API
  1. Build and run the app.

  2. Navigate to https://localhost:<port>/api/books to test the controller's parameterless Get action method. The following JSON response is displayed:

    [
      {
        "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. Navigate to https://localhost:<port>/api/books/{id here} to test the controller's overloaded Get action method. The following JSON response is displayed:

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

There are two details to change about the JSON responses returned in the Test the web API section:

To satisfy the preceding requirements, make the following changes:

  1. Json.NET has been removed from ASP.NET shared framework. Add a package reference to Microsoft.AspNetCore.Mvc.NewtonsoftJson.

  2. In Startup.ConfigureServices, chain the following highlighted code on to the AddControllers method call:

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

    With the preceding change, property names in the web API's serialized JSON response match their corresponding property names in the CLR object type. For example, the Book class's Author property serializes as Author.

  3. In Models/Book.cs, annotate the BookName property with the following [JsonProperty] attribute:

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

    The [JsonProperty] attribute's value of Name represents the property name in the web API's serialized JSON response.

  4. Add the following code to the top of Models/Book.cs to resolve the [JsonProperty] attribute reference:

    using Newtonsoft.Json;
    
  5. Repeat the steps defined in the Test the web API section. Notice the difference in JSON property names.

Add authentication support to a web API

ASP.NET Core Identity adds user interface (UI) login functionality to ASP.NET Core web apps. To secure web APIs and SPAs, use one of the following:

Duende IdentityServer is an OpenID Connect and OAuth 2.0 framework for ASP.NET Core. Duende IdentityServer enables the following security features:

For more information, see Overview of Duende IdentityServer.

For more information on other authentication providers, see Community OSS authentication options for ASP.NET Core

Next steps

For more information on building ASP.NET Core web APIs, see the following resources:


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4