使用 ASP.NET Core 和 MongoDB 创建 Web API

注意

此版本不是本文的最新版本。 对于当前版本,请参阅此文的 .NET 8 版本

重要

此信息与预发布产品相关,相应产品在商业发布之前可能会进行重大修改。 Microsoft 对此处提供的信息不提供任何明示或暗示的保证。

对于当前版本,请参阅此文的 .NET 8 版本

作者 Pratik KhandelwalScott Addie

本教程创建对 MongoDB NoSQL 数据库运行创建、读取、更新和删除 (CRUD) 操作的 Web API。

在本教程中,你将了解:

  • 配置 MongoDB
  • 创建 MongoDB 数据库
  • 定义 MongoDB 集合和架构
  • 从 Web API 执行 MongoDB CRUD 操作
  • 自定义 JSON 序列化

先决条件

配置 MongoDB

从开发计算机上的任意位置启用 MongoDB 和 MongoDB Shell 访问 (Windows/Linux/macOS):

  1. 下载并安装 MongoDB Shell:

    • macOS/Linux:选择要将 MongoDB Shell 提取到的目录。 将 mongosh 的结果路径添加到 PATH 环境变量。
    • Windows:MongoDB Shell (mongosh.exe) 安装在 C:\Users<user>\AppData\Local\Programs\mongosh。 将 mongosh.exe 的结果路径添加到 PATH 环境变量。
  2. 下载并安装 MongoDB:

    • macOS/Linux:验证安装了 MongoDB 的目录,通常在 /usr/local/mongodb 中。 将 mongodb 的结果路径添加到 PATH 环境变量。
    • Windows:MongoDB 默认安装在 C:\Program Files\MongoDB。 将 C:\Program Files\MongoDB\Server\version_number>\bin 添加到 PATH 环境变量。
  3. 选择数据存储目录:选择开发计算机上用于存储数据的目录。 创建目录(如果不存在)。 MongoDB Shell 不会创建新目录:

    • macOS/Linux:例如 /usr/local/var/mongodb
    • Windows:例如 C:\\BooksData
  4. 在 OS 命令行界面(而不是 MongoDB Shell)中,使用以下命令连接到默认端口 27017 上的 MongoDB。 将 <data_directory_path> 替换为上一步中选择的目录。

    mongod --dbpath <data_directory_path>
    

在以下步骤中使用之前安装的 MongoDB Shell 可以创建数据库、创建集合和存储文档。 有关 MongoDB Shell 命令的详细信息,请参阅 mongosh

  1. 通过启动 mongosh.exe 打开 MongoDB 命令行界面实例。

  2. 在命令 shell 中,通过运行以下命令连接到默认的测试数据库:

    mongosh
    
  3. 在命令行界面中运行以下命令:

    use BookStore
    

    如果它不存在,则创建名为 BookStore 的数据库。 如果该数据库存在,则将为事务打开其连接。

  4. 使用以下命令创建 Books 集合:

    db.createCollection('Books')
    

    显示以下结果:

    { "ok" : 1 }
    
  5. 使用以下命令定义 Books 集合的架构并插入两个文档:

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

    显示的结果如下所示:

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

    注意

    前面结果中显示的 ObjectId 与命令行界面中显示的结果不一致。

  6. 使用以下命令查看数据库中的文档:

    db.Books.find().pretty()
    

    显示的结果如下所示:

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

    该架构将为每个文档添加类型 ObjectId 的自动生成的 _id 属性。

创建 ASP.NET Core Web API 项目

  1. 转到“文件”“新建”“项目”。

  2. 选择“ASP.NET Core Web API”项目类型,然后选择“下一步”。

  3. 将项目命名为“BookStoreApi”,然后选择“下一步”。

  4. 选择“.NET 8.0 (长期支持)”框架,然后选择“创建”

  5. 在“包管理器控制台”窗口中,导航到项目根。 运行以下命令以安装适用于 MongoDB 的 .NET 驱动程序:

    Install-Package MongoDB.Driver
    

添加实体模型

  1. 将 Models 目录添加到项目根。

  2. 使用以下代码将 Book 类添加到 Models 目录:

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

    在前面的类中,Id 属性为:

    BookName 属性使用 [BsonElement] 特性进行批注。 Name 的属性值表示 MongoDB 集合中的属性名称。

