使用 ASP.NET Core 和 MongoDB 创建 Web API
注意
此版本不是本文的最新版本。 对于当前版本,请参阅此文的 .NET 8 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 对于当前版本,请参阅此文的 .NET 8 版本。
作者 Pratik Khandelwal 和 Scott Addie
本教程创建对 MongoDB NoSQL 数据库运行创建、读取、更新和删除 (CRUD) 操作的 Web API。
在本教程中,你将了解:
- 配置 MongoDB
- 创建 MongoDB 数据库
- 定义 MongoDB 集合和架构
- 从 Web API 执行 MongoDB CRUD 操作
- 自定义 JSON 序列化
先决条件
带有 ASP.NET 和 Web 开发工作负载的 Visual Studio 2022。
配置 MongoDB
从开发计算机上的任意位置启用 MongoDB 和 MongoDB Shell 访问 (Windows/Linux/macOS):
下载并安装 MongoDB Shell:
- macOS/Linux:选择要将 MongoDB Shell 提取到的目录。 将
mongosh
的结果路径添加到PATH
环境变量。 - Windows:MongoDB Shell (mongosh.exe) 安装在 C:\Users<user>\AppData\Local\Programs\mongosh。 将
mongosh.exe
的结果路径添加到PATH
环境变量。
- macOS/Linux:选择要将 MongoDB Shell 提取到的目录。 将
下载并安装 MongoDB:
- macOS/Linux:验证安装了 MongoDB 的目录,通常在 /usr/local/mongodb 中。 将
mongodb
的结果路径添加到PATH
环境变量。 - Windows:MongoDB 默认安装在 C:\Program Files\MongoDB。 将 C:\Program Files\MongoDB\Server\version_number>\bin 添加到
PATH
环境变量。
- macOS/Linux:验证安装了 MongoDB 的目录,通常在 /usr/local/mongodb 中。 将
选择数据存储目录:选择开发计算机上用于存储数据的目录。 创建目录(如果不存在)。 MongoDB Shell 不会创建新目录:
- macOS/Linux:例如
/usr/local/var/mongodb
。 - Windows:例如
C:\\BooksData
。
- macOS/Linux:例如
在 OS 命令行界面(而不是 MongoDB Shell)中,使用以下命令连接到默认端口 27017 上的 MongoDB。 将
<data_directory_path>
替换为上一步中选择的目录。mongod --dbpath <data_directory_path>
在以下步骤中使用之前安装的 MongoDB Shell 可以创建数据库、创建集合和存储文档。 有关 MongoDB Shell 命令的详细信息,请参阅 mongosh
。
通过启动
mongosh.exe
打开 MongoDB 命令行界面实例。在命令 shell 中,通过运行以下命令连接到默认的测试数据库:
mongosh
在命令行界面中运行以下命令:
use BookStore
如果它不存在,则创建名为 BookStore 的数据库。 如果该数据库存在,则将为事务打开其连接。
使用以下命令创建
Books
集合:db.createCollection('Books')
显示以下结果:
{ "ok" : 1 }
使用以下命令定义
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
与命令行界面中显示的结果不一致。使用以下命令查看数据库中的文档:
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 项目
转到“文件”“新建”“项目”。
选择“ASP.NET Core Web API”项目类型,然后选择“下一步”。
将项目命名为“BookStoreApi”,然后选择“下一步”。
选择“.NET 8.0 (长期支持)”框架,然后选择“创建”。
在“包管理器控制台”窗口中,导航到项目根。 运行以下命令以安装适用于 MongoDB 的 .NET 驱动程序:
Install-Package MongoDB.Driver
添加实体模型
将 Models 目录添加到项目根。
使用以下代码将
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
而非[BsonRepresentation(BsonType.ObjectId)]
结构传递。 Mongo 处理从string
到ObjectId
的转换。
BookName
属性使用[BsonElement]
特性进行批注。Name
的属性值表示 MongoDB 集合中的属性名称。
添加配置模型
向
appsettings.json
添加以下数据库配置值:{ "BookStoreDatabase": { "ConnectionString": "mongodb://localhost:27017", "DatabaseName": "BookStore", "BooksCollectionName": "Books" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
使用以下代码将
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# 具有相同的属性名称,目的是简化映射过程。将以下突出显示的代码添加到
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
属性进行填充。将以下代码添加到
Program.cs
的顶部以解析BookStoreDatabaseSettings
引用:using BookStoreApi.Models;
添加 CRUD 操作服务
将 Services 目录添加到项目根。
使用以下代码将
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
实例。 使用此方法可访问在添加配置模型部分中添加的 配置值。将以下突出显示的代码添加到
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
。将以下代码添加到
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
生成并运行应用。
导航到
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" } ]
导航到
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
返回。
为满足上述要求,请进行以下更改:
在
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
。在
Models/Book.cs
中,使用[JsonPropertyName]
特性批注BookName
属性:[BsonElement("Name")] [JsonPropertyName("Name")] public string BookName { get; set; } = null!;
[JsonPropertyName]
属性的Name
值表示 Web API 的序列化 JSON 响应中的属性名称。将以下代码添加到
Models/Book.cs
的顶部以解析[JsonProperty]
特性引用:using System.Text.Json.Serialization;
重复测试 Web API 部分中定义的步骤。 注意 JSON 属性名称中的区别。
向 Web API 添加身份验证支持
ASP.NET Core Identity 将用户界面 (UI) 登录功能添加到 ASP.NET Core Web 应用。 若要保护 Web API 和 SPA,请使用以下项之一:
- Microsoft Entra ID
- Azure Active Directory B2C (Azure AD B2C)
- Duende Identity Server
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 序列化
先决条件
带有 ASP.NET 和 Web 开发工作负载的 Visual Studio 2022。
配置 MongoDB
从开发计算机上的任意位置启用 MongoDB 和 Mongo DB Shell 访问:
在 Windows 上,MongoDB 将默认安装在 C:\Program Files\MongoDB 中。 将 C:\Program Files\MongoDB\Server\version_number>\bin 添加到
PATH
环境变量。下载 MongoDB Shell 并选择要将它提取到的目录。 将
mongosh.exe
的结果路径添加到PATH
环境变量。选择开发计算机上用于存储数据的目录。 例如,在 Windows 上为 C:\BooksData。 创建目录(如果不存在)。 mongo Shell 不会创建新目录。
在 OS 命令行界面(而不是 MongoDB Shell)中,使用以下命令连接到默认端口 27017 上的 MongoDB。 将
<data_directory_path>
替换为上一步中选择的目录。mongod --dbpath <data_directory_path>
在以下步骤中使用之前安装的 MongoDB Shell 可以创建数据库、创建集合和存储文档。 有关 MongoDB Shell 命令的详细信息,请参阅 mongosh
。
通过启动
mongosh.exe
打开 MongoDB 命令行界面实例。在命令行界面中,通过运行以下命令连接到默认的测试数据库:
mongosh
在命令行界面中运行以下命令:
use BookStore
如果它不存在,则创建名为 BookStore 的数据库。 如果该数据库存在,则将为事务打开其连接。
使用以下命令创建
Books
集合:db.createCollection('Books')
显示以下结果:
{ "ok" : 1 }
使用以下命令定义
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
与命令行界面中显示的结果不一致。使用以下命令查看数据库中的文档:
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 项目
转到“文件”“新建”“项目”。
选择“ASP.NET Core Web API”项目类型,然后选择“下一步”。
将项目命名为“BookStoreApi”,然后选择“下一步”。
选择“.NET 7.0 (标准期限支持)”框架,然后选择“创建”。
在“工具”菜单中,选择“NuGet 包管理器”>“包管理器控制台”。
在“包管理器控制台”窗口中,导航到项目根。 运行以下命令以安装适用于 MongoDB 的 .NET 驱动程序:
Install-Package MongoDB.Driver
添加实体模型
将 Models 目录添加到项目根。
使用以下代码将
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
而非[BsonRepresentation(BsonType.ObjectId)]
结构传递。 Mongo 处理从string
到ObjectId
的转换。
BookName
属性使用[BsonElement]
特性进行批注。Name
的属性值表示 MongoDB 集合中的属性名称。
添加配置模型
向
appsettings.json
添加以下数据库配置值:{ "BookStoreDatabase": { "ConnectionString": "mongodb://localhost:27017", "DatabaseName": "BookStore", "BooksCollectionName": "Books" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
使用以下代码将
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# 具有相同的属性名称,目的是简化映射过程。将以下突出显示的代码添加到
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
属性进行填充。将以下代码添加到
Program.cs
的顶部以解析BookStoreDatabaseSettings
引用:using BookStoreApi.Models;
添加 CRUD 操作服务
将 Services 目录添加到项目根。
使用以下代码将
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
实例。 使用此方法可访问在添加配置模型部分中添加的 配置值。将以下突出显示的代码添加到
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
。将以下代码添加到
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
生成并运行应用。
导航到
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" } ]
导航到
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
返回。
为满足上述要求,请进行以下更改:
在
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
。在
Models/Book.cs
中,使用[JsonPropertyName]
特性批注BookName
属性:[BsonElement("Name")] [JsonPropertyName("Name")] public string BookName { get; set; } = null!;
[JsonPropertyName]
属性的Name
值表示 Web API 的序列化 JSON 响应中的属性名称。将以下代码添加到
Models/Book.cs
的顶部以解析[JsonProperty]
特性引用:using System.Text.Json.Serialization;
重复测试 Web API 部分中定义的步骤。 注意 JSON 属性名称中的区别。
向 Web API 添加身份验证支持
ASP.NET Core Identity 将用户界面 (UI) 登录功能添加到 ASP.NET Core Web 应用。 若要保护 Web API 和 SPA,请使用以下项之一:
- Microsoft Entra ID
- Azure Active Directory B2C (Azure AD B2C)
- Duende Identity Server
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 序列化
先决条件
- 带有 ASP.NET 和 Web 开发工作负载的 Visual Studio 2022。
- .NET 6.0 SDK
配置 MongoDB
从开发计算机上的任意位置启用 MongoDB 和 Mongo DB Shell 访问:
在 Windows 上,MongoDB 将默认安装在 C:\Program Files\MongoDB 中。 将 C:\Program Files\MongoDB\Server\version_number>\bin 添加到
PATH
环境变量。下载 MongoDB Shell 并选择要将它提取到的目录。 将
mongosh.exe
的结果路径添加到PATH
环境变量。选择开发计算机上用于存储数据的目录。 例如,在 Windows 上为 C:\BooksData。 创建目录(如果不存在)。 mongo Shell 不会创建新目录。
在 OS 命令行界面(而不是 MongoDB Shell)中,使用以下命令连接到默认端口 27017 上的 MongoDB。 将
<data_directory_path>
替换为上一步中选择的目录。mongod --dbpath <data_directory_path>
在以下步骤中使用之前安装的 MongoDB Shell 可以创建数据库、创建集合和存储文档。 有关 MongoDB Shell 命令的详细信息,请参阅 mongosh
。
通过启动
mongosh.exe
打开 MongoDB 命令行界面实例。在命令行界面中,通过运行以下命令连接到默认的测试数据库:
mongosh
在命令行界面中运行以下命令:
use BookStore
如果它不存在,则创建名为 BookStore 的数据库。 如果该数据库存在,则将为事务打开其连接。
使用以下命令创建
Books
集合:db.createCollection('Books')
显示以下结果:
{ "ok" : 1 }
使用以下命令定义
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
与命令行界面中显示的结果不一致。使用以下命令查看数据库中的文档:
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 项目
转到“文件”“新建”“项目”。
选择“ASP.NET Core Web API”项目类型,然后选择“下一步”。
将项目命名为“BookStoreApi”,然后选择“下一步”。
选择“.NET 6.0(长期支持)”框架,然后选择“创建”。
在“包管理器控制台”窗口中,导航到项目根。 运行以下命令以安装适用于 MongoDB 的 .NET 驱动程序:
Install-Package MongoDB.Driver
添加实体模型
将 Models 目录添加到项目根。
使用以下代码将
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
而非[BsonRepresentation(BsonType.ObjectId)]
结构传递。 Mongo 处理从string
到ObjectId
的转换。
BookName
属性使用[BsonElement]
特性进行批注。Name
的属性值表示 MongoDB 集合中的属性名称。
添加配置模型
向
appsettings.json
添加以下数据库配置值:{ "BookStoreDatabase": { "ConnectionString": "mongodb://localhost:27017", "DatabaseName": "BookStore", "BooksCollectionName": "Books" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
使用以下代码将
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# 具有相同的属性名称,目的是简化映射过程。将以下突出显示的代码添加到
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
属性进行填充。将以下代码添加到
Program.cs
的顶部以解析BookStoreDatabaseSettings
引用:using BookStoreApi.Models;
添加 CRUD 操作服务
将 Services 目录添加到项目根。
使用以下代码将
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
实例。 使用此方法可访问在添加配置模型部分中添加的 配置值。将以下突出显示的代码添加到
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
。将以下代码添加到
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
生成并运行应用。
导航到
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" } ]
导航到
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
返回。
为满足上述要求,请进行以下更改:
在
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
。在
Models/Book.cs
中,使用[JsonPropertyName]
特性批注BookName
属性:[BsonElement("Name")] [JsonPropertyName("Name")] public string BookName { get; set; } = null!;
[JsonPropertyName]
属性的Name
值表示 Web API 的序列化 JSON 响应中的属性名称。将以下代码添加到
Models/Book.cs
的顶部以解析[JsonProperty]
特性引用:using System.Text.Json.Serialization;
重复测试 Web API 部分中定义的步骤。 注意 JSON 属性名称中的区别。
向 Web API 添加身份验证支持
ASP.NET Core Identity 将用户界面 (UI) 登录功能添加到 ASP.NET Core Web 应用。 若要保护 Web API 和 SPA,请使用以下项之一:
- Microsoft Entra ID
- Azure Active Directory B2C (Azure AD B2C)
- Duende Identity Server
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 序列化
先决条件
- .NET Core SDK 3.0 或更高版本
- Visual Studio 2019 与 ASP.NET 和 Web 开发工作负载
- MongoDB
配置 MongoDB
如果使用的是 Windows,MongoDB 将默认安装在 C:\Program Files\MongoDB 中。 将 C:\Program Files\MongoDB\Server\version_number>\bin 添加到 Path
环境变量。 通过此更改可以从开发计算机上的任意位置访问 MongoDB。
使用以下步骤中的 mongo Shell 可以创建数据库、创建集合和存储文档。 有关 mongo Shell 命令的详细信息,请参阅使用 mongo Shell。
选择开发计算机上用于存储数据的目录。 例如,在 Windows 上为 C:\BooksData。 创建目录(如果不存在)。 mongo Shell 不会创建新目录。
打开命令行界面。 运行以下命令以连接到默认端口 27017 上的 MongoDB。 请记得将
<data_directory_path>
替换为上一步中选择的目录。mongod --dbpath <data_directory_path>
打开另一个命令行界面实例。 通过运行以下命令来连接到默认测试数据库:
mongo
在命令行界面中运行以下命令:
use BookstoreDb
如果它不存在,则将创建名为“BookstoreDb”的数据库。 如果该数据库存在,则将为事务打开其连接。
使用以下命令创建
Books
集合:db.createCollection('Books')
显示以下结果:
{ "ok" : 1 }
使用以下命令定义
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 不匹配。
使用以下命令查看数据库中的文档:
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 项目
转到“文件”“新建”“项目”。
选择“ASP.NET Core Web 应用程序”项目类型,然后选择“下一步” 。
将项目命名为“BooksApi”,然后选择“创建”。
选择“.NET Core”目标框架和“ASP.NET Core 3.0”。 选择“API”项目模板,然后选择“创建” 。
访问 NuGet 库:MongoDB.Driver 来确定适用于 MongoDB 的 .NET 驱动程序的最新稳定版本。 在“包管理器控制台”窗口中,导航到项目根。 运行以下命令以安装适用于 MongoDB 的 .NET 驱动程序:
Install-Package MongoDB.Driver -Version {VERSION}
添加实体模型
将 Models 目录添加到项目根。
使用以下代码将
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
而非[BsonRepresentation(BsonType.ObjectId)]
结构传递。 Mongo 处理从string
到ObjectId
的转换。
BookName
属性使用[BsonElement]
特性进行批注。Name
的属性值表示 MongoDB 集合中的属性名称。
添加配置模型
向
appsettings.json
添加以下数据库配置值:{ "BookstoreDatabaseSettings": { "BooksCollectionName": "Books", "ConnectionString": "mongodb://localhost:27017", "DatabaseName": "BookstoreDb" }, "Logging": { "IncludeScopes": false, "Debug": { "LogLevel": { "Default": "Warning" } }, "Console": { "LogLevel": { "Default": "Warning" } } } }
使用以下代码将
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# 具有相同的属性名称,目的是简化映射过程。将以下突出显示的代码添加到
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
对象。
将以下代码添加到
Startup.cs
的顶部以解析BookstoreDatabaseSettings
和IBookstoreDatabaseSettings
引用:using BooksApi.Models;
添加 CRUD 操作服务
将 Services 目录添加到项目根。
使用以下代码将
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
实例。 使用此方法可访问在添加配置模型部分中添加的 配置值。将以下突出显示的代码添加到
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
。将以下代码添加到
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
生成并运行应用。
导航到
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" } ]
导航到
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
返回。
为满足上述要求,请进行以下更改:
Json.NET 已从 ASP.NET 共享框架中删除。 将包引用添加到
Microsoft.AspNetCore.Mvc.NewtonsoftJson
。在
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
。在
Models/Book.cs
中,使用以下[JsonProperty]
特性批注BookName
属性:[BsonElement("Name")] [JsonProperty("Name")] public string BookName { get; set; }
[JsonProperty]
属性的Name
值表示 Web API 的序列化 JSON 响应中的属性名称。将以下代码添加到
Models/Book.cs
的顶部以解析[JsonProperty]
特性引用:using Newtonsoft.Json;
重复测试 Web API 部分中定义的步骤。 注意 JSON 属性名称中的区别。
向 Web API 添加身份验证支持
ASP.NET Core Identity 将用户界面 (UI) 登录功能添加到 ASP.NET Core Web 应用。 若要保护 Web API 和 SPA,请使用以下项之一:
- Microsoft Entra ID
- Azure Active Directory B2C (Azure AD B2C)
- Duende IdentityServer。 Duende IdentityServer 是第三方产品。
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 的详细信息,请参阅以下资源:
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