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

注意

此版本不是本文的最新版本。 有关当前版本,请参阅本文的 .NET 9 版本

警告

此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 有关当前版本,请参阅本文的 .NET 9 版本

重要

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

有关当前版本,请参阅本文的 .NET 9 版本

作者 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 命令 shell 实例:

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

    use BookStore
    

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

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

    db.createCollection('Books')
    

    显示以下结果:

    { "ok" : 1 }
    
  4. 使用以下命令定义 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 与命令行界面中显示的结果不一致。

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

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

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

创建 ASP.NET Core Web API 项目

  1. 转到“文件”>“新建”>“项目”
  2. 选择“ASP.NET Core Web API”项目类型,然后选择“下一步”。
  3. 将项目命名为“BookStoreApi”,然后选择“下一步”。
  4. 在“其他信息”对话框中:
  • 确认 Framework.NET 9.0(标准期限支持)
  • 确认已选中“使用控制器”复选框
  • 确认已选中“启用 OpenAPI 支持”复选框。
  • 选择 创建
  1. 在“包管理器控制台”窗口中,导航到项目根。 运行以下命令以安装适用于 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 属性为:

    • 需要在将公共语言运行时 (CLR) 对象映射到 MongoDB 集合时使用。
    • 使用 [BsonId] 进行批注,以将此属性指定为文档的主键。
    • 使用 [BsonRepresentation(BsonType.ObjectId)] 进行注释,以允许将参数作为类型 string 而非 ObjectId 结构传递。 Mongo 处理从 stringObjectId 的转换。

    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 属性使用 BookStoreDatabase:ConnectionString 中的 appsettings.json 属性进行填充。

  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 实例。 使用此方法可访问在appsettings.json部分添加的 配置值。

  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) 返回表示集合的 MongoCollection 对象。 在本教程中,对集合调用以下方法:

  • 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 请求。
  • CreatedAtAction操作方法中调用Create,以返回HTTP 201响应。 状态代码 201 是在服务器上创建新资源的 HTTP POST 方法的标准响应。 CreatedAtAction 还会将 Location 标头添加到响应中。 Location 标头指定新建书籍的 URI。

配置 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 中,使用 BookName 特性批注 [JsonPropertyName] 属性:

    [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

本教程使用终结点资源管理器和 .http 文件来测试 API。

  1. 生成并运行应用。

  2. 终结点资源管理器中,右键单击第一个 GET 终结点 /api/books,然后选择“ 生成请求”。

    以下内容将添加到 BookStoreApi.http 文件。 如果这是首次生成请求,则会在项目根目录中创建该文件。

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

    端口号应已设置为应用使用的端口,例如 https://localhost:56874。 如果不是这种情况,可以在启动应用时在输出窗口中找到端口号。

  3. 选择新请求行上方的GET”链接。

    GET 请求将发送到应用,响应将显示在“响应”窗格中。

  4. 响应正文显示JSON格式的结果,其中包含类似于以下内容的书籍条目:

    [
      {
        "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. 若要检索单个书籍,请在/api/books/{id}, params (string id)终结点资源管理器中右键单击 GET 终结点,然后选择“生成请求”。

    以下内容将追加到 BookStoreApi.http 该文件中:

    @id=string
    GET {{BookStoreApi_HostAddress}}/api/books/{{id}}
    
    ###
    
  6. 将变量替换为 id 先前请求返回的 ID 之一,例如:

    @id="61a6058e6c43f32854e51f52"
    GET {{BookStoreApi_HostAddress}}/api/books/{{id}}
    
    ###
    
  7. 选择新请求行上方的GET”链接。

    GET 请求将发送到应用,响应将显示在“响应”窗格中。

  8. 在响应正文中可以看到类似于以下内容的 JSON:

    {
      "Id": "61a6058e6c43f32854e51f52",
      "Name": "Clean Code",
      "Price": 43.15,
      "Category": "Computers",
      "Author": "Robert C. Martin"
    }
    
  9. 若要测试 POST 终结点,请/api/books右键单击 POST 终结点并选择“生成请求”。

    将以下内容添加到 BookStoreApi.http 文件中:

    POST {{BookStoreApi_HostAddress}}/api/books
    Content-Type: application/json
    
    {
      //Book
    }
    
    ###
    
  10. 将 Book 注释替换为书籍对象作为 JSON 请求正文:

    POST {{BookStoreApi_HostAddress}}/api/books
    Content-Type: application/json
    
     {
       "Name": "The Pragmatic Programmer",
       "Price": 49.99,
       "Category": "Computers",
       "Author": "Andy Hunt"
     }
    
    ###
    
  11. 选择请求行上方的POST”链接。

    POST 请求将发送到应用,响应将显示在 “响应 ”窗格中。 响应应包含新创建的书籍及其分配的 ID。

  12. 最后,若要删除书籍,请/api/books/{id}, params (string id)右键单击 DELETE 终结点并选择“生成请求”。

    以下内容将追加到 BookStoreApi.http 该文件中:

    DELETE {{BookStoreApi_HostAddress}}/api/Books/{{id}}
    
    ###
    
  13. id 变量替换为前面请求返回的 ID 之一,然后单击“ 发送”请求。 例如:

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

向 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 的访问控制
  • 联盟网关

重要

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

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

其他资源

本教程创建对 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 命令 shell 实例:

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

    use BookStore
    

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

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

    db.createCollection('Books')
    

    显示以下结果:

    { "ok" : 1 }
    
  4. 使用以下命令定义 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 与命令行界面中显示的结果不一致。

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

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

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

创建 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 属性为:

    • 需要在将公共语言运行时 (CLR) 对象映射到 MongoDB 集合时使用。
    • 使用 [BsonId] 进行批注,以将此属性指定为文档的主键。
    • 使用 [BsonRepresentation(BsonType.ObjectId)] 进行注释,以允许将参数作为类型 string 而非 ObjectId 结构传递。 Mongo 处理从 stringObjectId 的转换。

    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 属性使用 BookStoreDatabase:ConnectionString 中的 appsettings.json 属性进行填充。

  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 实例。 使用此方法可访问在appsettings.json部分添加的 配置值。

  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) 返回表示集合的 MongoCollection 对象。 在本教程中,对集合调用以下方法:

  • 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 请求。
  • CreatedAtAction操作方法中调用Create,以返回HTTP 201响应。 状态代码 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 中,使用 BookName 特性批注 [JsonPropertyName] 属性:

    [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 的访问控制
  • 联盟网关

重要

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

有关详细信息,请参阅 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 命令 shell 实例:

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

    use BookStore
    

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

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

    db.createCollection('Books')
    

    显示以下结果:

    { "ok" : 1 }
    
  4. 使用以下命令定义 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 与命令行界面中显示的结果不一致。

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

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

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

创建 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 属性为:

    • 需要在将公共语言运行时 (CLR) 对象映射到 MongoDB 集合时使用。
    • 使用 [BsonId] 进行批注,以将此属性指定为文档的主键。
    • 使用 [BsonRepresentation(BsonType.ObjectId)] 进行注释,以允许将参数作为类型 string 而非 ObjectId 结构传递。 Mongo 处理从 stringObjectId 的转换。

    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 属性使用 BookStoreDatabase:ConnectionString 中的 appsettings.json 属性进行填充。

  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 实例。 使用此方法可访问在appsettings.json部分添加的 配置值。

  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) 返回表示集合的 MongoCollection 对象。 在本教程中,对集合调用以下方法:

  • 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 请求。
  • CreatedAtAction操作方法中调用Create,以返回HTTP 201响应。 状态代码 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 中,使用 BookName 特性批注 [JsonPropertyName] 属性:

    [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 的访问控制
  • 联盟网关

重要

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

有关详细信息,请参阅 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 命令 shell 实例:

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

    use BookStore
    

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

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

    db.createCollection('Books')
    

    显示以下结果:

    { "ok" : 1 }
    
  4. 使用以下命令定义 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 与命令行界面中显示的结果不一致。

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

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

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

创建 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 属性为:

    • 需要在将公共语言运行时 (CLR) 对象映射到 MongoDB 集合时使用。
    • 使用 [BsonId] 进行批注,以将此属性指定为文档的主键。
    • 使用 [BsonRepresentation(BsonType.ObjectId)] 进行注释,以允许将参数作为类型 string 而非 ObjectId 结构传递。 Mongo 处理从 stringObjectId 的转换。

    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 属性使用 BookStoreDatabase:ConnectionString 中的 appsettings.json 属性进行填充。

  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 实例。 使用此方法可访问在appsettings.json部分添加的 配置值。

  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) 返回表示集合的 MongoCollection 对象。 在本教程中,对集合调用以下方法:

  • 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 请求。
  • CreatedAtAction操作方法中调用Create,以返回HTTP 201响应。 状态代码 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 中,使用 BookName 特性批注 [JsonPropertyName] 属性:

    [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 的访问控制
  • 联盟网关

重要

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

有关详细信息,请参阅 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"
    }
    

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

数据库可供使用了。 你可以开始创建 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 属性为:

    • 需要在将公共语言运行时 (CLR) 对象映射到 MongoDB 集合时使用。
    • 使用 [BsonId] 进行批注,以将此属性指定为文档的主键。
    • 使用 [BsonRepresentation(BsonType.ObjectId)] 进行注释,以允许将参数作为类型 string 而非 ObjectId 结构传递。 Mongo 处理从 stringObjectId 的转换。

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

    在上述代码中:

    • 在依赖注入 (DI) 容器中注册的是与appsettings.json 文件的 BookstoreDatabaseSettings 部分绑定的配置实例。 例如,BookstoreDatabaseSettings 对象的 ConnectionString 属性使用 BookstoreDatabaseSettings:ConnectionString 中的 appsettings.json 属性进行填充。
    • 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 实例。 使用此方法可访问在appsettings.json部分添加的 配置值。

  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) 返回表示集合的 MongoCollection 对象。 在本教程中,对集合调用以下方法:

  • 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 请求。
  • CreatedAtRoute操作方法中调用Create,以返回HTTP 201响应。 状态代码 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 中,使用以下 BookName 特性批注 [JsonProperty] 属性:

    [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 的访问控制
  • 联盟网关

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

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

后续步骤

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