添加配置模型

  1. appsettings.json 添加以下数据库配置值:

    {
        "BookStoreDatabase": {
            "ConnectionString": "mongodb://localhost:27017",
            "DatabaseName": "BookStore",
            "BooksCollectionName": "Books"
        },
        "Logging": {
            "LogLevel": {
                "Default": "Information",
                "Microsoft.AspNetCore": "Warning"
            }
        },
        "AllowedHosts": "*"
    }
    
  2. 使用以下代码将 BookStoreDatabaseSettings 类添加到 Models 目录:

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

    前面的 BookStoreDatabaseSettings 类用于存储 appsettings.json 文件的 BookStoreDatabase 属性值。 JSON 和 C# 具有相同的属性名称,目的是简化映射过程。

  3. 将以下突出显示的代码添加到 Program.cs

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

    在前面的代码中,appsettings.json 文件的 BookStoreDatabase 部分绑定到的配置实例在依赖关系注入 (DI) 容器中注册。 例如,BookStoreDatabaseSettings 对象的 ConnectionString 属性使用 appsettings.json 中的 BookStoreDatabase:ConnectionString 属性进行填充。

  4. 将以下代码添加到 Program.cs 的顶部以解析 BookStoreDatabaseSettings 引用:

    using BookStoreApi.Models;
    

添加 CRUD 操作服务

  1. 将 Services 目录添加到项目根。

  2. 使用以下代码将 BooksService 类添加到 Services 目录:

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

    上面的代码通过构造函数注入从 DI 检索 BookStoreDatabaseSettings 实例。 使用此方法可访问在添加配置模型部分中添加的 配置值。

  3. 将以下突出显示的代码添加到 Program.cs

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

    上面的代码向 DI 注册了 BooksService 类,以支持消费类中的构造函数注入。 单一实例服务生存期是最合适的,因为 BooksService 直接依赖于 MongoClient。 根据官方 Mongo Client 重用准则,应使用单一实例服务生存期在 DI 中注册 MongoClient

  4. 将以下代码添加到 Program.cs 的顶部以解析 BooksService 引用:

    using BookStoreApi.Services;
    

BooksService 类使用以下 MongoDB.Driver 成员对数据库运行 CRUD 操作:

  • MongoClient:读取用于运行数据库操作的服务器实例。 此类的构造函数是在 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:表示用于运行操作的 Mongo 数据库。 本教程在界面上使用泛型 GetCollection<TDocument>(collection) 方法来获取对特定集合中数据的访问权限。 调用此方法后,对集合运行 CRUD 操作。 在 GetCollection<TDocument>(collection) 方法调用中:

    • collection 表示集合名称。
    • TDocument 表示存储在集合中的 CLR 对象类型。

GetCollection<TDocument>(collection) 返回表示集合的 GetCollection<TDocument>(collection) 对象。 在本教程中,对集合调用以下方法:

  • DeleteOneAsync:删除与提供的搜索条件匹配的单个文档。
  • Find<TDocument>:返回集合中与提供的搜索条件匹配的所有文档。
  • InsertOneAsync:插入提供的对象作为集合中的新文档。
  • ReplaceOneAsync:将与提供的搜索条件匹配的单个文档替换为提供的对象。

添加控制器

使用以下代码将 BooksController 类添加到 Controllers 目录:

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

前面的 Web API 控制器:

  • 使用 BooksService 类运行 CRUD 操作。
  • 包含操作方法以支持 GET、POST、PUT 和 DELETE HTTP 请求。
  • Create 操作方法中调用 CreatedAtAction,以返回 CreatedAtAction 响应。 状态代码 201 是在服务器上创建新资源的 HTTP POST 方法的标准响应。 CreatedAtAction 还会将 Location 标头添加到响应中。 Location 标头指定新建书籍的 URI。

