注意
此版本不是本文的最新版本。 有关当前版本,请参阅本文的 .NET 9 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 有关当前版本,请参阅本文的 .NET 9 版本。
作者 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" }
该架构将为每个文档添加类型
_id
的自动生成的ObjectId
属性。
创建 ASP.NET Core Web API 项目
- 转到“文件”>“新建”>“项目”。
- 选择“ASP.NET Core Web API”项目类型,然后选择“下一步”。
- 将项目命名为“BookStoreApi”,然后选择“下一步”。
- 在“其他信息”对话框中:
- 确认 Framework 为 .NET 9.0(标准期限支持)。
- 确认已选中“使用控制器”复选框。
- 确认已选中“启用 OpenAPI 支持”复选框。
- 选择 创建。
在“包管理器控制台”窗口中,导航到项目根。 运行以下命令以安装适用于 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
而非 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
属性使用BookStoreDatabase:ConnectionString
中的appsettings.json
属性进行填充。将以下代码添加到
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
实例。 使用此方法可访问在appsettings.json
部分添加的 配置值。将以下突出显示的代码添加到
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)
返回表示集合的 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
返回。
为满足上述要求,请进行以下更改:
在
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
中,使用BookName
特性批注[JsonPropertyName]
属性:[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
本教程使用终结点资源管理器和 .http 文件来测试 API。
生成并运行应用。
在 终结点资源管理器中,右键单击第一个 GET 终结点
/api/books
,然后选择“ 生成请求”。以下内容将添加到
BookStoreApi.http
文件。 如果这是首次生成请求,则会在项目根目录中创建该文件。@BookStoreApi_HostAddress = https://localhost:<port> GET {{BookStoreApi_HostAddress}}/api/books ###
端口号应已设置为应用使用的端口,例如
https://localhost:56874
。 如果不是这种情况,可以在启动应用时在输出窗口中找到端口号。选择新请求行上方的
GET
”链接。GET 请求将发送到应用,响应将显示在“响应”窗格中。
响应正文显示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" } ]
若要检索单个书籍,请在
/api/books/{id}, params (string id)
终结点资源管理器中右键单击 GET 终结点,然后选择“生成请求”。以下内容将追加到
BookStoreApi.http
该文件中:@id=string GET {{BookStoreApi_HostAddress}}/api/books/{{id}} ###
将变量替换为
id
先前请求返回的 ID 之一,例如:@id="61a6058e6c43f32854e51f52" GET {{BookStoreApi_HostAddress}}/api/books/{{id}} ###
选择新请求行上方的
GET
”链接。GET 请求将发送到应用,响应将显示在“响应”窗格中。
在响应正文中可以看到类似于以下内容的 JSON:
{ "Id": "61a6058e6c43f32854e51f52", "Name": "Clean Code", "Price": 43.15, "Category": "Computers", "Author": "Robert C. Martin" }
若要测试 POST 终结点,请
/api/books
右键单击 POST 终结点并选择“生成请求”。将以下内容添加到
BookStoreApi.http
文件中:POST {{BookStoreApi_HostAddress}}/api/books Content-Type: application/json { //Book } ###
将 Book 注释替换为书籍对象作为 JSON 请求正文:
POST {{BookStoreApi_HostAddress}}/api/books Content-Type: application/json { "Name": "The Pragmatic Programmer", "Price": 49.99, "Category": "Computers", "Author": "Andy Hunt" } ###
选择请求行上方的
POST
”链接。POST 请求将发送到应用,响应将显示在 “响应 ”窗格中。 响应应包含新创建的书籍及其分配的 ID。
最后,若要删除书籍,请
/api/books/{id}, params (string id)
右键单击 DELETE 终结点并选择“生成请求”。以下内容将追加到
BookStoreApi.http
该文件中:DELETE {{BookStoreApi_HostAddress}}/api/Books/{{id}} ###
将
id
变量替换为前面请求返回的 ID 之一,然后单击“ 发送”请求。 例如:DELETE {{BookStoreApi_HostAddress}}/api/Books/67f417517ce1b36aeab71236 ###
向 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 服务器
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 序列化
先决条件
带有 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" }
该架构将为每个文档添加类型
_id
的自动生成的ObjectId
属性。
创建 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
而非 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
属性使用BookStoreDatabase:ConnectionString
中的appsettings.json
属性进行填充。将以下代码添加到
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
实例。 使用此方法可访问在appsettings.json
部分添加的 配置值。将以下突出显示的代码添加到
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)
返回表示集合的 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
生成并运行应用。
导航到
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
中,使用BookName
特性批注[JsonPropertyName]
属性:[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 服务器
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 序列化
先决条件
带有 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 命令 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" }
该架构将为每个文档添加类型
_id
的自动生成的ObjectId
属性。
创建 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
而非 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
属性使用BookStoreDatabase:ConnectionString
中的appsettings.json
属性进行填充。将以下代码添加到
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
实例。 使用此方法可访问在appsettings.json
部分添加的 配置值。将以下突出显示的代码添加到
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)
返回表示集合的 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
生成并运行应用。
导航到
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
中,使用BookName
特性批注[JsonPropertyName]
属性:[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 服务器
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 序列化
先决条件
- 带有 ASP.NET 和 Web 开发工作负载的 Visual Studio 2022。
- .NET 6 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 命令 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" }
该架构将为每个文档添加类型
_id
的自动生成的ObjectId
属性。
创建 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
而非 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
属性使用BookStoreDatabase:ConnectionString
中的appsettings.json
属性进行填充。将以下代码添加到
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
实例。 使用此方法可访问在appsettings.json
部分添加的 配置值。将以下突出显示的代码添加到
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)
返回表示集合的 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
生成并运行应用。
导航到
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
中,使用BookName
特性批注[JsonPropertyName]
属性:[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 服务器
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 序列化
先决条件
- .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" }
该架构将为每个文档添加类型
_id
的自动生成的ObjectId
属性。
数据库可供使用了。 你可以开始创建 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
而非 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(); }
在上述代码中:
- 在依赖注入 (DI) 容器中注册的是与
appsettings.json
文件的BookstoreDatabaseSettings
部分绑定的配置实例。 例如,BookstoreDatabaseSettings
对象的ConnectionString
属性使用BookstoreDatabaseSettings:ConnectionString
中的appsettings.json
属性进行填充。 -
IBookstoreDatabaseSettings
接口使用单一实例服务生存期在 DI 中注册。 在注入时,接口实例将解析为BookstoreDatabaseSettings
对象。
- 在依赖注入 (DI) 容器中注册的是与
将以下代码添加到
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
实例。 使用此方法可访问在appsettings.json
部分添加的 配置值。将以下突出显示的代码添加到
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)
返回表示集合的 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
生成并运行应用。
导航到
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
中,使用以下BookName
特性批注[JsonProperty]
属性:[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 的访问控制
- 联盟网关
有关详细信息,请参阅 Duende IdentityServer 概述。
有关其他身份验证提供程序的详细信息,请参阅适用于 ASP.NET Core 的社区 OSS 身份验证选项
后续步骤
有关构建 ASP.NET Core Web API 的详细信息,请参阅以下资源: