![]() |
重情义的鸵鸟 · Python IDLE Shell ...· 1 年前 · |
![]() |
重情义的甘蔗 · visual studio 2017 ...· 1 年前 · |
![]() |
行走的豆芽 · Spark SQL case ...· 2 年前 · |
![]() |
空虚的南瓜 · 陶飞教授解读“数字孪生”· 2 年前 · |
![]() |
刀枪不入的开心果 · 菲渔民在南海发现一具无人潜航机 ...· 2 年前 · |
作者: Tim Deschryver 和 Rick Anderson
本教程介绍生成使用数据库的基于控制器的 Web API 的基础知识。 在 ASP.NET Core 中创建 API 的另一种方法是创建最小 API 。 有关在最小 API 和基于控制器的 API 之间进行选择的帮助,请参阅 API 概述 。 有关创建最小 API 的教程,请参阅 教程:使用 ASP.NET Core 创建最小 API 。
本教程将创建以下 API:
可以按照 macOS、Linux 或 Windows 上的 Visual Studio Code 指南进行操作。 如果使用 Visual Studio Code 以外的集成开发环境(IDE),可能需要更改。
必须添加 NuGet 包以支持本教程中使用的数据库。
Microsoft.EntityFrameworkCore.InMemory
。
dotnet new webapi --use-controllers -o TodoApi
cd TodoApi
dotnet add package Microsoft.EntityFrameworkCore.InMemory
code -r ../TodoApi
这些命令会:
- 创建新的 Web API 项目,并在 Visual Studio Code 中打开它。
- 添加下一部分所需的 NuGet 包。
- 在 Visual Studio Code 的当前实例中打开 TodoApi 文件夹。
Visual Studio Code 可能会显示一个对话框,询问:“你是否信任此文件夹中的文件作者?”
- 如果你信任父文件夹中的所有文件,请选择“信任父文件夹中所有文件的作者”。
- 选择“是,我信任所有作者”,因为项目文件夹包含 .NET 生成的文件。
- 当 Visual Studio Code 请求你添加资产来生成和调试项目时,请选择“是”。 如果 Visual Studio Code 不提供添加生成和调试资产的功能,请选择视图>命令面板,然后在搜索框中输入“
.NET
”。 从命令列表中选择 .NET: Generate Assets for Build and Debug
命令。
Visual Studio Code 添加一个包含生成的 launch.json
和 tasks.json
文件的 .vscode
文件夹。
如果你同意信任开发证书,请选择“是”。
有关信任 Firefox 浏览器的信息,请参阅 Firefox SEC_ERROR_INADEQUATE_KEY_USAGE 证书错误。
Visual Studio 启动一个终端窗口,并显示正在运行的应用的 URL。 API 托管在 https://localhost:<port>
,其中 <port>
是在项目创建时随机选择的端口号。
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:7260
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:7261
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
Ctrl+单击输出中的 HTTPS URL 以在浏览器中测试 Web 应用。 https://localhost:<port>
没有终结点,因此浏览器返回 HTTP 404 未找到。
将 /weatherforecast
附加到 URL,以测试 WeatherForecast API。
浏览器显示类似于以下示例的 JSON:
"date": "2025-07-16",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
"date": "2025-07-17",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
"date": "2025-07-18",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
"date": "2025-07-19",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
"date": "2025-07-20",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
前面的命令需要 Linux 上的 .NET 9 SDK 或更高版本。 对于 .NET 8.0.401 SDK 及更早版本上的 Linux,有关信任证书的信息,请参阅 Linux 发行版的文档。
如果证书以前不受信任,上述命令显示以下对话:
如果你同意信任开发证书,请选择“是”。
有关详细信息,请参阅强制实施 SSL 一文中的信任 ASP.NET Core HTTPS 开发证书部分。
有关信任 Firefox 浏览器的信息,请参阅 Firefox SEC_ERROR_INADEQUATE_KEY_USAGE 证书错误。
运行应用:
运行以下命令,在 https
配置文件上启动应用:
dotnet run --launch-profile https
输出会显示类似于以下内容的消息,表示应用正在运行且正在等待请求:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:{port}
Ctrl+单击输出中的 HTTPS URL 以在浏览器中测试 Web 应用。
默认浏览器会启动到 https://localhost:<port>
,其中 <port>
是输出中显示的随机选择的端口号。 https://localhost:<port>
没有终结点,因此浏览器返回 HTTP 404 未找到。
将 /weatherforecast
附加到 URL,以测试 WeatherForecast API。 浏览器显示类似于以下示例的 JSON:
"date": "2025-07-16",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
"date": "2025-07-17",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
"date": "2025-07-18",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
"date": "2025-07-19",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
"date": "2025-07-20",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
- 按照以下说明测试完 Web 应用后,在集成终端中按 Ctrl+C 将其关闭。
Visual Studio
Visual Studio Code
使用 Swagger 创建 API 测试 UI
有许多可用的 Web API 测试工具可供选择,你可以使用首选工具遵循本教程介绍的 API 测试步骤。
本教程利用 .NET 包 NSwag.AspNetCore,它集成了 Swagger 工具,可用于生成遵循 OpenAPI 规范的测试 UI:
- NSwag:将 Swagger 直接集成到 ASP.NET Core 应用程序中的 .NET 库,提供了中间件和配置。
- Swagger:一组开放源代码工具(如 OpenAPIGenerator 和 SwaggerUI),用于生成遵循 OpenAPI 规范的 API 测试页。
- OpenAPI 规范:基于控制器和模型中的 XML 和属性注释,描述 API 功能的文档。
有关将 OpenAPI 和 NSwag 与 ASP.NET 配合使用的详细信息,请参阅使用 Swagger / OpenAPI 的 ASP.NET Core Web API 文档。
安装 Swagger 工具
运行以下命令:
dotnet add package NSwag.AspNetCore
上一个命令添加了 NSwag.AspNetCore 包,其中包含用于生成 Swagger 文档和 UI 的工具。
因为我们的项目使用 OpenAPI,所以我们只使用 NSwag 包来生成 Swagger UI。
配置 Swagger 中间件
- 在
Program.cs
中,添加以下突出显示的代码:
var app = builder.Build();
if (app.Environment.IsDevelopment())
app.MapOpenApi();
app.UseSwaggerUi(options =>
options.DocumentPath = "/openapi/v1.json";
前面的代码使 Swagger 中间件能够使用 Swagger UI 为生成的 JSON 文档提供服务。 Swagger 仅在开发环境中启用。 在生产环境中启用 Swagger 可能会公开有关 API 结构和实现的潜在敏感详细信息。
该应用使用 OpenAPI 生成的 OpenAPI 文档(位于 /openapi/v1.json
)来生成 UI。
在项目运行时,通过在浏览器中导航到 https://localhost:<port>/openapi/v1.json
,查看为 WeatherForecast
API 生成的 OpenAPI 规范。
OpenAPI 规范是一个 JSON 格式的文档,它描述了 API 的结构和功能,包括终结点、请求/响应格式、参数等。 它本质上是 API 的蓝图,各种工具可以使用它来理解 API 并与之交互。
添加模型类
模型是一组表示应用管理的数据的类。 此应用的模型是 TodoItem
类。
Visual Studio
Visual Studio Code
- 在“解决方案资源管理器”中,右键单击项目。 选择添加>新建文件夹。 将该文件夹命名为
Models
注册一个免费试用帐户。
- 右键单击
Models
文件夹,然后选择添加>类。 将类命名为 TodoItem,然后选择“添加”。
- 将模板代码替换为以下内容:
namespace TodoApi.Models;
public class TodoItem
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public class TodoContext : DbContext
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
public DbSet<TodoItem> TodoItems { get; set; } = null!;
public class TodoContext : DbContext
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
public DbSet<TodoItem> TodoItems { get; set; } = null!;
注册数据库上下文
在 ASP.NET Core 中,服务(如数据库上下文)必须向依赖关系注入 (DI) 容器进行注册。 该容器向控制器提供服务。
使用以下突出显示的代码更新 Program.cs
:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddOpenApi();
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
var app = builder.Build();
if (app.Environment.IsDevelopment())
app.MapOpenApi();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
前面的代码:
- 添加
using
指令。
- 将数据库上下文添加到 DI 容器。
- 指定数据库上下文将使用内存中数据库。
构建控制器
Visual Studio
Visual Studio Code
- 在“模型类”中选择“TodoItem (TodoApi.Models)”。
- 在“数据上下文类”中选择“TodoContext (TodoAPI.Models)”。
- 选择“添加”。
如果基架操作失败,请选择“添加”以第二次尝试使用基架。
此步骤将 Microsoft.VisualStudio.Web.CodeGeneration.Design
和 Microsoft.EntityFrameworkCore.Tools
NuGet 包添加到项目中。
这些包是基架所需的。
请确保目前所有更改均已保存。
- 右键单击(或在 macOS 中使用 Command 单击)TodoAPI 项目,然后选择在终端中打开。 终端将在
TodoAPI
项目文件夹中打开。
运行以下命令:
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet tool uninstall -g dotnet-aspnet-codegenerator
dotnet tool install -g dotnet-aspnet-codegenerator
dotnet tool update -g dotnet-aspnet-codegenerator
前面的命令:
- 添加构建所需的 NuGet 包。
- 卸载任何可能的早期版本后,安装基架引擎 (
dotnet-aspnet-codegenerator
)。
对于 Linux,使用以下命令将 .NET 工具目录添加到系统路径:
echo 'export PATH=$HOME/.dotnet/tools:$PATH' >> ~/.bashrc
source ~/.bashrc
默认情况下,要安装的 .NET 二进制文件的体系结构表示当前运行的 OS 体系结构。 若要指定不同的 OS 体系结构,请参阅 dotnet tool install, --arch option。
有关详细信息,请参阅 GitHub 问题 dotnet/AspNetCore.Docs #29262。
生成项目。
运行以下命令:
dotnet aspnet-codegenerator controller -name TodoItemsController -async -api -m TodoItem -dc TodoContext -outDir Controllers
上述命令为 TodoItemsController
创建基架。
生成的代码:
- 使用
[ApiController]
属性标记类。 此属性指示控制器响应 Web API 请求。 有关该属性启用的特定行为的信息,请参阅使用 ASP.NET Core 创建 Web API。
- 使用 DI 将数据库上下文 (
TodoContext
) 注入到控制器中。 数据库上下文将在控制器中的每个 CRUD 方法中使用。
ASP.NET Core 模板:
- 具有视图的控制器在路由模板中包含
[action]
。
- API 控制器不在路由模板中包含
[action]
。
[action]
令牌不在路由模板中时,终结点中不包含 action 名称(方法名称)。 也就是说,不会在匹配的路由中使用操作的关联方法名称。
更新 PostTodoItem create 方法
更新 PostTodoItem
中的 return 语句,以使用 nameof 运算符:
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
上述代码是 HTTP POST
方法,如 [HttpPost]
属性所指示。 此方法从 HTTP 请求正文获取 TodoItem
的值。
有关详细信息,请参阅使用 Http [Verb] 特性的特性路由。
CreatedAtAction 方法:
- 如果成功,将返回 HTTP 201 状态代码。
HTTP 201
是在服务器上创建新资源的 HTTP POST
方法的标准响应。
- 向响应添加位置标头。
Location
标头指定新建的待办事项的 URI。 有关详细信息,请参阅创建的 10.2.2 201。
- 引用
GetTodoItem
操作以创建 Location
标头的 URI。 C# nameof
关键字用于避免在 CreatedAtAction
调用中硬编码操作名称。
测试 PostTodoItem
Visual Studio
Visual Studio Code
在名为 TodoApi.http
的项目文件夹中创建一个新文件,其内容类似于以下示例:
@TodoApi_HostAddress = https://localhost:49738
POST {{TodoApi_HostAddress}}/api/todoitems
Content-Type: application/json
//TodoItem
- 第一行创建了一个变量,该变量适用于所有终结点。
- 下一行定义了 POST 请求。
- POST 请求行后面的行会定义标头以及请求正文的占位符。
- 三重井号标签 (
###
) 行是请求分隔符:对于不同的请求,该标签之后的内容属于另一个请求。
POST 请求需要 TodoItem
。 要定义待办事项,请将 //TodoItem
注释替换为以下 JSON:
"name": "walk dog",
"isComplete": true
TodoApi.http 文件现在应如以下示例所示,但带有端口号:
@TodoApi_HostAddress = https://localhost:7260
Post {{TodoApi_HostAddress}}/api/todoitems
Content-Type: application/json
"name": "walk dog",
"isComplete": true
运行应用。
选择 请求行上方的“发送请求”POST
链接。
POST 请求将发送到应用,响应将显示在“响应”窗格中。
当应用仍在运行时,在浏览器中导航到 https://localhost:<port>/swagger
以显示 Swagger 生成的 API 测试页。 单击 TodoItems 展开操作。
在 Swagger API 测试页面上,选择 Post /api/todoitems>试用。
请注意,“请求正文”字段包含了反映 API 参数的生成的示例格式。
在请求正文中,输入待办项的 JSON,无需指定可选的 id
:
"name": "walk dog",
"isComplete": true
选择“执行”。
Swagger在“执行”按钮下方提供“响应”窗格。
请注意一些有用的详细信息:
- cURL:Swagger 在 Unix/Linux 语法中提供了一个示例 cURL 命令,可以通过任何使用 Unix/Linux 语法的 bash shell(包括 Git for Windows 中的 Git Bash)在命令行上运行。
- 请求 URL:Swagger UI 针对 API 调用的 JavaScript 代码发出的 HTTP 请求的简化表示形式。 实际请求可以包括标头和查询参数以及请求正文等详细信息。
- 服务器响应:包括响应正文和标头。 响应正文显示
id
已设置为 1
。
- 响应代码:返回了 201
HTTP
状态代码,指示请求已成功处理并导致创建新资源。
测试位置标头 URI
Visual Studio
Visual Studio Code
通过从浏览器调用 GET
终结点或使用 终结点探查器来测试应用。 以下步骤适用于终结点资源管理器。
在“终结点资源管理器”中,右键单击第一个 GET 终结点,然后选择“生成请求”。
将以下内容添加到 TodoApi.http
文件中:
GET {{TodoApi_HostAddress}}/api/todoitems
选择新的 请求行上方的“发送请求”GET
链接。
GET 请求将发送到应用,响应将显示在“响应”窗格中。
响应正文与以下 JSON 类似:
"id": 1,
"name": "walk dog",
"isComplete": true
在终结点资源管理器中,右键单击/api/todoitems/{id}
GET终结点,然后选择生成请求。
将以下内容添加到 TodoApi.http
文件中:
@id=0
GET {{TodoApi_HostAddress}}/api/todoitems/{{id}}
将 {@id}
分配给 1
(而不是 0
)。
选择新的 GET 请求行上方的“发送请求”链接。
GET 请求将发送到应用,响应将显示在“响应”窗格中。
响应正文与以下 JSON 类似:
"id": 1,
"name": "walk dog",
"isComplete": true
在 Swagger 中,选择 GET /api/todoitems>试用>执行。
或者,通过输入 URI https://localhost:<port>/api/todoitems
从浏览器调用 GET /api/todoitems。 例如: https://localhost:7260/api/todoitems
对 GET /api/todoitems
的调用生成如下响应:
"id": 1,
"name": "walk dog",
"isComplete": true
在 Swagger 中调用 GET /api/todoitems/{id} 以从特定 id 返回数据:
- 选择 GET /api/todoitems>试用。
- 将 id 字段设置为
1
并选择执行。
或者,通过输入 URI https://localhost:<port>/api/todoitems/1
从浏览器调用 GET /api/todoitems。 例如: https://localhost:7260/api/todoitems/1
响应类似于以下内容:
"id": 1,
"name": "walk dog",
"isComplete": true
上一节展示了 /api/todoitems/{id}
路由的示例。
按照 POST 说明添加另一待办事项,然后使用 Swagger 测试 /api/todoitems
路由。
此应用使用内存中数据库。 如果停止并启动应用,则前面的 GET 请求不会返回任何数据。 如果未返回任何数据,将数据 POST 到应用。
路由和 URL 路径
[HttpGet]
属性表示响应 HTTP GET
请求的方法。 每个方法的 URL 路径构造如下所示:
在控制器的 Route
属性中以模板字符串开头:
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
将 [controller]
替换为控制器的名称,按照惯例,在控制器类名称中去掉“Controller”后缀。 对于此示例,控制器类名称为“TodoItems”控制器,因此控制器名称为“TodoItems”。 ASP.NET Core 路由不区分大小写。
如果 [HttpGet]
属性具有路由模板(例如 [HttpGet("products")]
),则将它追加到路径。 此示例不使用模板。 有关详细信息,请参阅使用 Http [Verb] 特性的特性路由。
在下面的 GetTodoItem
方法中,"{id}"
是待办事项的唯一标识符的占位符变量。 调用 GetTodoItem
时,URL 中 "{id}"
的值会在 id
参数中提供给方法。
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
return NotFound();
return todoItem;
GetTodoItems
和 GetTodoItem
方法的返回类型是 ActionResult<T> 类型。 ASP.NET Core 自动将对象序列化为 JSON,并将 JSON 写入响应消息的正文中。 此返回类型的响应代码为 200 OK(假设没有未处理的异常)。 未经处理的异常将转换为 5xx 错误。
ActionResult
返回类型可以表示大范围的 HTTP 状态代码。 例如,GetTodoItem
可以返回两个不同的状态值:
- 如果没有任何项与请求的 ID 匹配,该方法将返回 404 状态NotFound错误代码。
- 否则,此方法将返回具有 JSON 响应正文的 200。 返回
item
则产生 HTTP 200
响应。
PutTodoItem 方法
检查 PutTodoItem
方法:
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
if (id != todoItem.Id)
return BadRequest();
_context.Entry(todoItem).State = EntityState.Modified;
await _context.SaveChangesAsync();
catch (DbUpdateConcurrencyException)
if (!TodoItemExists(id))
return NotFound();
throw;
return NoContent();
PutTodoItem
与 PostTodoItem
类似,但使用的是 HTTP PUT
。 响应是 204(无内容)。 根据 HTTP 规范,PUT
请求需要客户端发送整个更新的实体,而不仅仅是更改。 若要支持部分更新,请使用 HTTP PATCH。
测试 PutTodoItem 方法
本示例使用内存内、数据库,每次启动应用时都必须对其进行初始化。 在进行 PUT 调用之前,数据库中必须有一个项。 调用 GET,以确保在调用 PUT 之前数据库中存在项。
使用 PUT
方法更新 Id=1 的 TodoItem
,并将其名称设置为 "feed fish"
。 请注意,响应为 HTTP 204 No Content
。
Visual Studio
Visual Studio Code
在“终结点资源管理器”中,右键单击 PUT 终结点,然后选择“生成请求”。
将以下内容添加到 TodoApi.http
文件中:
PUT {{TodoApi_HostAddress}}/api/todoitems/{{id}}
Content-Type: application/json
//TodoItem
在 PUT 请求行中,将 {{id}}
替换为 1
。
将 //TodoItem
占位符替换为以下行:
PUT {{TodoApi_HostAddress}}/api/todoitems/1
Content-Type: application/json
"id": 1,
"name": "feed fish",
"isComplete": false
选择新的 PUT 请求行上方的“发送请求”链接。
PUT 请求将发送到应用,响应将显示在“响应”窗格中。 响应正文为空,状态代码为 204。
检查 DeleteTodoItem
方法:
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
return NotFound();
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
测试 DeleteTodoItem 方法
使用 DELETE
方法删除 Id = 1 的 TodoItem
。 请注意,响应为 HTTP 204 No Content
。
Visual Studio
Visual Studio Code
将 DELETE 请求行中的 {{id}}
替换为 1
。 DELETE 请求应如以下示例所示:
DELETE {{TodoApi_HostAddress}}/api/todoitems/{{id}}
选择 DELETE 请求的“发送请求”链接。
DELETE 请求将发送到应用,响应将显示在“响应”窗格中。 响应正文为空,状态代码为 204。
防止过度发布
目前,示例应用公开了整个 TodoItem
对象。 生产应用通常使用模型的子集来限制输入和返回的数据。 这背后有多种原因,但安全性是主要原因。 模型的子集通常称为数据传输对象 (DTO)、输入模型或视图模型。 本教程使用了 DTO。
DTO 可用于:
- 防止过度发布。
- 隐藏客户端不应查看的属性。
- 省略一些属性以缩减有效负载大小。
- 平展包含嵌套对象的对象图。 对客户端而言,平展的对象图可能更方便。
若要演示 DTO 方法,请更新 TodoItem
类,使其包含机密字段:
namespace TodoApi.Models;
public class TodoItem
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
此应用需要隐藏机密字段,但管理应用可以选择公开它。
确保可以发布和获取机密字段。
在 Models/TodoItemsDTO.cs 文件中创建 DTO 模型:
namespace TodoApi.Models;
public class TodoItemDTO
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
更新 TodoItemsController
以使用 TodoItemDTO
:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
_context = context;
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
// GET: api/TodoItems/5
// <snippet_GetByID>
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
return NotFound();
return ItemToDTO(todoItem);
// </snippet_GetByID>
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Update>
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
if (id != todoDTO.Id)
return BadRequest();
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
return NotFound();
todoItem.Name = todoDTO.Name;
todoItem.IsComplete = todoDTO.IsComplete;
await _context.SaveChangesAsync();
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
return NotFound();
return NoContent();
// </snippet_Update>
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Create>
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
var todoItem = new TodoItem
IsComplete = todoDTO.IsComplete,
Name = todoDTO.Name
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
// </snippet_Create>
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
return NotFound();
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
private bool TodoItemExists(long id)
return _context.TodoItems.Any(e => e.Id == id);
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
确保无法发布或获取机密字段。
使用 JavaScript 调用 Web API
请参阅教程:使用 JavaScript 调用 ASP.NET Core Web API。
Web API 视频系列
请参阅视频:初学者系列:Web API。
企业 Web 应用模式
有关创建可靠、安全、高性能、可测试且可缩放的 ASP.NET Core 应用的指导,请参阅 企业 Web 应用模式。 提供了一个完整且具备生产质量的示例 Web 应用,该应用实现了这些模式。
向 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 网站)。
发布到 Azure
有关部署到 Azure 的信息,请参阅快速入门:部署 ASP.NET Web 应用。
查看或下载本教程的示例代码。 请参阅如何下载。
有关详细信息,请参阅以下资源:
- 使用 ASP.NET Core 创建 Web API
- 教程:使用 ASP.NET Core 创建最小 API
- 使用生成的 OpenAPI 文档
- 带有 Swagger/OpenAPI 的 ASP.NET Core Web API 文档
- Razor ASP.NET Core 中带 Entity Framework Core 的页面 - 第 1 个教程(共 8 个)
- 在 ASP.NET Core 中路由到控制器操作
- ASP.NET Core Web API 中控制器操作的返回类型
- 将 ASP.NET Core 应用部署到 Azure 应用程序服务
- 托管和部署 ASP.NET Core
- 使用 ASP.NET Core 创建 Web API
本教程介绍生成使用数据库的基于控制器的 Web API 的基础知识。 在 ASP.NET Core 中创建 API 的另一种方法是创建最小 API。 有关在最小 API 和基于控制器的 API 之间进行选择的帮助,请参阅 API 概述。 有关创建最小 API 的教程,请参阅教程:使用 ASP.NET Core 创建最小 API。
本教程将创建以下 API:
Microsoft 已宣布停用 Visual Studio for Mac。 从 2024 年 8 月 31 日起,将不再支持 Visual Studio for Mac。 替代方案包括:
- 带有 C# 开发工具包 和相关扩展(如 .NET MAUI 和 Unity)的 Visual Studio Code。
- 在 Mac 虚拟机中的 Windows 上运行的 Visual Studio IDE。
- 在云虚拟机中的 Windows 上运行的 Visual Studio IDE。
有关详细信息,请参阅 Visual Studio for Mac 停用公告。
- 在搜索框中输入“Web API”。
- 选择“ASP.NET Core Web API”模板,然后选择“下一步”。
- 在“配置新项目”对话框中,将项目命名为“TodoApi”,然后选择“下一步”。
- 在“其他信息”对话框中:
- 确认“框架”为“.NET 8.0 (长期支持)”。
- 确认已选中“使用控制器(取消选中以使用最小 API)”复选框。
- 确认已选中“启用 OpenAPI 支持”复选框。
- 选择“创建”。
添加 NuGet 包
必须添加 NuGet 包以支持本教程中使用的数据库。
- 在“工具”菜单中,选择“NuGet 包管理器”>“管理解决方案的 NuGet 包”。
- 选择“浏览”选项卡。
- 在搜索框中输入“Microsoft.EntityFrameworkCore.InMemory”,然后选择
Microsoft.EntityFrameworkCore.InMemory
。
- 选中右窗格中的“项目”复选框,然后选择“安装”。
dotnet new webapi --use-controllers -o TodoApi
cd TodoApi
dotnet add package Microsoft.EntityFrameworkCore.InMemory
code -r ../TodoApi
这些命令会:
- 创建新的 Web API 项目,并在 Visual Studio Code 中打开它。
- 添加下一部分所需的 NuGet 包。
- 在 Visual Studio Code 的当前实例中打开 TodoApi 文件夹。
Visual Studio Code 可能会显示一个对话框,询问:“你是否信任此文件夹中的文件作者?”
- 如果你信任父文件夹中的所有文件,请选择“信任父文件夹中所有文件的作者”。
- 选择“是,我信任所有作者”,因为项目文件夹包含 .NET 生成的文件。
- 当 Visual Studio Code 请求你添加资产来生成和调试项目时,请选择“是”。 如果 Visual Studio Code 不提供添加生成和调试资产的功能,请选择视图>命令面板,然后在搜索框中输入“
.NET
”。 从命令列表中选择 .NET: Generate Assets for Build and Debug
命令。
Visual Studio Code 添加一个包含生成的 launch.json
和 tasks.json
文件的 .vscode
文件夹。
- 在 Visual Studio for Mac 2022 工具栏中,选择项目>管理 NuGet 包...。
- 在搜索框中,输入 Microsoft.EntityFrameworkCore.InMemory。
- 在结果窗口中,选中
Microsoft.EntityFrameworkCore.InMemory
。
- 选择“添加包”
- 在“选择项目”窗口中,选择“确定”。
- 在“许可协议”窗口中,选择“同意”。
前面的命令需要 Linux 上的 .NET 9 SDK 或更高版本。 对于 .NET 8.0.401 SDK 及更早版本上的 Linux,有关信任证书的信息,请参阅 Linux 发行版的文档。
如果证书以前不受信任,上述命令显示以下对话:
如果你同意信任开发证书,请选择“是”。
有关详细信息,请参阅强制实施 SSL 一文中的信任 ASP.NET Core HTTPS 开发证书部分。
有关信任 Firefox 浏览器的信息,请参阅 Firefox SEC_ERROR_INADEQUATE_KEY_USAGE 证书错误。
运行应用:
运行以下命令,在 https
配置文件上启动应用:
dotnet run --launch-profile https
输出会显示类似于以下内容的消息,表示应用正在运行且正在等待请求:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:{port}
Ctrl+单击输出中的 HTTPS URL 以在浏览器中测试 Web 应用。
默认浏览器会启动到 https://localhost:<port>/swagger/index.html
,其中 <port>
是输出中显示的随机选择的端口号。 https://localhost:<port>
没有终结点,因此浏览器返回 HTTP 404 未找到。 将 /swagger
附加到 URL https://localhost:<port>/swagger
。
按照以下说明测试 Web 应用后,在集成终端中按 Ctrl+C 来将其关闭。
选择调试>开始调试以启动应用。 Visual Studio for Mac 将启动一个浏览器并导航到 https://localhost:<port>/swagger/index.html
,其中 <port>
是在创建项目时设置的一个随机选择的端口号。
随即显示 Swagger 页面 /swagger/index.html
。 选择 GET>试用>执行。 页面将显示:
- 用于测试 WeatherForecast API 的 Curl 命令。
- 用于测试 WeatherForecast API 的 URL。
- 响应代码、正文和标头。
- 包含媒体类型、示例值和架构的下拉列表框。
如果 Swagger 页面未显示,请参阅此 GitHub 问题。
Swagger 用于为 Web API 生成有用的文档和帮助页面。 本教程使用 Swagger 测试应用。 有关 Swagger 的详细信息,请参阅包含 Swagger / OpenAPI 的 ASP.NET Core Web API 文档。
将请求 URL 复制粘贴到浏览器中:https://localhost:<port>/weatherforecast
返回类似于以下示例的 JSON:
"date": "2019-07-16T19:04:05.7257911-06:00",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
"date": "2019-07-17T19:04:05.7258461-06:00",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
"date": "2019-07-18T19:04:05.7258467-06:00",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
"date": "2019-07-19T19:04:05.7258471-06:00",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
"date": "2019-07-20T19:04:05.7258474-06:00",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
添加模型类
模型是一组表示应用管理的数据的类。 此应用的模型是 TodoItem
类。
Visual Studio
Visual Studio Code
Visual Studio for Mac
- 在“解决方案资源管理器”中,右键单击项目。 选择添加>新建文件夹。 将该文件夹命名为
Models
注册一个免费试用帐户。
- 右键单击
Models
文件夹,然后选择添加>类。 将类命名为 TodoItem,然后选择“添加”。
- 将模板代码替换为以下内容:
按 Control 并单击 TodoAPI 项目,然后选择添加>新建文件夹。 将该文件夹命名为 Models
注册一个免费试用帐户。
按 Control 并单击 Models
文件夹,然后选择添加>新建类...>常规>空类。
将类命名为“TodoItem”,然后选择“创建”。
将模板代码替换为以下内容:
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
Id
属性用作关系数据库中的唯一键。
模型类可位于项目的任意位置,但按照惯例会使用 Models
文件夹。
添加数据库上下文
数据库上下文是为数据模型协调 Entity Framework 功能的主类。 此类由 Microsoft.EntityFrameworkCore.DbContext 类派生而来。
Visual Studio
Visual Studio Code / Visual Studio for Mac
public class TodoContext : DbContext
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
public DbSet<TodoItem> TodoItems { get; set; } = null!;
注册数据库上下文
在 ASP.NET Core 中,服务(如数据库上下文)必须向依赖关系注入 (DI) 容器进行注册。 该容器向控制器提供服务。
使用以下突出显示的代码更新 Program.cs
:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
前面的代码:
- 添加
using
指令。
- 将数据库上下文添加到 DI 容器。
- 指定数据库上下文将使用内存中数据库。
构建控制器
Visual Studio
Visual Studio Code / Visual Studio for Mac
- 在“模型类”中选择“TodoItem (TodoApi.Models)”。
- 在“数据上下文类”中选择“TodoContext (TodoAPI.Models)”。
- 选择“添加”。
如果基架操作失败,请选择“添加”以第二次尝试使用基架。
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet tool uninstall -g dotnet-aspnet-codegenerator
dotnet tool install -g dotnet-aspnet-codegenerator
dotnet tool update -g dotnet-aspnet-codegenerator
前面的命令:
- 添加构建所需的 NuGet 包。
- 卸载任何可能的早期版本后,安装基架引擎 (
dotnet-aspnet-codegenerator
)。
对于 Linux,使用以下命令将 .NET 工具目录添加到系统路径:
echo 'export PATH=$HOME/.dotnet/tools:$PATH' >> ~/.bashrc
source ~/.bashrc
默认情况下,要安装的 .NET 二进制文件的体系结构表示当前运行的 OS 体系结构。 若要指定不同的 OS 体系结构,请参阅 dotnet tool install, --arch option。
有关详细信息,请参阅 GitHub 问题 dotnet/AspNetCore.Docs #29262。
生成项目。
运行以下命令:
dotnet aspnet-codegenerator controller -name TodoItemsController -async -api -m TodoItem -dc TodoContext -outDir Controllers
上述命令为 TodoItemsController
创建基架。
生成的代码:
- 使用
[ApiController]
属性标记类。 此属性指示控制器响应 Web API 请求。 有关该属性启用的特定行为的信息,请参阅使用 ASP.NET Core 创建 Web API。
- 使用 DI 将数据库上下文 (
TodoContext
) 注入到控制器中。 数据库上下文将在控制器中的每个 CRUD 方法中使用。
ASP.NET Core 模板:
- 具有视图的控制器在路由模板中包含
[action]
。
- API 控制器不在路由模板中包含
[action]
。
[action]
令牌不在路由模板中时,终结点中不包含 action 名称(方法名称)。 也就是说,不会在匹配的路由中使用操作的关联方法名称。
更新 PostTodoItem create 方法
更新 PostTodoItem
中的 return 语句,以使用 nameof 运算符:
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
上述代码是 HTTP POST
方法,如 [HttpPost]
属性所指示。 此方法从 HTTP 请求正文获取 TodoItem
的值。
有关详细信息,请参阅使用 Http [Verb] 特性的特性路由。
CreatedAtAction 方法:
- 如果成功,将返回 HTTP 201 状态代码。
HTTP 201
是在服务器上创建新资源的 HTTP POST
方法的标准响应。
- 向响应添加位置标头。
Location
标头指定新建的待办事项的 URI。 有关详细信息,请参阅创建的 10.2.2 201。
- 引用
GetTodoItem
操作以创建 Location
标头的 URI。 C# nameof
关键字用于避免在 CreatedAtAction
调用中硬编码操作名称。
测试 PostTodoItem
按 Ctrl+F5 运行应用。
在 Swagger 浏览器窗口中,选择“POST /api/TodoItems”,然后选择“试用”。
在请求正文输入窗口中,更新 JSON。 例如,
"name": "walk dog",
"isComplete": true
选择“执行”
测试位置标头 URI
在上述 POST 中,Swagger UI 在“响应标头”下显示了位置标头。 例如,location: https://localhost:7260/api/TodoItems/1
。 位置标头显示创建资源的 URI。
测试位置标头:
在 Swagger 浏览器窗口中,选择“GET /api/TodoItems/{id}”,然后选择“试用”。
在 id
输入框中输入 1
,然后选择“执行”。
检查 GET 方法
实现了两个 GET 终结点:
GET /api/todoitems
GET /api/todoitems/{id}
上一节展示了 /api/todoitems/{id}
路由的示例。
按照 POST 说明添加另一待办事项,然后使用 Swagger 测试 /api/todoitems
路由。
此应用使用内存中数据库。 如果停止并启动应用,则前面的 GET 请求不会返回任何数据。 如果未返回任何数据,将数据 POST 到应用。
路由和 URL 路径
[HttpGet]
属性表示响应 HTTP GET
请求的方法。 每个方法的 URL 路径构造如下所示:
在控制器的 Route
属性中以模板字符串开头:
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
将 [controller]
替换为控制器的名称,按照惯例,在控制器类名称中去掉“Controller”后缀。 对于此示例,控制器类名称为“TodoItems”控制器,因此控制器名称为“TodoItems”。 ASP.NET Core 路由不区分大小写。
如果 [HttpGet]
属性具有路由模板(例如 [HttpGet("products")]
),则将它追加到路径。 此示例不使用模板。 有关详细信息,请参阅使用 Http [Verb] 特性的特性路由。
在下面的 GetTodoItem
方法中,"{id}"
是待办事项的唯一标识符的占位符变量。 调用 GetTodoItem
时,URL 中 "{id}"
的值会在 id
参数中提供给方法。
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
return NotFound();
return todoItem;
GetTodoItems
和 GetTodoItem
方法的返回类型是 ActionResult<T> 类型。 ASP.NET Core 自动将对象序列化为 JSON,并将 JSON 写入响应消息的正文中。 此返回类型的响应代码为 200 OK(假设没有未处理的异常)。 未经处理的异常将转换为 5xx 错误。
ActionResult
返回类型可以表示大范围的 HTTP 状态代码。 例如,GetTodoItem
可以返回两个不同的状态值:
- 如果没有任何项与请求的 ID 匹配,该方法将返回 404 状态NotFound错误代码。
- 否则,此方法将返回具有 JSON 响应正文的 200。 返回
item
则产生 HTTP 200
响应。
PutTodoItem 方法
检查 PutTodoItem
方法:
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
if (id != todoItem.Id)
return BadRequest();
_context.Entry(todoItem).State = EntityState.Modified;
await _context.SaveChangesAsync();
catch (DbUpdateConcurrencyException)
if (!TodoItemExists(id))
return NotFound();
throw;
return NoContent();
PutTodoItem
与 PostTodoItem
类似,但使用的是 HTTP PUT
。 响应是 204(无内容)。 根据 HTTP 规范,PUT
请求需要客户端发送整个更新的实体,而不仅仅是更改。 若要支持部分更新,请使用 HTTP PATCH。
测试 PutTodoItem 方法
本示例使用内存内、数据库,每次启动应用时都必须对其进行初始化。 在进行 PUT 调用之前,数据库中必须有一个项。 调用 GET,以确保在调用 PUT 之前数据库中存在项。
使用 Swagger UI,使用 PUT 按钮更新 Id = 1 的 TodoItem
并将其名称设置为 "feed fish"
。 请注意,响应为 HTTP 204 No Content
。
DeleteTodoItem 方法
检查 DeleteTodoItem
方法:
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
return NotFound();
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
测试 DeleteTodoItem 方法
使用 Swagger UI 删除 Id = 1 的 TodoItem
。 请注意,响应为 HTTP 204 No Content
。
使用其他工具进行测试
还有许多其他工具可用于测试 Web API,例如:
- Visual Studio 终结点资源管理器和 .http 文件
- http-repl
- curl。 Swagger 使用
curl
并显示它提交的 curl
命令。
- Fiddler
有关详细信息,请参阅:
- 最小 API 教程:使用 .http 文件和终结点资源管理器进行测试
防止过度发布
目前,示例应用公开了整个 TodoItem
对象。 生产应用通常使用模型的子集来限制输入和返回的数据。 这背后有多种原因,但安全性是主要原因。 模型的子集通常称为数据传输对象 (DTO)、输入模型或视图模型。 本教程使用了 DTO。
DTO 可用于:
- 防止过度发布。
- 隐藏客户端不应查看的属性。
- 省略一些属性以缩减有效负载大小。
- 平展包含嵌套对象的对象图。 对客户端而言,平展的对象图可能更方便。
若要演示 DTO 方法,请更新 TodoItem
类,使其包含机密字段:
namespace TodoApi.Models
public class TodoItem
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
此应用需要隐藏机密字段,但管理应用可以选择公开它。
确保可以发布和获取机密字段。
创建 DTO 模型:
namespace TodoApi.Models;
public class TodoItemDTO
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
更新 TodoItemsController
以使用 TodoItemDTO
:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
_context = context;
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
// GET: api/TodoItems/5
// <snippet_GetByID>
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
return NotFound();
return ItemToDTO(todoItem);
// </snippet_GetByID>
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Update>
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
if (id != todoDTO.Id)
return BadRequest();
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
return NotFound();
todoItem.Name = todoDTO.Name;
todoItem.IsComplete = todoDTO.IsComplete;
await _context.SaveChangesAsync();
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
return NotFound();
return NoContent();
// </snippet_Update>
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Create>
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
var todoItem = new TodoItem
IsComplete = todoDTO.IsComplete,
Name = todoDTO.Name
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
// </snippet_Create>
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
return NotFound();
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
private bool TodoItemExists(long id)
return _context.TodoItems.Any(e => e.Id == id);
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
确保无法发布或获取机密字段。
使用 JavaScript 调用 Web API
请参阅教程:使用 JavaScript 调用 ASP.NET Core Web API。
Web API 视频系列
请参阅视频:初学者系列:Web API。
企业 Web 应用模式
有关创建可靠、安全、高性能、可测试且可缩放的 ASP.NET Core 应用的指导,请参阅 企业 Web 应用模式。 提供了一个完整且具备生产质量的示例 Web 应用,该应用实现了这些模式。
向 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 网站)。
发布到 Azure
有关部署到 Azure 的信息,请参阅快速入门:部署 ASP.NET Web 应用。
查看或下载本教程的示例代码。 请参阅如何下载。
有关详细信息,请参阅以下资源:
- 使用 ASP.NET Core 创建 Web API
- 教程:使用 ASP.NET Core 创建最小 API
- 带有 Swagger/OpenAPI 的 ASP.NET Core Web API 文档
- Razor ASP.NET Core 中带 Entity Framework Core 的页面 - 第 1 个教程(共 8 个)
- 在 ASP.NET Core 中路由到控制器操作
- ASP.NET Core Web API 中控制器操作的返回类型
- 将 ASP.NET Core 应用部署到 Azure 应用程序服务
- 托管和部署 ASP.NET Core
- 使用 ASP.NET Core 创建 Web API
本教程介绍生成使用数据库的基于控制器的 Web API 的基础知识。 在 ASP.NET Core 中创建 API 的另一种方法是创建最小 API。 有关在最小 API 和基于控制器的 API 之间进行选择的帮助,请参阅 API 概述。 有关创建最小 API 的教程,请参阅教程:使用 ASP.NET Core 创建最小 API。
本教程将创建以下 API:
- .NET 8.0 SDK
Visual Studio Code 说明使用用于 ASP.NET Core 的 .NET CLI 开发功能,如项目创建。 可在(macOS、Linux 或 Windows)上或在任何代码编辑器中遵循这些说明。 如果使用 Visual Studio Code 以外的其他内容,则可能需要进行少量更改。
创建 Web 项目
Visual Studio
Visual Studio Code
- 在搜索框中输入“Web API”。
- 选择“ASP.NET Core Web API”模板,然后选择“下一步”。
- 在“配置新项目”对话框中,将项目命名为“TodoApi”,然后选择“下一步”。
- 在“其他信息”对话框中:
- 确认“框架”为“.NET 8.0 (长期支持)”。
- 确认已选中“使用控制器(取消选中以使用最小 API)”复选框。
- 确认已选中“启用 OpenAPI 支持”复选框。
- 选择“创建”。
添加 NuGet 包
必须添加 NuGet 包以支持本教程中使用的数据库。
- 在“工具”菜单中,选择“NuGet 包管理器”>“管理解决方案的 NuGet 包”。
- 选择“浏览”选项卡。
- 在搜索框中输入“Microsoft.EntityFrameworkCore.InMemory”,然后选择
Microsoft.EntityFrameworkCore.InMemory
。
- 选中右窗格中的“项目”复选框,然后选择“安装”。
dotnet new webapi --use-controllers -o TodoApi
cd TodoApi
dotnet add package Microsoft.EntityFrameworkCore.InMemory
code -r ../TodoApi
这些命令会:
- 创建新的 Web API 项目,并在 Visual Studio Code 中打开它。
- 添加下一部分所需的 NuGet 包。
- 在 Visual Studio Code 的当前实例中打开 TodoApi 文件夹。
Visual Studio Code 可能会显示一个对话框,询问:“你是否信任此文件夹中的文件作者?”
- 如果你信任父文件夹中的所有文件,请选择“信任父文件夹中所有文件的作者”。
- 选择“是,我信任所有作者”,因为项目文件夹包含 .NET 生成的文件。
- 当 Visual Studio Code 请求你添加资产来生成和调试项目时,请选择“是”。 如果 Visual Studio Code 不提供添加生成和调试资产的功能,请选择视图>命令面板,然后在搜索框中输入“
.NET
”。 从命令列表中选择 .NET: Generate Assets for Build and Debug
命令。
Visual Studio Code 添加一个包含生成的 launch.json
和 tasks.json
文件的 .vscode
文件夹。
前面的命令需要 Linux 上的 .NET 9 SDK 或更高版本。 对于 .NET 8.0.401 SDK 及更早版本上的 Linux,有关信任证书的信息,请参阅 Linux 发行版的文档。
如果证书以前不受信任,上述命令显示以下对话:
如果你同意信任开发证书,请选择“是”。
有关详细信息,请参阅强制实施 SSL 一文中的信任 ASP.NET Core HTTPS 开发证书部分。
有关信任 Firefox 浏览器的信息,请参阅 Firefox SEC_ERROR_INADEQUATE_KEY_USAGE 证书错误。
运行应用:
运行以下命令,在 https
配置文件上启动应用:
dotnet run --launch-profile https
输出会显示类似于以下内容的消息,表示应用正在运行且正在等待请求:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:{port}
Ctrl+单击输出中的 HTTPS URL 以在浏览器中测试 Web 应用。
默认浏览器会启动到 https://localhost:<port>/swagger/index.html
,其中 <port>
是输出中显示的随机选择的端口号。 https://localhost:<port>
没有终结点,因此浏览器返回 HTTP 404 未找到。 将 /swagger
附加到 URL https://localhost:<port>/swagger
。
按照以下说明测试 Web 应用后,在集成终端中按 Ctrl+C 来将其关闭。
随即显示 Swagger 页面 /swagger/index.html
。 选择 GET>试用>执行。 页面将显示:
- 用于测试 WeatherForecast API 的 Curl 命令。
- 用于测试 WeatherForecast API 的 URL。
- 响应代码、正文和标头。
- 包含媒体类型、示例值和架构的下拉列表框。
如果 Swagger 页面未显示,请参阅此 GitHub 问题。
Swagger 用于为 Web API 生成有用的文档和帮助页面。 本教程使用 Swagger 测试应用。 有关 Swagger 的详细信息,请参阅包含 Swagger / OpenAPI 的 ASP.NET Core Web API 文档。
将请求 URL 复制粘贴到浏览器中:https://localhost:<port>/weatherforecast
返回类似于以下示例的 JSON:
"date": "2019-07-16T19:04:05.7257911-06:00",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
"date": "2019-07-17T19:04:05.7258461-06:00",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
"date": "2019-07-18T19:04:05.7258467-06:00",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
"date": "2019-07-19T19:04:05.7258471-06:00",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
"date": "2019-07-20T19:04:05.7258474-06:00",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
添加模型类
模型是一组表示应用管理的数据的类。 此应用的模型是 TodoItem
类。
Visual Studio
Visual Studio Code
- 在“解决方案资源管理器”中,右键单击项目。 选择添加>新建文件夹。 将该文件夹命名为
Models
注册一个免费试用帐户。
- 右键单击
Models
文件夹,然后选择添加>类。 将类命名为 TodoItem,然后选择“添加”。
- 将模板代码替换为以下内容:
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
Id
属性用作关系数据库中的唯一键。
模型类可位于项目的任意位置,但按照惯例会使用 Models
文件夹。
添加数据库上下文
数据库上下文是为数据模型协调 Entity Framework 功能的主类。 此类由 Microsoft.EntityFrameworkCore.DbContext 类派生而来。
Visual Studio
Visual Studio Code
public class TodoContext : DbContext
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
public DbSet<TodoItem> TodoItems { get; set; } = null!;
注册数据库上下文
在 ASP.NET Core 中,服务(如数据库上下文)必须向依赖关系注入 (DI) 容器进行注册。 该容器向控制器提供服务。
使用以下突出显示的代码更新 Program.cs
:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
前面的代码:
- 添加
using
指令。
- 将数据库上下文添加到 DI 容器。
- 指定数据库上下文将使用内存中数据库。
构建控制器
Visual Studio
Visual Studio Code
- 在“模型类”中选择“TodoItem (TodoApi.Models)”。
- 在“数据上下文类”中选择“TodoContext (TodoAPI.Models)”。
- 选择“添加”。
如果基架操作失败,请选择“添加”以第二次尝试使用基架。
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet tool uninstall -g dotnet-aspnet-codegenerator
dotnet tool install -g dotnet-aspnet-codegenerator
dotnet tool update -g dotnet-aspnet-codegenerator
前面的命令:
- 添加构建所需的 NuGet 包。
- 卸载任何可能的早期版本后,安装基架引擎 (
dotnet-aspnet-codegenerator
)。
对于 Linux,使用以下命令将 .NET 工具目录添加到系统路径:
echo 'export PATH=$HOME/.dotnet/tools:$PATH' >> ~/.bashrc
source ~/.bashrc
默认情况下,要安装的 .NET 二进制文件的体系结构表示当前运行的 OS 体系结构。 若要指定不同的 OS 体系结构,请参阅 dotnet tool install, --arch option。
有关详细信息,请参阅 GitHub 问题 dotnet/AspNetCore.Docs #29262。
生成项目。
运行以下命令:
dotnet aspnet-codegenerator controller -name TodoItemsController -async -api -m TodoItem -dc TodoContext -outDir Controllers
上述命令为 TodoItemsController
创建基架。
生成的代码:
- 使用
[ApiController]
属性标记类。 此属性指示控制器响应 Web API 请求。 有关该属性启用的特定行为的信息,请参阅使用 ASP.NET Core 创建 Web API。
- 使用 DI 将数据库上下文 (
TodoContext
) 注入到控制器中。 数据库上下文将在控制器中的每个 CRUD 方法中使用。
ASP.NET Core 模板:
- 具有视图的控制器在路由模板中包含
[action]
。
- API 控制器不在路由模板中包含
[action]
。
[action]
令牌不在路由模板中时,终结点中不包含 action 名称(方法名称)。 也就是说,不会在匹配的路由中使用操作的关联方法名称。
更新 PostTodoItem create 方法
更新 PostTodoItem
中的 return 语句,以使用 nameof 运算符:
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
上述代码是 HTTP POST
方法,如 [HttpPost]
属性所指示。 此方法从 HTTP 请求正文获取 TodoItem
的值。
有关详细信息,请参阅使用 Http [Verb] 特性的特性路由。
CreatedAtAction 方法:
- 如果成功,将返回 HTTP 201 状态代码。
HTTP 201
是在服务器上创建新资源的 HTTP POST
方法的标准响应。
- 向响应添加位置标头。
Location
标头指定新建的待办事项的 URI。 有关详细信息,请参阅创建的 10.2.2 201。
- 引用
GetTodoItem
操作以创建 Location
标头的 URI。 C# nameof
关键字用于避免在 CreatedAtAction
调用中硬编码操作名称。
测试 PostTodoItem
按 Ctrl+F5 运行应用。
在 Swagger 浏览器窗口中,选择“POST /api/TodoItems”,然后选择“试用”。
在请求正文输入窗口中,更新 JSON。 例如,
"name": "walk dog",
"isComplete": true
选择“执行”
测试位置标头 URI
在上述 POST 中,Swagger UI 在“响应标头”下显示了位置标头。 例如,location: https://localhost:7260/api/TodoItems/1
。 位置标头显示创建资源的 URI。
测试位置标头:
在 Swagger 浏览器窗口中,选择“GET /api/TodoItems/{id}”,然后选择“试用”。
在 id
输入框中输入 1
,然后选择“执行”。
检查 GET 方法
实现了两个 GET 终结点:
GET /api/todoitems
GET /api/todoitems/{id}
上一节展示了 /api/todoitems/{id}
路由的示例。
按照 POST 说明添加另一待办事项,然后使用 Swagger 测试 /api/todoitems
路由。
此应用使用内存中数据库。 如果停止并启动应用,则前面的 GET 请求不会返回任何数据。 如果未返回任何数据,将数据 POST 到应用。
路由和 URL 路径
[HttpGet]
属性表示响应 HTTP GET
请求的方法。 每个方法的 URL 路径构造如下所示:
在控制器的 Route
属性中以模板字符串开头:
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase