教程:使用 ASP.NET Core 创建 Web API
注意
此版本不是本文的最新版本。 对于当前版本,请参阅此文的 .NET 8 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 对于当前版本,请参阅此文的 .NET 8 版本。
作者:Rick Anderson 和 Kirk Larkin
本教程介绍生成使用数据库的基于控制器的 Web API 的基础知识。 在 ASP.NET Core 中创建 API 的另一种方法是创建最小 API。 有关在最小 API 和基于控制器的 API 之间进行选择的帮助,请参阅 API 概述。 有关创建最小 API 的教程,请参阅教程:使用 ASP.NET Core 创建最小 API。
概述
本教程将创建以下 API:
API | 描述 | 请求正文 | 响应正文 |
---|---|---|---|
GET /api/todoitems |
获取所有待办事项 | None | 待办事项的数组 |
GET /api/todoitems/{id} |
按 ID 获取项 | None | 待办事项 |
POST /api/todoitems |
添加新项 | 待办事项 | 待办事项 |
PUT /api/todoitems/{id} |
更新现有项 | 待办事项 | None |
DELETE /api/todoitems/{id} |
删除项 | None | None |
下图显示了应用的设计。
先决条件
带有 ASP.NET 和 Web 开发工作负载的 Visual Studio 2022。
创建 Web 项目
- 从“文件”菜单中选择“新建”>“项目” 。
- 在搜索框中输入“Web API”。
- 选择“ASP.NET Core Web API”模板,然后选择“下一步”。
- 在“配置新项目”对话框中,将项目命名为“TodoApi”,然后选择“下一步”。
- 在“其他信息”对话框中:
- 确认“框架”为“.NET 8.0 (长期支持)”。
- 确认已选中“使用控制器(取消选中以使用最小 API)”。
- 确认已选中“启用 OpenAPI 支持”复选框。
- 选择“创建”。
添加 NuGet 包
必须添加 NuGet 包以支持本教程中使用的数据库。
- 在“工具”菜单中,选择“NuGet 包管理器”>“管理解决方案的 NuGet 包”。
- 选择“浏览”选项卡。
- 在搜索框中输入“Microsoft.EntityFrameworkCore.InMemory”,然后选择
Microsoft.EntityFrameworkCore.InMemory
。 - 选中右窗格中的“项目”复选框,然后选择“安装” 。
注意
有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。
测试项目
项目模板创建了一个支持 Swagger 的 WeatherForecast
API。
按 Ctrl+F5 以在不使用调试程序的情况下运行。
如果尚未将项目配置为使用 SSL,Visual Studio 显示以下对话:
如果信任 IIS Express SSL 证书,请选择“是”。
将显示以下对话框:
如果你同意信任开发证书,请选择“是”。
有关信任 Firefox 浏览器的信息,请参阅 Firefox SEC_ERROR_INADEQUATE_KEY_USAGE 证书错误。
Visual Studio 将启动默认浏览器并导航到 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
类。
- 在“解决方案资源管理器”中,右键单击项目。 选择“添加”>“新建文件夹”。 将该文件夹命名为
Models
注册一个免费试用帐户。 - 右键单击
Models
文件夹,然后选择“添加”>“类”。 将类命名为 TodoItem,然后选择“添加”。 - 将模板代码替换为以下内容:
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Id
属性用作关系数据库中的唯一键。
模型类可位于项目的任意位置,但按照惯例会使用 Models
文件夹。
添加数据库上下文
数据库上下文是为数据模型协调 Entity Framework 功能的主类。 此类由 Microsoft.EntityFrameworkCore.DbContext 类派生而来。
- 右键单击
Models
文件夹,然后选择“添加”>“类”。 将类命名为 TodoContext,然后单击“添加”。
输入以下代码:
using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; 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 容器。
- 指定数据库上下文将使用内存中数据库。
构建控制器
右键单击
Controllers
文件夹。选择 添加>New Scaffolded Item 。
选择“其操作使用实体框架的 API 控制器”,然后选择“添加” 。
在“添加其操作使用实体框架的 API 控制器”对话框中:
- 在“模型类”中选择“TodoItem (TodoApi.Models)” 。
- 在“数据上下文类”中选择“TodoContext (TodoAPI.Models)” 。
- 选择“添加”。
如果基架操作失败,请选择“添加”以第二次尝试使用基架。
生成的代码:
- 使用
[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
可以返回两个不同的状态值:
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;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
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
有关详细信息,请参阅:
防止过度发布
目前,示例应用公开了整个 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;
try
{
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 应用模式
请观看《适用于 .NET 的可靠 Web 应用模式》YouTube 视频和文章,了解如何创建新式、可靠、高性能、可测试、经济高效且可缩放的 ASP.NET Core 应用,无论是从头开始创建还是重构现有应用。
向 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 文档
- RazorASP.NET Core 中的 Pages 和 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:
API | 描述 | 请求正文 | 响应正文 |
---|---|---|---|
GET /api/todoitems |
获取所有待办事项 | None | 待办事项的数组 |
GET /api/todoitems/{id} |
按 ID 获取项 | None | 待办事项 |
POST /api/todoitems |
添加新项 | 待办事项 | 待办事项 |
PUT /api/todoitems/{id} |
更新现有项 | 待办事项 | None |
DELETE /api/todoitems/{id} |
删除项 | None | None |
下图显示了应用的设计。
先决条件
带有 ASP.NET 和 Web 开发工作负载的 Visual Studio 2022。
创建 Web 项目
- 从“文件”菜单中选择“新建”>“项目” 。
- 在搜索框中输入“Web API”。
- 选择“ASP.NET Core Web API”模板,然后选择“下一步”。
- 在“配置新项目”对话框中,将项目命名为“TodoApi”,然后选择“下一步”。
- 在“其他信息”对话框中:
- 确认“框架”为“.NET 8.0 (长期支持)”。
- 确认已选中“使用控制器(取消选中以使用最小 API)”。
- 确认已选中“启用 OpenAPI 支持”复选框。
- 选择“创建”。
添加 NuGet 包
必须添加 NuGet 包以支持本教程中使用的数据库。
- 在“工具”菜单中,选择“NuGet 包管理器”>“管理解决方案的 NuGet 包”。
- 选择“浏览”选项卡。
- 在搜索框中输入“Microsoft.EntityFrameworkCore.InMemory”,然后选择
Microsoft.EntityFrameworkCore.InMemory
。 - 选中右窗格中的“项目”复选框,然后选择“安装” 。
说明
有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。
测试项目
项目模板创建了一个支持 Swagger 的 WeatherForecast
API。
按 Ctrl+F5 以在不使用调试程序的情况下运行。
如果尚未将项目配置为使用 SSL,Visual Studio 显示以下对话:
如果信任 IIS Express SSL 证书,请选择“是”。
将显示以下对话框:
如果你同意信任开发证书,请选择“是”。
有关信任 Firefox 浏览器的信息,请参阅 Firefox SEC_ERROR_INADEQUATE_KEY_USAGE 证书错误。
Visual Studio 将启动默认浏览器并导航到 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
类。
- 在“解决方案资源管理器”中,右键单击项目。 选择“添加”>“新建文件夹”。 将该文件夹命名为
Models
注册一个免费试用帐户。 - 右键单击
Models
文件夹,然后选择“添加”>“类”。 将类命名为 TodoItem,然后选择“添加”。 - 将模板代码替换为以下内容:
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Id
属性用作关系数据库中的唯一键。
模型类可位于项目的任意位置,但按照惯例会使用 Models
文件夹。
添加数据库上下文
数据库上下文是为数据模型协调 Entity Framework 功能的主类。 此类由 Microsoft.EntityFrameworkCore.DbContext 类派生而来。
- 右键单击
Models
文件夹,然后选择“添加”>“类”。 将类命名为 TodoContext,然后单击“添加”。
输入以下代码:
using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; 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 容器。
- 指定数据库上下文将使用内存中数据库。
构建控制器
右键单击
Controllers
文件夹。选择 添加>New Scaffolded Item 。
选择“其操作使用实体框架的 API 控制器”,然后选择“添加” 。
在“添加其操作使用实体框架的 API 控制器”对话框中:
- 在“模型类”中选择“TodoItem (TodoApi.Models)” 。
- 在“数据上下文类”中选择“TodoContext (TodoAPI.Models)” 。
- 选择“添加”。
如果基架操作失败,请选择“添加”以第二次尝试使用基架。
生成的代码:
- 使用
[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
可以返回两个不同的状态值:
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;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
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
有关详细信息,请参阅:
防止过度发布
目前,示例应用公开了整个 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;
try
{
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 应用模式
请观看《适用于 .NET 的可靠 Web 应用模式》YouTube 视频和文章,了解如何创建新式、可靠、高性能、可测试、经济高效且可缩放的 ASP.NET Core 应用,无论是从头开始创建还是重构现有应用。
向 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 文档
- RazorASP.NET Core 中的 Pages 和 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:
API | 描述 | 请求正文 | 响应正文 |
---|---|---|---|
GET /api/todoitems |
获取所有待办事项 | None | 待办事项的数组 |
GET /api/todoitems/{id} |
按 ID 获取项 | None | 待办事项 |
POST /api/todoitems |
添加新项 | 待办事项 | 待办事项 |
PUT /api/todoitems/{id} |
更新现有项 | 待办事项 | None |
DELETE /api/todoitems/{id} |
删除项 | None | None |
下图显示了应用的设计。
先决条件
带有 ASP.NET 和 Web 开发工作负载的 Visual Studio 2022。
创建 Web 项目
- 从“文件”菜单中选择“新建”>“项目” 。
- 在搜索框中输入“Web API”。
- 选择“ASP.NET Core Web API”模板,然后选择“下一步”。
- 在“配置新项目”对话框中,将项目命名为“TodoApi”,然后选择“下一步”。
- 在“其他信息”对话框中:
- 确认“框架”为“.NET 8.0 (长期支持)”。
- 确认已选中“使用控制器(取消选中以使用最小 API)”。
- 确认已选中“启用 OpenAPI 支持”复选框。
- 选择“创建”。
添加 NuGet 包
必须添加 NuGet 包以支持本教程中使用的数据库。
- 在“工具”菜单中,选择“NuGet 包管理器”>“管理解决方案的 NuGet 包”。
- 选择“浏览”选项卡。
- 在搜索框中输入“Microsoft.EntityFrameworkCore.InMemory”,然后选择
Microsoft.EntityFrameworkCore.InMemory
。 - 选中右窗格中的“项目”复选框,然后选择“安装” 。
注意
有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。
测试项目
项目模板创建了一个支持 Swagger 的 WeatherForecast
API。
按 Ctrl+F5 以在不使用调试程序的情况下运行。
如果尚未将项目配置为使用 SSL,Visual Studio 显示以下对话:
如果信任 IIS Express SSL 证书,请选择“是”。
将显示以下对话框:
如果你同意信任开发证书,请选择“是”。
有关信任 Firefox 浏览器的信息,请参阅 Firefox SEC_ERROR_INADEQUATE_KEY_USAGE 证书错误。
Visual Studio 将启动默认浏览器并导航到 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
类。
- 在“解决方案资源管理器”中,右键单击项目。 选择“添加”>“新建文件夹”。 将该文件夹命名为
Models
注册一个免费试用帐户。 - 右键单击
Models
文件夹,然后选择“添加”>“类”。 将类命名为 TodoItem,然后选择“添加”。 - 将模板代码替换为以下内容:
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Id
属性用作关系数据库中的唯一键。
模型类可位于项目的任意位置,但按照惯例会使用 Models
文件夹。
添加数据库上下文
数据库上下文是为数据模型协调 Entity Framework 功能的主类。 此类由 Microsoft.EntityFrameworkCore.DbContext 类派生而来。
- 右键单击
Models
文件夹,然后选择“添加”>“类”。 将类命名为 TodoContext,然后单击“添加”。
输入以下代码:
using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; 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 容器。
- 指定数据库上下文将使用内存中数据库。
构建控制器
右键单击
Controllers
文件夹。选择 添加>New Scaffolded Item 。
选择“其操作使用实体框架的 API 控制器”,然后选择“添加” 。
在“添加其操作使用实体框架的 API 控制器”对话框中:
- 在“模型类”中选择“TodoItem (TodoApi.Models)” 。
- 在“数据上下文类”中选择“TodoContext (TodoAPI.Models)” 。
- 选择“添加”。
如果基架操作失败,请选择“添加”以第二次尝试使用基架。
生成的代码:
- 使用
[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
可以返回两个不同的状态值:
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;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
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
有关详细信息,请参阅:
防止过度发布
目前,示例应用公开了整个 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;
try
{
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 应用模式
请观看《适用于 .NET 的可靠 Web 应用模式》YouTube 视频和文章,了解如何创建新式、可靠、高性能、可测试、经济高效且可缩放的 ASP.NET Core 应用,无论是从头开始创建还是重构现有应用。
向 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 文档
- RazorASP.NET Core 中的 Pages 和 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