测试 Web API

  1. 生成并运行应用。

  2. 导航到 https://localhost:<port>/api/books,其中 <port> 是为应用自动分配的端口号,用于测试控制器的无参数 Get 操作方法,请选择“试用”>“执行”。 显示类似下面的 JSON 响应:

    [
      {
        "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. 导航到 https://localhost:<port>/api/books/{id here},测试控制器的重载 Get 操作方法。 显示类似下面的 JSON 响应:

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

配置 JSON 序列化选项

关于在测试 Web API 部分中返回的 JSON 响应,有两个细节需要更改:

  • 应更改属性名称的默认驼峰式大小写风格,以匹配 CLR 对象属性名称的 Pascal 大小写。
  • bookName 属性应作为 Name 返回。

为满足上述要求,请进行以下更改:

  1. Program.cs 中,将以下突出显示的代码链接到 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);
    

    通过上述更改,Web API 的序列化 JSON 响应中的属性名称与 CLR 对象类型中其相应的属性名称匹配。 例如,Book 类的 Author 属性序列化为 Author,而不是 author

  2. Models/Book.cs 中,使用 [JsonPropertyName] 特性批注 BookName 属性:

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

    [JsonPropertyName] 属性的 Name 值表示 Web API 的序列化 JSON 响应中的属性名称。

  3. 将以下代码添加到 Models/Book.cs 的顶部以解析 [JsonProperty] 特性引用:

    using System.Text.Json.Serialization;
    
  4. 重复测试 Web API 部分中定义的步骤。 注意 JSON 属性名称中的区别。

向 Web API 添加身份验证支持

ASP.NET Core Identity 将用户界面 (UI) 登录功能添加到 ASP.NET Core Web 应用。 若要保护 Web API 和 SPA,请使用以下项之一:

Duende Identity Server 是适用于 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 框架。 Duende Identity Server 支持以下安全功能:

  • 身份验证即服务 (AaaS)
  • 跨多个应用程序类型的单一登录/注销 (SSO)
  • API 的访问控制
  • Federation Gateway

重要事项

Duende Software 可能会要求你为 Duende Identity Server 的生产使用支付许可证费用。 有关详细信息,请参阅从 ASP.NET Core 5.0 迁移到 6.0

有关详细信息,请参阅 Duende Identity Server 文档(Duende Software 网站)

其他资源

本教程创建对 MongoDB NoSQL 数据库运行创建、读取、更新和删除 (CRUD) 操作的 Web API。

在本教程中,你将了解:

  • 配置 MongoDB
  • 创建 MongoDB 数据库
  • 定义 MongoDB 集合和架构
  • 从 Web API 执行 MongoDB CRUD 操作
  • 自定义 JSON 序列化

先决条件

配置 MongoDB

从开发计算机上的任意位置启用 MongoDB 和 Mongo DB Shell 访问:

  1. 在 Windows 上,MongoDB 将默认安装在 C:\Program Files\MongoDB 中。 将 C:\Program Files\MongoDB\Server\version_number>\bin 添加到 PATH 环境变量。

  2. 下载 MongoDB Shell 并选择要将它提取到的目录。 将 mongosh.exe 的结果路径添加到 PATH 环境变量。

  3. 选择开发计算机上用于存储数据的目录。 例如,在 Windows 上为 C:\BooksData。 创建目录(如果不存在)。 mongo Shell 不会创建新目录。

  4. 在 OS 命令行界面(而不是 MongoDB Shell)中,使用以下命令连接到默认端口 27017 上的 MongoDB。 将 <data_directory_path> 替换为上一步中选择的目录。

    mongod --dbpath <data_directory_path>
    

在以下步骤中使用之前安装的 MongoDB Shell 可以创建数据库、创建集合和存储文档。 有关 MongoDB Shell 命令的详细信息,请参阅 mongosh

  1. 通过启动 mongosh.exe 打开 MongoDB 命令行界面实例。

  2. 在命令行界面中,通过运行以下命令连接到默认的测试数据库:

    mongosh
    
  3. 在命令行界面中运行以下命令:

    use BookStore
    

    如果它不存在,则创建名为 BookStore 的数据库。 如果该数据库存在,则将为事务打开其连接。

  4. 使用以下命令创建 Books 集合:

    db.createCollection('Books')
    

    显示以下结果:

    { "ok" : 1 }
    
  5. 使用以下命令定义 Books 集合的架构并插入两个文档:

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

    显示的结果如下所示:

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

    注意

    前面结果中显示的 ObjectId 与命令行界面中显示的结果不一致。

  6. 使用以下命令查看数据库中的文档:

    db.Books.find().pretty()
    

    显示的结果如下所示:

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

    该架构将为每个文档添加类型 ObjectId 的自动生成的 _id 属性。

创建 ASP.NET Core Web API 项目

  1. 转到“文件”“新建”“项目”。

  2. 选择“ASP.NET Core Web API”项目类型,然后选择“下一步”。

  3. 将项目命名为“BookStoreApi”,然后选择“下一步”。

  4. 选择“.NET 7.0 (标准期限支持)”框架,然后选择“创建”。

  5. 在“工具”菜单中,选择“NuGet 包管理器”>“包管理器控制台”。

  6. 在“包管理器控制台”窗口中,导航到项目根。 运行以下命令以安装适用于 MongoDB 的 .NET 驱动程序:

    Install-Package MongoDB.Driver
    

添加实体模型

  1. 将 Models 目录添加到项目根。

  2. 使用以下代码将 Book 类添加到 Models 目录:

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

    在前面的类中,Id 属性为:

    BookName 属性使用 [BsonElement] 特性进行批注。 Name 的属性值表示 MongoDB 集合中的属性名称。

添加配置模型

  1. appsettings.json 添加以下数据库配置值:

    {
      "BookStoreDatabase": {
        "ConnectionString": "mongodb://localhost:27017",
        "DatabaseName": "BookStore",
        "BooksCollectionName": "Books"
      },
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*"
    }
    
  2. 使用以下代码将 BookStoreDatabaseSettings 类添加到 Models 目录:

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

    前面的 BookStoreDatabaseSettings 类用于存储 appsettings.json 文件的 BookStoreDatabase 属性值。 JSON 和 C# 具有相同的属性名称,目的是简化映射过程。

  3. 将以下突出显示的代码添加到 Program.cs

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

    在前面的代码中,appsettings.json 文件的 BookStoreDatabase 部分绑定到的配置实例在依赖关系注入 (DI) 容器中注册。 例如,BookStoreDatabaseSettings 对象的 ConnectionString 属性使用 appsettings.json 中的 BookStoreDatabase:ConnectionString 属性进行填充。

  4. 将以下代码添加到 Program.cs 的顶部以解析 BookStoreDatabaseSettings 引用:

    using BookStoreApi.Models;
    

添加 CRUD 操作服务

  1. 将 Services 目录添加到项目根。

  2. 使用以下代码将 BooksService 类添加到 Services 目录:

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

    上面的代码通过构造函数注入从 DI 检索 BookStoreDatabaseSettings 实例。 使用此方法可访问在添加配置模型部分中添加的 配置值。

  3. 将以下突出显示的代码添加到 Program.cs

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

    上面的代码向 DI 注册了 BooksService 类,以支持消费类中的构造函数注入。 单一实例服务生存期是最合适的,因为 BooksService 直接依赖于 MongoClient。 根据官方 Mongo Client 重用准则,应使用单一实例服务生存期在 DI 中注册 MongoClient

  4. 将以下代码添加到 Program.cs 的顶部以解析 BooksService 引用:

    using BookStoreApi.Services;
    

BooksService 类使用以下 MongoDB.Driver 成员对数据库运行 CRUD 操作:

  • MongoClient:读取用于运行数据库操作的服务器实例。 此类的构造函数提供了 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:表示用于运行操作的 Mongo 数据库。 本教程在界面上使用泛型 GetCollection<TDocument>(collection) 方法来获取对特定集合中数据的访问权限。 调用此方法后,对集合运行 CRUD 操作。 在 GetCollection<TDocument>(collection) 方法调用中:

    • collection 表示集合名称。
    • TDocument 表示存储在集合中的 CLR 对象类型。

GetCollection<TDocument>(collection) 返回表示集合的 GetCollection<TDocument>(collection) 对象。 在本教程中,对集合调用以下方法:

  • DeleteOneAsync:删除与提供的搜索条件匹配的单个文档。
  • Find<TDocument>:返回集合中与提供的搜索条件匹配的所有文档。
  • InsertOneAsync:插入提供的对象作为集合中的新文档。
  • ReplaceOneAsync:将与提供的搜索条件匹配的单个文档替换为提供的对象。

添加控制器

使用以下代码将 BooksController 类添加到 Controllers 目录:

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

前面的 Web API 控制器:

  • 使用 BooksService 类运行 CRUD 操作。
  • 包含操作方法以支持 GET、POST、PUT 和 DELETE HTTP 请求。
  • Create 操作方法中调用 CreatedAtAction,以返回 CreatedAtAction 响应。 状态代码 201 是在服务器上创建新资源的 HTTP POST 方法的标准响应。 CreatedAtAction 还会将 Location 标头添加到响应中。 Location 标头指定新建书籍的 URI。

测试 Web API

  1. 生成并运行应用。

  2. 导航到 https://localhost:<port>/api/books,其中 <port> 是应用自动分配的端口号,用于测试控制器的无参数 Get 操作方法。 显示类似下面的 JSON 响应:

    [
      {
        "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. 导航到 https://localhost:<port>/api/books/{id here},测试控制器的重载 Get 操作方法。 显示类似下面的 JSON 响应:

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

配置 JSON 序列化选项

关于在测试 Web API 部分中返回的 JSON 响应,有两个细节需要更改:

  • 应更改属性名称的默认驼峰式大小写风格,以匹配 CLR 对象属性名称的 Pascal 大小写。
  • bookName 属性应作为 Name 返回。

为满足上述要求,请进行以下更改:

  1. Program.cs 中,将以下突出显示的代码链接到 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);
    

    通过上述更改,Web API 的序列化 JSON 响应中的属性名称与 CLR 对象类型中其相应的属性名称匹配。 例如,Book 类的 Author 属性序列化为 Author,而不是 author

  2. Models/Book.cs 中,使用 [JsonPropertyName] 特性批注 BookName 属性:

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

    [JsonPropertyName] 属性的 Name 值表示 Web API 的序列化 JSON 响应中的属性名称。

  3. 将以下代码添加到 Models/Book.cs 的顶部以解析 [JsonProperty] 特性引用:

    using System.Text.Json.Serialization;
    
  4. 重复测试 Web API 部分中定义的步骤。 注意 JSON 属性名称中的区别。

向 Web API 添加身份验证支持

ASP.NET Core Identity 将用户界面 (UI) 登录功能添加到 ASP.NET Core Web 应用。 若要保护 Web API 和 SPA,请使用以下项之一:

Duende Identity Server 是适用于 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 框架。 Duende Identity Server 支持以下安全功能:

  • 身份验证即服务 (AaaS)
  • 跨多个应用程序类型的单一登录/注销 (SSO)
  • API 的访问控制
  • Federation Gateway

重要事项

Duende Software 可能会要求你为 Duende Identity Server 的生产使用支付许可证费用。 有关详细信息,请参阅从 ASP.NET Core 5.0 迁移到 6.0

有关详细信息,请参阅 Duende Identity Server 文档(Duende Software 网站)

其他资源

本教程创建对 MongoDB NoSQL 数据库运行创建、读取、更新和删除 (CRUD) 操作的 Web API。

在本教程中,你将了解:

  • 配置 MongoDB
  • 创建 MongoDB 数据库
  • 定义 MongoDB 集合和架构
  • 从 Web API 执行 MongoDB CRUD 操作
  • 自定义 JSON 序列化

先决条件

配置 MongoDB

从开发计算机上的任意位置启用 MongoDB 和 Mongo DB Shell 访问:

  1. 在 Windows 上,MongoDB 将默认安装在 C:\Program Files\MongoDB 中。 将 C:\Program Files\MongoDB\Server\version_number>\bin 添加到 PATH 环境变量。

  2. 下载 MongoDB Shell 并选择要将它提取到的目录。 将 mongosh.exe 的结果路径添加到 PATH 环境变量。

  3. 选择开发计算机上用于存储数据的目录。 例如,在 Windows 上为 C:\BooksData。 创建目录(如果不存在)。 mongo Shell 不会创建新目录。

  4. 在 OS 命令行界面(而不是 MongoDB Shell)中,使用以下命令连接到默认端口 27017 上的 MongoDB。 将 <data_directory_path> 替换为上一步中选择的目录。

    mongod --dbpath <data_directory_path>
    

在以下步骤中使用之前安装的 MongoDB Shell 可以创建数据库、创建集合和存储文档。 有关 MongoDB Shell 命令的详细信息,请参阅 mongosh

  1. 通过启动 mongosh.exe 打开 MongoDB 命令行界面实例。

  2. 在命令行界面中,通过运行以下命令连接到默认的测试数据库:

    mongosh
    
  3. 在命令行界面中运行以下命令:

    use BookStore
    

    如果它不存在,则创建名为 BookStore 的数据库。 如果该数据库存在,则将为事务打开其连接。

  4. 使用以下命令创建 Books 集合:

    db.createCollection('Books')
    

    显示以下结果:

    { "ok" : 1 }
    
  5. 使用以下命令定义 Books 集合的架构并插入两个文档:

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

    显示的结果如下所示:

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

    注意

    前面结果中显示的 ObjectId 与命令行界面中显示的结果不一致。

  6. 使用以下命令查看数据库中的文档:

    db.Books.find().pretty()
    

    显示的结果如下所示:

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

    该架构将为每个文档添加类型 ObjectId 的自动生成的 _id 属性。

创建 ASP.NET Core Web API 项目

  1. 转到“文件”“新建”“项目”。

  2. 选择“ASP.NET Core Web API”项目类型,然后选择“下一步”。

  3. 将项目命名为“BookStoreApi”,然后选择“下一步”。

  4. 选择“.NET 6.0(长期支持)”框架,然后选择“创建”。

  5. 在“包管理器控制台”窗口中,导航到项目根。 运行以下命令以安装适用于 MongoDB 的 .NET 驱动程序:

    Install-Package MongoDB.Driver
    

添加实体模型

  1. 将 Models 目录添加到项目根。

  2. 使用以下代码将 Book 类添加到 Models 目录:

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

    在前面的类中,Id 属性为:

    BookName 属性使用 [BsonElement] 特性进行批注。 Name 的属性值表示 MongoDB 集合中的属性名称。

添加配置模型

  1. appsettings.json 添加以下数据库配置值:

    {
        "BookStoreDatabase": {
            "ConnectionString": "mongodb://localhost:27017",
            "DatabaseName": "BookStore",
            "BooksCollectionName": "Books"
        },
        "Logging": {
            "LogLevel": {
                "Default": "Information",
                "Microsoft.AspNetCore": "Warning"
            }
        },
        "AllowedHosts": "*"
    }
    
  2. 使用以下代码将 BookStoreDatabaseSettings 类添加到 Models 目录:

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

    前面的 BookStoreDatabaseSettings 类用于存储 appsettings.json 文件的 BookStoreDatabase 属性值。 JSON 和 C# 具有相同的属性名称,目的是简化映射过程。

  3. 将以下突出显示的代码添加到 Program.cs

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

    在前面的代码中,appsettings.json 文件的 BookStoreDatabase 部分绑定到的配置实例在依赖关系注入 (DI) 容器中注册。 例如,BookStoreDatabaseSettings 对象的 ConnectionString 属性使用 appsettings.json 中的 BookStoreDatabase:ConnectionString 属性进行填充。

  4. 将以下代码添加到 Program.cs 的顶部以解析 BookStoreDatabaseSettings 引用:

    using BookStoreApi.Models;
    

添加 CRUD 操作服务

  1. 将 Services 目录添加到项目根。

  2. 使用以下代码将 BooksService 类添加到 Services 目录:

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

    上面的代码通过构造函数注入从 DI 检索 BookStoreDatabaseSettings 实例。 使用此方法可访问在添加配置模型部分中添加的 配置值。

  3. 将以下突出显示的代码添加到 Program.cs

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

    上面的代码向 DI 注册了 BooksService 类,以支持消费类中的构造函数注入。 单一实例服务生存期是最合适的,因为 BooksService 直接依赖于 MongoClient。 根据官方 Mongo Client 重用准则,应使用单一实例服务生存期在 DI 中注册 MongoClient

  4. 将以下代码添加到 Program.cs 的顶部以解析 BooksService 引用:

    using BookStoreApi.Services;
    

BooksService 类使用以下 MongoDB.Driver 成员对数据库运行 CRUD 操作:

  • MongoClient:读取用于运行数据库操作的服务器实例。 此类的构造函数提供了 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:表示用于运行操作的 Mongo 数据库。 本教程在界面上使用泛型 GetCollection<TDocument>(collection) 方法来获取对特定集合中数据的访问权限。 调用此方法后,对集合运行 CRUD 操作。 在 GetCollection<TDocument>(collection) 方法调用中:

    • collection 表示集合名称。
    • TDocument 表示存储在集合中的 CLR 对象类型。

GetCollection<TDocument>(collection) 返回表示集合的 GetCollection<TDocument>(collection) 对象。 在本教程中,对集合调用以下方法:

  • DeleteOneAsync:删除与提供的搜索条件匹配的单个文档。
  • Find<TDocument>:返回集合中与提供的搜索条件匹配的所有文档。
  • InsertOneAsync:插入提供的对象作为集合中的新文档。
  • ReplaceOneAsync:将与提供的搜索条件匹配的单个文档替换为提供的对象。

添加控制器

使用以下代码将 BooksController 类添加到 Controllers 目录:

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

前面的 Web API 控制器:

  • 使用 BooksService 类运行 CRUD 操作。
  • 包含操作方法以支持 GET、POST、PUT 和 DELETE HTTP 请求。
  • Create 操作方法中调用 CreatedAtAction,以返回 CreatedAtAction 响应。 状态代码 201 是在服务器上创建新资源的 HTTP POST 方法的标准响应。 CreatedAtAction 还会将 Location 标头添加到响应中。 Location 标头指定新建书籍的 URI。

测试 Web API

  1. 生成并运行应用。

  2. 导航到 https://localhost:<port>/api/books,其中 <port> 是应用自动分配的端口号,用于测试控制器的无参数 Get 操作方法。 显示类似下面的 JSON 响应:

    [
      {
        "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. 导航到 https://localhost:<port>/api/books/{id here},测试控制器的重载 Get 操作方法。 显示类似下面的 JSON 响应:

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

配置 JSON 序列化选项

关于在测试 Web API 部分中返回的 JSON 响应,有两个细节需要更改:

  • 应更改属性名称的默认驼峰式大小写风格,以匹配 CLR 对象属性名称的 Pascal 大小写。
  • bookName 属性应作为 Name 返回。

为满足上述要求,请进行以下更改:

  1. Program.cs 中,将以下突出显示的代码链接到 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);
    

    通过上述更改,Web API 的序列化 JSON 响应中的属性名称与 CLR 对象类型中其相应的属性名称匹配。 例如,Book 类的 Author 属性序列化为 Author,而不是 author

  2. Models/Book.cs 中,使用 [JsonPropertyName] 特性批注 BookName 属性:

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

    [JsonPropertyName] 属性的 Name 值表示 Web API 的序列化 JSON 响应中的属性名称。

  3. 将以下代码添加到 Models/Book.cs 的顶部以解析 [JsonProperty] 特性引用:

    using System.Text.Json.Serialization;
    
  4. 重复测试 Web API 部分中定义的步骤。 注意 JSON 属性名称中的区别。

向 Web API 添加身份验证支持

ASP.NET Core Identity 将用户界面 (UI) 登录功能添加到 ASP.NET Core Web 应用。 若要保护 Web API 和 SPA,请使用以下项之一:

Duende Identity Server 是适用于 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 框架。 Duende Identity Server 支持以下安全功能:

  • 身份验证即服务 (AaaS)
  • 跨多个应用程序类型的单一登录/注销 (SSO)
  • API 的访问控制
  • Federation Gateway

重要事项

Duende Software 可能会要求你为 Duende Identity Server 的生产使用支付许可证费用。 有关详细信息,请参阅从 ASP.NET Core 5.0 迁移到 6.0

有关详细信息,请参阅 Duende Identity Server 文档(Duende Software 网站)

其他资源

本教程创建对 MongoDB NoSQL 数据库运行创建、读取、更新和删除 (CRUD) 操作的 Web API。

在本教程中,你将了解:

  • 配置 MongoDB
  • 创建 MongoDB 数据库
  • 定义 MongoDB 集合和架构
  • 从 Web API 执行 MongoDB CRUD 操作
  • 自定义 JSON 序列化

查看或下载示例代码如何下载

先决条件

配置 MongoDB

如果使用的是 Windows,MongoDB 将默认安装在 C:\Program Files\MongoDB 中。 将 C:\Program Files\MongoDB\Server\version_number>\bin 添加到 Path 环境变量。 通过此更改可以从开发计算机上的任意位置访问 MongoDB。

使用以下步骤中的 mongo Shell 可以创建数据库、创建集合和存储文档。 有关 mongo Shell 命令的详细信息,请参阅使用 mongo Shell

  1. 选择开发计算机上用于存储数据的目录。 例如,在 Windows 上为 C:\BooksData。 创建目录(如果不存在)。 mongo Shell 不会创建新目录。

  2. 打开命令行界面。 运行以下命令以连接到默认端口 27017 上的 MongoDB。 请记得将 <data_directory_path> 替换为上一步中选择的目录。

    mongod --dbpath <data_directory_path>
    
  3. 打开另一个命令行界面实例。 通过运行以下命令来连接到默认测试数据库:

    mongo
    
  4. 在命令行界面中运行以下命令:

    use BookstoreDb
    

    如果它不存在,则将创建名为“BookstoreDb”的数据库。 如果该数据库存在,则将为事务打开其连接。

  5. 使用以下命令创建 Books 集合:

    db.createCollection('Books')
    

    显示以下结果:

    { "ok" : 1 }
    
  6. 使用以下命令定义 Books 集合的架构并插入两个文档:

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

    显示以下结果:

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

    注意

    本文所示的 ID 与运行此示例时的 ID 不匹配。

  7. 使用以下命令查看数据库中的文档:

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

    显示以下结果:

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

    该架构将为每个文档添加类型 ObjectId 的自动生成的 _id 属性。

数据库可供使用了。 你可以开始创建 ASP.NET Core Web API。

创建 ASP.NET Core Web API 项目

  1. 转到“文件”“新建”“项目”。

  2. 选择“ASP.NET Core Web 应用程序”项目类型,然后选择“下一步” 。

  3. 将项目命名为“BooksApi”,然后选择“创建”。

  4. 选择“.NET Core”目标框架和“ASP.NET Core 3.0”。 选择“API”项目模板,然后选择“创建” 。

  5. 访问 NuGet 库:MongoDB.Driver 来确定适用于 MongoDB 的 .NET 驱动程序的最新稳定版本。 在“包管理器控制台”窗口中,导航到项目根。 运行以下命令以安装适用于 MongoDB 的 .NET 驱动程序:

    Install-Package MongoDB.Driver -Version {VERSION}
    

添加实体模型

  1. 将 Models 目录添加到项目根。

  2. 使用以下代码将 Book 类添加到 Models 目录:

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

    在前面的类中,Id 属性为:

    BookName 属性使用 [BsonElement] 特性进行批注。 Name 的属性值表示 MongoDB 集合中的属性名称。

添加配置模型

  1. appsettings.json 添加以下数据库配置值:

    {
      "BookstoreDatabaseSettings": {
        "BooksCollectionName": "Books",
        "ConnectionString": "mongodb://localhost:27017",
        "DatabaseName": "BookstoreDb"
      },
      "Logging": {
        "IncludeScopes": false,
        "Debug": {
          "LogLevel": {
            "Default": "Warning"
          }
        },
        "Console": {
          "LogLevel": {
            "Default": "Warning"
          }
        }
      }
    }
    
  2. 使用以下代码将 BookstoreDatabaseSettings.cs 文件添加到 Models 目录:

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

    前面的 BookstoreDatabaseSettings 类用于存储 appsettings.json 文件的 BookstoreDatabaseSettings 属性值。 JSON 和 C# 具有相同的属性名称,目的是简化映射过程。

  3. 将以下突出显示的代码添加到 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();
    }
    

    在上述代码中:

    • appsettings.json 文件的 BookstoreDatabaseSettings 部分绑定到的配置实例在依赖关系注入 (DI) 容器中注册。 例如,BookstoreDatabaseSettings 对象的 ConnectionString 属性使用 appsettings.json 中的 BookstoreDatabaseSettings:ConnectionString 属性进行填充。
    • IBookstoreDatabaseSettings 接口使用单一实例IBookstoreDatabaseSettings在 DI 中注册。 在注入时,接口实例时将解析为 BookstoreDatabaseSettings 对象。
  4. 将以下代码添加到 Startup.cs 的顶部以解析 BookstoreDatabaseSettingsIBookstoreDatabaseSettings 引用:

    using BooksApi.Models;
    

添加 CRUD 操作服务

  1. 将 Services 目录添加到项目根。

  2. 使用以下代码将 BookService 类添加到 Services 目录:

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

    上面的代码通过构造函数注入从 DI 检索 IBookstoreDatabaseSettings 实例。 使用此方法可访问在添加配置模型部分中添加的 配置值。

  3. 将以下突出显示的代码添加到 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();
    }
    

    上面的代码向 DI 注册了 BookService 类,以支持消费类中的构造函数注入。 单一实例服务生存期是最合适的,因为 BookService 直接依赖于 MongoClient。 根据官方 Mongo Client 重用准则,应使用单一实例服务生存期在 DI 中注册 MongoClient

  4. 将以下代码添加到 Startup.cs 的顶部以解析 BookService 引用:

    using BooksApi.Services;
    

BookService 类使用以下 MongoDB.Driver 成员对数据库运行 CRUD 操作:

  • MongoClient:读取用于运行数据库操作的服务器实例。 此类的构造函数提供了 MongoDB 连接字符串:

    public BookService(IBookstoreDatabaseSettings settings)
    {
        var client = new MongoClient(settings.ConnectionString);
        var database = client.GetDatabase(settings.DatabaseName);
    
        _books = database.GetCollection<Book>(settings.BooksCollectionName);
    }
    
  • IMongoDatabase:表示用于运行操作的 Mongo 数据库。 本教程在界面上使用泛型 GetCollection<TDocument>(collection) 方法来获取对特定集合中数据的访问权限。 调用此方法后,对集合运行 CRUD 操作。 在 GetCollection<TDocument>(collection) 方法调用中:

    • collection 表示集合名称。
    • TDocument 表示存储在集合中的 CLR 对象类型。

GetCollection<TDocument>(collection) 返回表示集合的 GetCollection<TDocument>(collection) 对象。 在本教程中,对集合调用以下方法:

  • DeleteOne:删除与提供的搜索条件匹配的单个文档。
  • Find<TDocument>:返回集合中与提供的搜索条件匹配的所有文档。
  • InsertOne:插入提供的对象作为集合中的新文档。
  • ReplaceOne:将与提供的搜索条件匹配的单个文档替换为提供的对象。

添加控制器

使用以下代码将 BooksController 类添加到 Controllers 目录:

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

前面的 Web API 控制器:

  • 使用 BookService 类运行 CRUD 操作。
  • 包含操作方法以支持 GET、POST、PUT 和 DELETE HTTP 请求。
  • Create 操作方法中调用 CreatedAtRoute,以返回 CreatedAtRoute 响应。 状态代码 201 是在服务器上创建新资源的 HTTP POST 方法的标准响应。 CreatedAtRoute 还会将 Location 标头添加到响应中。 Location 标头指定新建书籍的 URI。

测试 Web API

  1. 生成并运行应用。

  2. 导航到 https://localhost:<port>/api/books,测试控制器的无参数 Get 操作方法。 将显示下面的 JSON 响应:

    [
      {
        "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. 导航到 https://localhost:<port>/api/books/{id here},测试控制器的重载 Get 操作方法。 将显示下面的 JSON 响应:

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

配置 JSON 序列化选项

关于在测试 Web API 部分中返回的 JSON 响应,有两个细节需要更改:

  • 应更改属性名称的默认驼峰式大小写风格,以匹配 CLR 对象属性名称的 Pascal 大小写。
  • bookName 属性应作为 Name 返回。

为满足上述要求,请进行以下更改:

  1. Json.NET 已从 ASP.NET 共享框架中删除。 将包引用添加到 Microsoft.AspNetCore.Mvc.NewtonsoftJson

  2. Startup.ConfigureServices 中,将以下突出显示的代码链接到 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());
    }
    

    通过上述更改,Web API 的序列化 JSON 响应中的属性名称与 CLR 对象类型中其相应的属性名称匹配。 例如,Book 类的 Author 属性序列化为 Author

  3. Models/Book.cs 中,使用以下 [JsonProperty] 特性批注 BookName 属性:

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

    [JsonProperty] 属性的 Name 值表示 Web API 的序列化 JSON 响应中的属性名称。

  4. 将以下代码添加到 Models/Book.cs 的顶部以解析 [JsonProperty] 特性引用:

    using Newtonsoft.Json;
    
  5. 重复测试 Web API 部分中定义的步骤。 注意 JSON 属性名称中的区别。

向 Web API 添加身份验证支持

ASP.NET Core Identity 将用户界面 (UI) 登录功能添加到 ASP.NET Core Web 应用。 若要保护 Web API 和 SPA,请使用以下项之一:

Duende IdentityServer 是适用于 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 框架。 Duende IdentityServer 支持以下安全功能:

  • 身份验证即服务 (AaaS)
  • 跨多个应用程序类型的单一登录/注销 (SSO)
  • API 的访问控制
  • Federation Gateway

有关详细信息,请参阅 Duende IdentityServer 概述

有关其他身份验证提供程序的详细信息,请参阅适用于 ASP.NET Core 的社区 OSS 身份验证选项

后续步骤

有关构建 ASP.NET Core Web API 的详细信息,请参阅以下资源: