教程:使用 ASP.NET Core 创建 Web APITutorial: Create a web API with ASP.NET Core

本文内容

作者:Rick AndersonKirk LarkinMike Wasson

本教程介绍使用 ASP.NET Core 构建 Web API 的基础知识。

在本教程中,你将了解:

  • 创建 Web API 项目。
  • 添加模型类和数据库上下文。
  • 使用 CRUD 方法构建控制器。
  • 配置路由、URL 路径和返回值。
  • 使用 Postman 调用 Web API。

在结束时,你会获得可以管理存储在数据库中的“待办事项”的 Web API。

概述Overview

本教程将创建以下 API:

API描述请求正文响应正文
GET /api/TodoItems获取所有待办事项None待办事项的数组
GET /api/TodoItems/{id}按 ID 获取项None待办事项
POST /api/TodoItems添加新项待办事项待办事项
PUT /api/TodoItems/{id}更新现有项 待办事项None
DELETE /api/TodoItems/{id} 删除项 NoneNone

下图显示了应用的设计。

右侧的框表示客户端。

先决条件Prerequisites

Visual Studio Code 说明使用用于 ASP.NET Core 的 .NET Core CLI 开发功能,如项目创建。可在任何平台(macOS、Linux 或 Windows)上或在任何代码编辑器中遵循这些说明。如果使用 Visual Studio Code 以外的其他内容,则可能需要进行少量更改。

创建 Web 项目Create a web project

  • 从“文件”菜单中选择“新建”>“项目” 。
  • 选择“ASP.NET Core Web 应用程序”模板,再单击“下一步” 。
  • 将项目命名为 TodoApi,然后单击“创建” 。
  • 在“创建新的 ASP.NET Core Web 应用程序”对话框中,确认选择“.NET Core”和“ASP.NET Core 3.1” 。选择“API”模板,然后单击“创建” 。

VS“新建项目”对话框

  • 打开集成终端

  • 将目录 (cd) 更改为包含项目文件夹的文件夹。

  • 运行以下命令:

  1. dotnet new webapi -o TodoApi
  2. cd TodoApi
  3. dotnet add package Microsoft.EntityFrameworkCore.SqlServer
  4. dotnet add package Microsoft.EntityFrameworkCore.InMemory
  5. code -r ../TodoApi
  • 当对话框询问是否要将所需资产添加到项目时,选择“是” 。

前面的命令:

  • 创建新的 web API 项目,并在 Visual Studio Code 中打开它。
  • 添加下一部分中所需的 NuGet 包。
  • 选择“文件”>“新建解决方案” 。

macOS 新建解决方案

  • 选择“.NET Core” >“应用” >“API” >“下一步”。

macOS“新建项目”对话框

  • 在“配置新的 ASP.NET Core Web API”对话框中,将目标框架选择为*“.NET Core 3.1” 。

  • 输入“TodoApi” 作为“项目名称” ,然后选择“创建” 。

配置对话框

访问 Visual studio for Mac 上的命令终端Accessing a Command Terminal on Visual Studios for Mac

首次访问 Mac 上的命令终端需要以下设置配置:

  • 导航到“系统首选项”>“键盘”>“快捷方式”>“服务”。
  • 在“文件和文件夹”下,验证是否选中了“文件夹中的新终端”。

上述说明以两种方式实现访问命令终端:从 Visual Studio 内访问或通过查找器访问。

若要从 Visual Studio for Mac 访问命令终端,请执行以下操作:To access a command terminal from Visual Studio for Mac:

  • 右键单击项目名称。
  • 导航到“工具”>“在终端中打开”。

若要通过查找器访问命令终端,请执行以下操作:To access a command terminal from Finder:

  • 右键单击所需文件夹。
  • 在列表底部,选择“文件夹中的新终端”。

在项目文件夹中打开命令终端并运行以下命令:

  1. dotnet add package Microsoft.EntityFrameworkCore.SqlServer
  2. dotnet add package Microsoft.EntityFrameworkCore.InMemory

测试 APITest the API

项目模板会创建 WeatherForecast API。从浏览器调用 Get 方法以测试应用。

按 Ctrl+F5 运行应用。Visual Studio 启动浏览器并导航到 https://localhost:<port>/WeatherForecast,其中 <port> 是随机选择的端口号。

如果出现询问是否应信任 IIS Express 证书的对话框,则选择“是” 。在接下来出现的“安全警告” 对话框中,选择“是” 。

按 Ctrl+F5 运行应用。在浏览器中,转到以下 URL:https://localhost:5001/WeatherForecast

选择“运行” > “开始调试” 以启动应用。Visual Studio for Mac 会启动浏览器并导航到 https://localhost:<port&gt;,其中 <port> 是随机选择的端口号。将返回 HTTP 404(未找到)错误。/WeatherForecast 追加到 URL(将 URL 更改为 https://localhost:<port>/WeatherForecast)。

返回类似于以下项的 JSON:

  1. [
  2. {
  3. "date": "2019-07-16T19:04:05.7257911-06:00",
  4. "temperatureC": 52,
  5. "temperatureF": 125,
  6. "summary": "Mild"
  7. },
  8. {
  9. "date": "2019-07-17T19:04:05.7258461-06:00",
  10. "temperatureC": 36,
  11. "temperatureF": 96,
  12. "summary": "Warm"
  13. },
  14. {
  15. "date": "2019-07-18T19:04:05.7258467-06:00",
  16. "temperatureC": 39,
  17. "temperatureF": 102,
  18. "summary": "Cool"
  19. },
  20. {
  21. "date": "2019-07-19T19:04:05.7258471-06:00",
  22. "temperatureC": 10,
  23. "temperatureF": 49,
  24. "summary": "Bracing"
  25. },
  26. {
  27. "date": "2019-07-20T19:04:05.7258474-06:00",
  28. "temperatureC": -1,
  29. "temperatureF": 31,
  30. "summary": "Chilly"
  31. }
  32. ]

添加模型类Add a model class

模型 是一组表示应用管理的数据的类。此应用的模型是单个 TodoItem 类。

  • 在“解决方案资源管理器” 中,右键单击项目。选择“添加” > “新建文件夹” 。将文件夹命名为“Models” 。

  • 右键单击“Models” 文件夹,然后选择“添加” > “类” 。将类命名为 TodoItem,然后选择“添加” 。

  • 将模板代码替换为以下代码:

  • 添加名为“Models”的文件夹 。

  • 使用以下代码将 TodoItem 类添加到 Models 文件夹:

  • 右键单击项目。选择“添加” > “新建文件夹” 。将文件夹命名为“Models” 。

新建文件夹

  • 右键单击“Models” 文件夹,然后选择“添加” >“新建文件” >“常规” >“空类” 。

  • 将类命名为“TodoItem”,然后单击“新建” 。

  • 将模板代码替换为以下代码:

  1. public class TodoItem
  2. {
  3. public long Id { get; set; }
  4. public string Name { get; set; }
  5. public bool IsComplete { get; set; }
  6. }

Id 属性用作关系数据库中的唯一键。

模型类可位于项目的任意位置,但按照惯例会使用 Models 文件夹。

添加数据库上下文Add a database context

数据库上下文是为数据模型协调 Entity Framework 功能的主类 。此类由 Microsoft.EntityFrameworkCore.DbContext 类派生而来。

添加 Add Microsoft.EntityFrameworkCore.SqlServerAdd Microsoft.EntityFrameworkCore.SqlServer

  • 在“工具”菜单中,依次选择“NuGet 包管理器”、“管理解决方案的 NuGet 包” 。
  • 选择“浏览”选项卡,然后在搜索框中输入 Microsoft.EntityFrameworkCore.SqlServer 。
  • 在左窗格中选择“Microsoft.EntityFrameworkCore.SqlServer” 。
  • 选中右窗格中的“项目”复选框,然后选择“安装” 。
  • 使用上述说明添加 Microsoft.EntityFrameworkCore.InMemory NuGet 包。

NuGet 程序包管理器

添加 TodoContext 数据库上下文Add the TodoContext database context

  • 右键单击“Models” 文件夹,然后选择“添加” > “类” 。将类命名为 TodoContext,然后单击“添加” 。
  • TodoContext 类添加到“Models” 文件夹。
  • 输入以下代码:
  1. using Microsoft.EntityFrameworkCore;
  2. namespace TodoApi.Models
  3. {
  4. public class TodoContext : DbContext
  5. {
  6. public TodoContext(DbContextOptions<TodoContext> options)
  7. : base(options)
  8. {
  9. }
  10. public DbSet<TodoItem> TodoItems { get; set; }
  11. }
  12. }

注册数据库上下文Register the database context

在 ASP.NET Core 中,服务(如数据库上下文)必须向依赖关系注入 (DI) 容器进行注册。该容器向控制器提供服务。

使用以下突出显示的代码更新 Startup.cs :

  1. // Unused usings removed
  2. using Microsoft.AspNetCore.Builder;
  3. using Microsoft.AspNetCore.Hosting;
  4. using Microsoft.Extensions.Configuration;
  5. using Microsoft.Extensions.DependencyInjection;
  6. using Microsoft.Extensions.Hosting;
  7. using Microsoft.EntityFrameworkCore;
  8. using TodoApi.Models;
  9. namespace TodoApi
  10. {
  11. public class Startup
  12. {
  13. public Startup(IConfiguration configuration)
  14. {
  15. Configuration = configuration;
  16. }
  17. public IConfiguration Configuration { get; }
  18. public void ConfigureServices(IServiceCollection services)
  19. {
  20. services.AddDbContext<TodoContext>(opt =>
  21. opt.UseInMemoryDatabase("TodoList"));
  22. services.AddControllers();
  23. }
  24. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  25. {
  26. if (env.IsDevelopment())
  27. {
  28. app.UseDeveloperExceptionPage();
  29. }
  30. app.UseHttpsRedirection();
  31. app.UseRouting();
  32. app.UseAuthorization();
  33. app.UseEndpoints(endpoints =>
  34. {
  35. endpoints.MapControllers();
  36. });
  37. }
  38. }
  39. }

前面的代码:

  • 删除未使用的 using 声明。
  • 将数据库上下文添加到 DI 容器。
  • 指定数据库上下文将使用内存中数据库。

构建控制器Scaffold a controller

  • 右键单击 Controllers 文件夹。

  • 选择“添加”>“新建构建项” 。

  • 选择“其操作使用实体框架的 API 控制器”,然后选择“添加” 。

  • 在“添加其操作使用实体框架的 API 控制器”对话框中 :

    • 在“模型类”中选择“TodoItem (TodoApi.Models)” 。
    • 在“数据上下文类”中选择“TodoContext (TodoAPI.Models)” 。
    • 选择“添加” 。

运行以下命令:

  1. dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
  2. dotnet add package Microsoft.EntityFrameworkCore.Design
  3. dotnet tool install --global dotnet-aspnet-codegenerator
  4. dotnet aspnet-codegenerator controller -name TodoItemsController -async -api -m TodoItem -dc TodoContext -outDir Controllers

前面的命令:

  • 添加构建所需的 NuGet 包。
  • 安装构建引擎 (dotnet-aspnet-codegenerator)。
  • 构建 TodoItemsController

生成的代码:

  • 使用 [ApiController] 属性标记类。此属性指示控制器响应 Web API 请求。有关该属性启用的特定行为的信息,请参阅 使用 ASP.NET Core 创建 Web API
  • 使用 DI 将数据库上下文 (TodoContext) 注入到控制器中。数据库上下文将在控制器中的每个 CRUD 方法中使用。

ASP.NET Core 模板:

  • 具有视图的控制器在路由模板中包含 [action]
  • API 控制器不在路由模板中包含 [action]

如果 [action] 标记不在路由模板中,则从路由中排除操作名称。也就是说,不会在匹配的路由中使用操作的关联方法名称。

检查 PostTodoItem create 方法Examine the PostTodoItem create method

替换 PostTodoItem 中的返回语句,以使用 nameof 运算符:

  1. // POST: api/TodoItems
  2. [HttpPost]
  3. public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
  4. {
  5. _context.TodoItems.Add(todoItem);
  6. await _context.SaveChangesAsync();
  7. //return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
  8. return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
  9. }

前面的代码是 HTTP POST 方法,如 [HttpPost] 属性所指示。该方法从 HTTP 请求正文获取待办事项的值。

CreatedAtAction 方法:

  • 如果成功,则返回 HTTP 201 状态代码。HTTP 201 是在服务器上创建新资源的 HTTP POST 方法的标准响应。
  • 向响应添加位置标头。Location 标头指定新建的待办事项的 URI。有关详细信息,请参阅创建的 10.2.2 201
  • 引用 GetTodoItem 操作以创建 Location 标头的 URI。C# nameof 关键字用于避免在 CreatedAtAction 调用中硬编码操作名称。

安装 PostmanInstall Postman

本教程使用 Postman 测试 Web API。

  • 安装 Postman
  • 启动 Web 应用。
  • 启动 Postman。
  • 禁用 SSL 证书验证
    • 在“文件” >“设置”(“常规” 选项卡)中,禁用“SSL 证书验证”。

警告

在测试控制器之后重新启用 SSL 证书验证。

通过 Postman 测试 PostTodoItemTest PostTodoItem with Postman

  • 创建新请求。

  • 将 HTTP 方法设置为 POST

  • 选择“正文”选项卡 。

  • 选择“原始”单选按钮 。

  • 将类型设置为 JSON (application/json)

  • 在请求正文中,输入待办事项的 JSON:

  1. {
  2. "name":"walk dog",
  3. "isComplete":true
  4. }
  • 选择Send

使用创建请求的 Postman

测试位置标头 URITest the location header URI

  • Headers 窗格中选择Response 选项卡。

  • 复制Location 标头值:

Postman 控制台的“标头”选项卡

检查 GET 方法Examine the GET methods

这些方法实现两个 GET 终结点:

  • GET /api/TodoItems
  • GET /api/TodoItems/{id}

通过从浏览器或 Postman 调用两个终结点来测试应用。例如:

GetTodoItems 的调用生成类似于以下项的响应:

  1. [
  2. {
  3. "id": 1,
  4. "name": "Item1",
  5. "isComplete": false
  6. }
  7. ]

使用 Postman 测试 GetTest Get with Postman

此应用使用内存中数据库。如果应用已停止并启动,则前面的 GET 请求将不会返回任何数据。如果未返回任何数据,将数据 POST 到应用。

路由和 URL 路径Routing and URL paths

[HttpGet] 属性表示响应 HTTP GET 请求的方法。每个方法的 URL 路径构造如下所示:

  • 在控制器的 Route 属性中以模板字符串开头:
  1. [Route("api/[controller]")]
  2. [ApiController]
  3. public class TodoItemsController : ControllerBase
  4. {
  5. private readonly TodoContext _context;
  6. public TodoItemsController(TodoContext context)
  7. {
  8. _context = context;
  9. }
  • [controller] 替换为控制器的名称,按照惯例,在控制器类名称中去掉“Controller”后缀。对于此示例,控制器类名称为“TodoItems”控制器,因此控制器名称为“TodoItems” 。ASP.NET Core 路由不区分大小写。

  • 如果 [HttpGet] 属性具有路由模板(例如 [HttpGet("products")]),则将它追加到路径。此示例不使用模板。有关详细信息,请参阅使用 Http [Verb] 特性的特性路由

在下面的 GetTodoItem 方法中,"{id}" 是待办事项的唯一标识符的占位符变量。调用 GetTodoItem 时,URL 中 "{id}" 的值会在 id 参数中提供给方法。

  1. // GET: api/TodoItems/5
  2. [HttpGet("{id}")]
  3. public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
  4. {
  5. var todoItem = await _context.TodoItems.FindAsync(id);
  6. if (todoItem == null)
  7. {
  8. return NotFound();
  9. }
  10. return todoItem;
  11. }

返回值Return values

GetTodoItemsGetTodoItem 方法的返回类型是 ActionResult<T> 类型ASP.NET Core 自动将对象序列化为 JSON,并将 JSON 写入响应消息的正文中。在假设没有未经处理的异常的情况下,此返回类型的响应代码为 200。未经处理的异常将转换为 5xx 错误。

ActionResult 返回类型可以表示大范围的 HTTP 状态代码。例如,GetTodoItem 可以返回两个不同的状态值:

  • 如果没有任何项与请求的 ID 匹配,则该方法将返回 404 NotFound 错误代码。
  • 否则,此方法将返回具有 JSON 响应正文的 200。返回 item 则产生 HTTP 200 响应。

PutTodoItem 方法The PutTodoItem method

检查 PutTodoItem 方法:

  1. // PUT: api/TodoItems/5
  2. [HttpPut("{id}")]
  3. public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
  4. {
  5. if (id != todoItem.Id)
  6. {
  7. return BadRequest();
  8. }
  9. _context.Entry(todoItem).State = EntityState.Modified;
  10. try
  11. {
  12. await _context.SaveChangesAsync();
  13. }
  14. catch (DbUpdateConcurrencyException)
  15. {
  16. if (!TodoItemExists(id))
  17. {
  18. return NotFound();
  19. }
  20. else
  21. {
  22. throw;
  23. }
  24. }
  25. return NoContent();
  26. }

PutTodoItemPostTodoItem 类似,但是使用的是 HTTP PUT。响应是 204(无内容)根据 HTTP 规范,PUT 请求需要客户端发送整个更新的实体,而不仅仅是更改。若要支持部分更新,请使用 HTTP PATCH

如果在调用 PutTodoItem 时出错,请调用 GET 以确保数据库中有项目。

测试 PutTodoItem 方法Test the PutTodoItem method

本示例使用内存内、数据库,每次启动应用时都必须对其进行初始化。在进行 PUT 调用之前,数据库中必须有一个项。调用 GET,以确保在调用 PUT 之前数据库中存在项目。

更新 ID = 1 的待办事项并将其名称设置为“feed fish”:

  1. {
  2. "ID":1,
  3. "name":"feed fish",
  4. "isComplete":true
  5. }

下图显示 Postman 更新:

显示 204(无内容)响应的 Postman 控制台

DeleteTodoItem 方法The DeleteTodoItem method

检查 DeleteTodoItem 方法:

  1. // DELETE: api/TodoItems/5
  2. [HttpDelete("{id}")]
  3. public async Task<ActionResult<TodoItem>> DeleteTodoItem(long id)
  4. {
  5. var todoItem = await _context.TodoItems.FindAsync(id);
  6. if (todoItem == null)
  7. {
  8. return NotFound();
  9. }
  10. _context.TodoItems.Remove(todoItem);
  11. await _context.SaveChangesAsync();
  12. return todoItem;
  13. }

测试 DeleteTodoItem 方法Test the DeleteTodoItem method

使用 Postman 删除待办事项:

防止过度发布Prevent over-posting

目前,示例应用公开了整个 TodoItem 对象。生产应用通常使用模型的子集来限制输入和返回的数据。这背后有多种原因,但安全性是主要原因。模型的子集通常称为数据传输对象 (DTO)、输入模型或视图模型。本文使用的是 DTO

DTO 可用于:

  • 防止过度发布。
  • 隐藏客户端不应查看的属性。
  • 省略一些属性以缩减有效负载大小。
  • 平展包含嵌套对象的对象图。对客户端而言,平展的对象图可能更方便。

若要演示 DTO 方法,请更新 TodoItem 类,使其包含机密字段:

  1. public class TodoItem
  2. {
  3. public long Id { get; set; }
  4. public string Name { get; set; }
  5. public bool IsComplete { get; set; }
  6. public string Secret { get; set; }
  7. }

此应用需要隐藏机密字段,但管理应用可以选择公开它。

确保可以发布和获取机密字段。

创建 DTO 模型:

  1. public class TodoItemDTO
  2. {
  3. public long Id { get; set; }
  4. public string Name { get; set; }
  5. public bool IsComplete { get; set; }
  6. }

更新 TodoItemsController 以使用 TodoItemDTO

  1. [HttpGet]
  2. public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
  3. {
  4. return await _context.TodoItems
  5. .Select(x => ItemToDTO(x))
  6. .ToListAsync();
  7. }
  8. [HttpGet("{id}")]
  9. public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
  10. {
  11. var todoItem = await _context.TodoItems.FindAsync(id);
  12. if (todoItem == null)
  13. {
  14. return NotFound();
  15. }
  16. return ItemToDTO(todoItem);
  17. }
  18. [HttpPut("{id}")]
  19. public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
  20. {
  21. if (id != todoItemDTO.Id)
  22. {
  23. return BadRequest();
  24. }
  25. var todoItem = await _context.TodoItems.FindAsync(id);
  26. if (todoItem == null)
  27. {
  28. return NotFound();
  29. }
  30. todoItem.Name = todoItemDTO.Name;
  31. todoItem.IsComplete = todoItemDTO.IsComplete;
  32. try
  33. {
  34. await _context.SaveChangesAsync();
  35. }
  36. catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
  37. {
  38. return NotFound();
  39. }
  40. return NoContent();
  41. }
  42. [HttpPost]
  43. public async Task<ActionResult<TodoItem>> CreateTodoItem(TodoItemDTO todoItemDTO)
  44. {
  45. var todoItem = new TodoItem
  46. {
  47. IsComplete = todoItemDTO.IsComplete,
  48. Name = todoItemDTO.Name
  49. };
  50. _context.TodoItems.Add(todoItem);
  51. await _context.SaveChangesAsync();
  52. return CreatedAtAction(
  53. nameof(GetTodoItem),
  54. new { id = todoItem.Id },
  55. ItemToDTO(todoItem));
  56. }
  57. [HttpDelete("{id}")]
  58. public async Task<IActionResult> DeleteTodoItem(long id)
  59. {
  60. var todoItem = await _context.TodoItems.FindAsync(id);
  61. if (todoItem == null)
  62. {
  63. return NotFound();
  64. }
  65. _context.TodoItems.Remove(todoItem);
  66. await _context.SaveChangesAsync();
  67. return NoContent();
  68. }
  69. private bool TodoItemExists(long id) =>
  70. _context.TodoItems.Any(e => e.Id == id);
  71. private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
  72. new TodoItemDTO
  73. {
  74. Id = todoItem.Id,
  75. Name = todoItem.Name,
  76. IsComplete = todoItem.IsComplete
  77. };
  78. }

确保无法发布或获取机密字段。

使用 JavaScript 调用 Web APICall the web API with JavaScript

请参阅教程:使用 JavaScript 调用 ASP.NET Core Web API

在本教程中,你将了解:

  • 创建 Web API 项目。
  • 添加模型类和数据库上下文。
  • 添加控制器。
  • 添加 CRUD 方法。
  • 配置路由和 URL 路径。
  • 指定返回值。
  • 使用 Postman 调用 Web API。
  • 使用 JavaScript 调用 Web API。

在结束时,你会获得可以管理存储在关系数据库中的“待办事项”的 Web API。

概述Overview

本教程将创建以下 API:

API描述请求正文响应正文
GET /api/TodoItems获取所有待办事项None待办事项的数组
GET /api/TodoItems/{id}按 ID 获取项None待办事项
POST /api/TodoItems添加新项待办事项待办事项
PUT /api/TodoItems/{id}更新现有项 待办事项None
DELETE /api/TodoItems/{id} 删除项 NoneNone

下图显示了应用的设计。

右侧的框表示客户端。

先决条件Prerequisites

警告

如果使用 Visual Studio 2017,请参阅 dotnet/sdk 问题 #3124,以了解无法与 Visual Studio 一起使用的 .NET Core SDK 版本的信息。

Visual Studio Code 说明使用用于 ASP.NET Core 的 .NET Core CLI 开发功能,如项目创建。可在任何平台(macOS、Linux 或 Windows)上或在任何代码编辑器中遵循这些说明。如果使用 Visual Studio Code 以外的其他内容,则可能需要进行少量更改。

创建 Web 项目Create a web project

  • 从“文件”菜单中选择“新建”>“项目” 。
  • 选择“ASP.NET Core Web 应用程序”模板,再单击“下一步” 。
  • 将项目命名为 TodoApi,然后单击“创建” 。
  • 在“创建新的 ASP.NET Core Web 应用程序”对话框中,确认选择“.NET Core”和“ASP.NET Core 2.2” 。选择“API”模板,然后单击“创建” 。请不要选择“启用 Docker 支持” 。

VS“新建项目”对话框

  • 打开集成终端

  • 将目录 (cd) 更改为包含项目文件夹的文件夹。

  • 运行以下命令:

  1. dotnet new webapi -o TodoApi
  2. code -r TodoApi

这些命令会创建新 Web API 项目并在新项目文件夹中打开 Visual Studio Code 的新实例。

  • 当对话框询问是否要将所需资产添加到项目时,选择“是” 。
  • 选择“文件”>“新建解决方案” 。

macOS 新建解决方案

  • 选择“.NET Core” >“应用” >“API” >“下一步”。

macOS“新建项目”对话框

  • 在“配置新的 ASP.NET Core Web API” 对话框中,接受默认的 *.NET Core 2.2 “目标框架” 。

  • 输入“TodoApi” 作为“项目名称” ,然后选择“创建” 。

配置对话框

测试 APITest the API

项目模板会创建 values API。从浏览器调用 Get 方法以测试应用。

按 Ctrl+F5 运行应用。Visual Studio 启动浏览器并导航到 https://localhost:<port>/api/values,其中 <port> 是随机选择的端口号。

如果出现询问是否应信任 IIS Express 证书的对话框,则选择“是” 。在接下来出现的“安全警告” 对话框中,选择“是” 。

按 Ctrl+F5 运行应用。在浏览器中,转到以下 URL:https://localhost:5001/api/values

选择“运行” > “开始调试” 以启动应用。Visual Studio for Mac 会启动浏览器并导航到 https://localhost:<port&gt;,其中 <port> 是随机选择的端口号。将返回 HTTP 404(未找到)错误。/api/values 追加到 URL(将 URL 更改为 https://localhost:<port>/api/values)。

会返回以下 JSON:

  1. ["value1","value2"]

添加模型类Add a model class

模型 是一组表示应用管理的数据的类。此应用的模型是单个 TodoItem 类。

  • 在“解决方案资源管理器” 中,右键单击项目。选择“添加” > “新建文件夹” 。将文件夹命名为“Models” 。

  • 右键单击“Models” 文件夹,然后选择“添加” > “类” 。将类命名为 TodoItem,然后选择“添加” 。

  • 将模板代码替换为以下代码:

  • 添加名为“Models”的文件夹 。

  • 使用以下代码将 TodoItem 类添加到 Models 文件夹:

  • 右键单击项目。选择“添加” > “新建文件夹” 。将文件夹命名为“Models” 。

新建文件夹

  • 右键单击“Models” 文件夹,然后选择“添加” >“新建文件” >“常规” >“空类” 。

  • 将类命名为“TodoItem”,然后单击“新建” 。

  • 将模板代码替换为以下代码:

  1. namespace TodoApi.Models
  2. {
  3. public class TodoItem
  4. {
  5. public long Id { get; set; }
  6. public string Name { get; set; }
  7. public bool IsComplete { get; set; }
  8. }
  9. }

Id 属性用作关系数据库中的唯一键。

模型类可位于项目的任意位置,但按照惯例会使用 Models 文件夹。

添加数据库上下文Add a database context

数据库上下文是为数据模型协调 Entity Framework 功能的主类 。此类由 Microsoft.EntityFrameworkCore.DbContext 类派生而来。

  • 右键单击“Models” 文件夹,然后选择“添加” > “类” 。将类命名为 TodoContext,然后单击“添加” 。
  • TodoContext 类添加到“Models” 文件夹。
  • 将模板代码替换为以下代码:
  1. using Microsoft.EntityFrameworkCore;
  2. namespace TodoApi.Models
  3. {
  4. public class TodoContext : DbContext
  5. {
  6. public TodoContext(DbContextOptions<TodoContext> options)
  7. : base(options)
  8. {
  9. }
  10. public DbSet<TodoItem> TodoItems { get; set; }
  11. }
  12. }

注册数据库上下文Register the database context

在 ASP.NET Core 中,服务(如数据库上下文)必须向依赖关系注入 (DI) 容器进行注册。该容器向控制器提供服务。

使用以下突出显示的代码更新 Startup.cs :

  1. // Unused usings removed
  2. using Microsoft.AspNetCore.Builder;
  3. using Microsoft.AspNetCore.Hosting;
  4. using Microsoft.AspNetCore.Mvc;
  5. using Microsoft.EntityFrameworkCore;
  6. using Microsoft.Extensions.Configuration;
  7. using Microsoft.Extensions.DependencyInjection;
  8. using TodoApi.Models;
  9. namespace TodoApi
  10. {
  11. public class Startup
  12. {
  13. public Startup(IConfiguration configuration)
  14. {
  15. Configuration = configuration;
  16. }
  17. public IConfiguration Configuration { get; }
  18. // This method gets called by the runtime. Use this method to add services to the
  19. //container.
  20. public void ConfigureServices(IServiceCollection services)
  21. {
  22. services.AddDbContext<TodoContext>(opt =>
  23. opt.UseInMemoryDatabase("TodoList"));
  24. services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
  25. }
  26. // This method gets called by the runtime. Use this method to configure the HTTP
  27. //request pipeline.
  28. public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  29. {
  30. if (env.IsDevelopment())
  31. {
  32. app.UseDeveloperExceptionPage();
  33. }
  34. else
  35. {
  36. // The default HSTS value is 30 days. You may want to change this for
  37. // production scenarios, see https://aka.ms/aspnetcore-hsts.
  38. app.UseHsts();
  39. }
  40. app.UseHttpsRedirection();
  41. app.UseMvc();
  42. }
  43. }
  44. }

前面的代码:

  • 删除未使用的 using 声明。
  • 将数据库上下文添加到 DI 容器。
  • 指定数据库上下文将使用内存中数据库。

添加控制器Add a controller

  • 右键单击 Controllers 文件夹。

  • 选择“添加”>“新项” 。

  • 在“添加新项”对话框中,选择“API 控制器类”模板 。

  • 将类命名为 TodoController,然后选择“添加” 。

“添加新项”对话框,“控制器”显示在搜索框中,并且“Web API 控制器”已选中

  • 在“控制器”文件夹中,创建名为 TodoController 的类 。
  • 将模板代码替换为以下代码:
  1. using Microsoft.AspNetCore.Mvc;
  2. using Microsoft.EntityFrameworkCore;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using TodoApi.Models;
  7. namespace TodoApi.Controllers
  8. {
  9. [Route("api/[controller]")]
  10. [ApiController]
  11. public class TodoController : ControllerBase
  12. {
  13. private readonly TodoContext _context;
  14. public TodoController(TodoContext context)
  15. {
  16. _context = context;
  17. if (_context.TodoItems.Count() == 0)
  18. {
  19. // Create a new TodoItem if collection is empty,
  20. // which means you can't delete all TodoItems.
  21. _context.TodoItems.Add(new TodoItem { Name = "Item1" });
  22. _context.SaveChanges();
  23. }
  24. }
  25. }
  26. }

前面的代码:

  • 定义了没有方法的 API 控制器类。
  • 使用 [ApiController] 属性标记类。此属性指示控制器响应 Web API 请求。有关该属性启用的特定行为的信息,请参阅 使用 ASP.NET Core 创建 Web API
  • 使用 DI 将数据库上下文 (TodoContext) 注入到控制器中。数据库上下文将在控制器中的每个 CRUD 方法中使用。
  • 如果数据库为空,则将名为 Item1 的项添加到数据库。此代码位于构造函数中,因此在每次出现新 HTTP 请求时运行。如果删除所有项,则构造函数会在下次调用 API 方法时再次创建 Item1。因此删除可能看上去不起作用,不过实际上确实有效。

添加 Get 方法Add Get methods

若要提供检索待办事项的 API,请将以下方法添加到 TodoController 类中:

  1. // GET: api/Todo
  2. [HttpGet]
  3. public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
  4. {
  5. return await _context.TodoItems.ToListAsync();
  6. }
  7. // GET: api/Todo/5
  8. [HttpGet("{id}")]
  9. public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
  10. {
  11. var todoItem = await _context.TodoItems.FindAsync(id);
  12. if (todoItem == null)
  13. {
  14. return NotFound();
  15. }
  16. return todoItem;
  17. }

这些方法实现两个 GET 终结点:

  • GET /api/todo
  • GET /api/todo/{id}

如果应用仍在运行,请停止它。然后再次运行它以包括最新更改。

通过从浏览器调用两个终结点来测试应用。例如:

以下 HTTP 响应通过调用 GetTodoItems 来生成:

  1. [
  2. {
  3. "id": 1,
  4. "name": "Item1",
  5. "isComplete": false
  6. }
  7. ]

路由和 URL 路径Routing and URL paths

[HttpGet] 属性表示响应 HTTP GET 请求的方法。每个方法的 URL 路径构造如下所示:

  • 在控制器的 Route 属性中以模板字符串开头:
  1. namespace TodoApi.Controllers
  2. {
  3. [Route("api/[controller]")]
  4. [ApiController]
  5. public class TodoController : ControllerBase
  6. {
  7. private readonly TodoContext _context;
  • [controller] 替换为控制器的名称,按照惯例,在控制器类名称中去掉“Controller”后缀。对于此示例,控制器类名称为“Todo”控制器,因此控制器名称为“todo” 。ASP.NET Core 路由不区分大小写。

  • 如果 [HttpGet] 属性具有路由模板(例如 [HttpGet("products")]),则将它追加到路径。此示例不使用模板。有关详细信息,请参阅使用 Http [Verb] 特性的特性路由

在下面的 GetTodoItem 方法中,"{id}" 是待办事项的唯一标识符的占位符变量。调用 GetTodoItem 时,URL 中 "{id}" 的值会在 id 参数中提供给方法。

  1. // GET: api/Todo/5
  2. [HttpGet("{id}")]
  3. public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
  4. {
  5. var todoItem = await _context.TodoItems.FindAsync(id);
  6. if (todoItem == null)
  7. {
  8. return NotFound();
  9. }
  10. return todoItem;
  11. }

返回值Return values

GetTodoItemsGetTodoItem 方法的返回类型是 ActionResult<T> 类型ASP.NET Core 自动将对象序列化为 JSON,并将 JSON 写入响应消息的正文中。在假设没有未经处理的异常的情况下,此返回类型的响应代码为 200。未经处理的异常将转换为 5xx 错误。

ActionResult 返回类型可以表示大范围的 HTTP 状态代码。例如,GetTodoItem 可以返回两个不同的状态值:

  • 如果没有任何项与请求的 ID 匹配,则该方法将返回 404 NotFound 错误代码。
  • 否则,此方法将返回具有 JSON 响应正文的 200。返回 item 则产生 HTTP 200 响应。

测试 GetTodoItems 方法Test the GetTodoItems method

本教程使用 Postman 测试 Web API。

  • 安装 Postman
  • 启动 Web 应用。
  • 启动 Postman。
  • 禁用 SSL 证书验证
  • 在“文件” >“设置”(“常规” 选项卡)中,禁用“SSL 证书验证”。
  • 在“Postman” > “首选项”(“常规”选项卡)中,禁用“SSL 证书验证” 。或者,选择扳手图标并选择“设置”,然后禁用“SSL 证书验证” 。

警告

在测试控制器之后重新启用 SSL 证书验证。

使用 Get 请求的 Postman

添加创建方法Add a Create method

在 Controllers / TodoController.cs 中添加以下 PostTodoItem 方法 :

  1. // POST: api/Todo
  2. [HttpPost]
  3. public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem item)
  4. {
  5. _context.TodoItems.Add(item);
  6. await _context.SaveChangesAsync();
  7. return CreatedAtAction(nameof(GetTodoItem), new { id = item.Id }, item);
  8. }

前面的代码是 HTTP POST 方法,如 [HttpPost] 属性所指示。该方法从 HTTP 请求正文获取待办事项的值。

CreatedAtAction 方法:

  • 如果成功,则返回 HTTP 201 状态代码。HTTP 201 是在服务器上创建新资源的 HTTP POST 方法的标准响应。

  • Location 标头添加到响应。Location 标头指定新建的待办事项的 URI。有关详细信息,请参阅创建的 10.2.2 201

  • 引用 GetTodoItem 操作以创建 Location 标头的 URI。C# nameof 关键字用于避免在 CreatedAtAction 调用中硬编码操作名称。

  1. // GET: api/Todo/5
  2. [HttpGet("{id}")]
  3. public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
  4. {
  5. var todoItem = await _context.TodoItems.FindAsync(id);
  6. if (todoItem == null)
  7. {
  8. return NotFound();
  9. }
  10. return todoItem;
  11. }

测试 PostTodoItem 方法Test the PostTodoItem method

  • 生成项目。

  • 在 Postman 中,将 HTTP 方法设置为 POST

  • 选择“正文”选项卡 。

  • 选择“原始”单选按钮 。

  • 将类型设置为 JSON (application/json)

  • 在请求正文中,输入待办事项的 JSON:

  1. {
  2. "name":"walk dog",
  3. "isComplete":true
  4. }
  • 选择Send

使用创建请求的 Postman

如果收到 405 不允许的方法错误,则可能是由于未在添加 PostTodoItem 方法之后编译项目。

测试位置标头 URITest the location header URI

  • Headers 窗格中选择Response 选项卡。

  • 复制Location 标头值:

Postman 控制台的“标头”选项卡

添加 PutTodoItem 方法Add a PutTodoItem method

添加以下 PutTodoItem 方法:

  1. // PUT: api/Todo/5
  2. [HttpPut("{id}")]
  3. public async Task<IActionResult> PutTodoItem(long id, TodoItem item)
  4. {
  5. if (id != item.Id)
  6. {
  7. return BadRequest();
  8. }
  9. _context.Entry(item).State = EntityState.Modified;
  10. await _context.SaveChangesAsync();
  11. return NoContent();
  12. }

PutTodoItemPostTodoItem 类似,但是使用的是 HTTP PUT。响应是 204(无内容)根据 HTTP 规范,PUT 请求需要客户端发送整个更新的实体,而不仅仅是更改。若要支持部分更新,请使用 HTTP PATCH

如果在调用 PutTodoItem 时出错,请调用 GET 以确保数据库中有项目。

测试 PutTodoItem 方法Test the PutTodoItem method

本示例使用内存内、数据库,每次启动应用时都必须对其进行初始化。在进行 PUT 调用之前,数据库中必须有一个项。调用 GET,以确保在调用 PUT 之前数据库中存在项目。

更新 id = 1 的待办事项并将其名称设置为“feed fish”:

  1. {
  2. "ID":1,
  3. "name":"feed fish",
  4. "isComplete":true
  5. }

下图显示 Postman 更新:

显示 204(无内容)响应的 Postman 控制台

添加 DeleteTodoItem 方法Add a DeleteTodoItem method

添加以下 DeleteTodoItem 方法:

  1. // DELETE: api/Todo/5
  2. [HttpDelete("{id}")]
  3. public async Task<IActionResult> DeleteTodoItem(long id)
  4. {
  5. var todoItem = await _context.TodoItems.FindAsync(id);
  6. if (todoItem == null)
  7. {
  8. return NotFound();
  9. }
  10. _context.TodoItems.Remove(todoItem);
  11. await _context.SaveChangesAsync();
  12. return NoContent();
  13. }

DeleteTodoItem 响应是 204(无内容)

测试 DeleteTodoItem 方法Test the DeleteTodoItem method

使用 Postman 删除待办事项:

可通过示例应用删除所有项。但如果删除最后一项,则在下次调用 API 时,模型类构造函数会创建一个新项。

使用 JavaScript 调用 Web APICall the web API with JavaScript

在本部分中,将添加一个 HTML 页面,该页面使用 JavaScript 调用 Web API。jQuery 可启动该请求。JavaScript 会使用 Web API 响应的详细信息来更新页面。

通过下面突出显示的代码更新 Startup.cs,配置应用来提供静态文件实现默认文件映射

  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  2. {
  3. if (env.IsDevelopment())
  4. {
  5. app.UseDeveloperExceptionPage();
  6. }
  7. else
  8. {
  9. // The default HSTS value is 30 days. You may want to change this for
  10. // production scenarios, see https://aka.ms/aspnetcore-hsts.
  11. app.UseHsts();
  12. }
  13. app.UseDefaultFiles();
  14. app.UseStaticFiles();
  15. app.UseHttpsRedirection();
  16. app.UseMvc();
  17. }

在项目目录中创建 wwwroot 文件夹。

将一个名为 index.html 的 HTML 文件添加到 wwwroot 目录 。用以下标记替代其内容:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>To-do CRUD</title>
  6. <style>
  7. input[type='submit'], button, [aria-label] {
  8. cursor: pointer;
  9. }
  10. #spoiler {
  11. display: none;
  12. }
  13. table {
  14. font-family: Arial, sans-serif;
  15. border: 1px solid;
  16. border-collapse: collapse;
  17. }
  18. th {
  19. background-color: #0066CC;
  20. color: white;
  21. }
  22. td {
  23. border: 1px solid;
  24. padding: 5px;
  25. }
  26. </style>
  27. </head>
  28. <body>
  29. <h1>To-do CRUD</h1>
  30. <h3>Add</h3>
  31. <form action="javascript:void(0);" method="POST" onsubmit="addItem()">
  32. <input type="text" id="add-name" placeholder="New to-do">
  33. <input type="submit" value="Add">
  34. </form>
  35. <div id="spoiler">
  36. <h3>Edit</h3>
  37. <form class="my-form">
  38. <input type="hidden" id="edit-id">
  39. <input type="checkbox" id="edit-isComplete">
  40. <input type="text" id="edit-name">
  41. <input type="submit" value="Save">
  42. <a onclick="closeInput()" aria-label="Close">&#10006;</a>
  43. </form>
  44. </div>
  45. <p id="counter"></p>
  46. <table>
  47. <tr>
  48. <th>Is Complete</th>
  49. <th>Name</th>
  50. <th></th>
  51. <th></th>
  52. </tr>
  53. <tbody id="todos"></tbody>
  54. </table>
  55. <script src="https://code.jquery.com/jquery-3.3.1.min.js"
  56. integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
  57. crossorigin="anonymous"></script>
  58. <script src="site.js"></script>
  59. </body>
  60. </html>

将名为 site.js 的 JavaScript 文件添加到 wwwroot 目录 。用以下代码替代其内容:

const uri = "api/todo";
let todos = null;
function getCount(data) {
  const el = $("#counter");
  let name = "to-do";
  if (data) {
    if (data > 1) {
      name = "to-dos";
    }
    el.text(data + " " + name);
  } else {
    el.text("No " + name);
  }
}

$(document).ready(function() {
  getData();
});

function getData() {
  $.ajax({
    type: "GET",
    url: uri,
    cache: false,
    success: function(data) {
      const tBody = $("#todos");

      $(tBody).empty();

      getCount(data.length);

      $.each(data, function(key, item) {
        const tr = $("<tr></tr>")
          .append(
            $("<td></td>").append(
              $("<input/>", {
                type: "checkbox",
                disabled: true,
                checked: item.isComplete
              })
            )
          )
          .append($("<td></td>").text(item.name))
          .append(
            $("<td></td>").append(
              $("<button>Edit</button>").on("click", function() {
                editItem(item.id);
              })
            )
          )
          .append(
            $("<td></td>").append(
              $("<button>Delete</button>").on("click", function() {
                deleteItem(item.id);
              })
            )
          );

        tr.appendTo(tBody);
      });

      todos = data;
    }
  });
}

function addItem() {
  const item = {
    name: $("#add-name").val(),
    isComplete: false
  };

  $.ajax({
    type: "POST",
    accepts: "application/json",
    url: uri,
    contentType: "application/json",
    data: JSON.stringify(item),
    error: function(jqXHR, textStatus, errorThrown) {
      alert("Something went wrong!");
    },
    success: function(result) {
      getData();
      $("#add-name").val("");
    }
  });
}

function deleteItem(id) {
  $.ajax({
    url: uri + "/" + id,
    type: "DELETE",
    success: function(result) {
      getData();
    }
  });
}

function editItem(id) {
  $.each(todos, function(key, item) {
    if (item.id === id) {
      $("#edit-name").val(item.name);
      $("#edit-id").val(item.id);
      $("#edit-isComplete")[0].checked = item.isComplete;
    }
  });
  $("#spoiler").css({ display: "block" });
}

$(".my-form").on("submit", function() {
  const item = {
    name: $("#edit-name").val(),
    isComplete: $("#edit-isComplete").is(":checked"),
    id: $("#edit-id").val()
  };

  $.ajax({
    url: uri + "/" + $("#edit-id").val(),
    type: "PUT",
    accepts: "application/json",
    contentType: "application/json",
    data: JSON.stringify(item),
    success: function(result) {
      getData();
    }
  });

  closeInput();
  return false;
});

function closeInput() {
  $("#spoiler").css({ display: "none" });
}

可能需要更改 ASP.NET Core 项目的启动设置,以便对 HTML 页面进行本地测试:

  • 打开 Properties\launchSettings.json 。
  • 删除 launchUrl 以便在项目的默认文件 index.html 强制打开应用 。

此示例调用 Web API 的所有 CRUD 方法。以下是 API 调用的说明。

获取待办事项的列表Get a list of to-do items

jQuery 将向 Web API 发送 HTTP GET 请求,该 API 返回表示待办事项的数组的 JSON。如果请求成功,则调用 success 回调函数。在该回调中使用待办事项信息更新 DOM。

$(document).ready(function() {
  getData();
});

function getData() {
  $.ajax({
    type: "GET",
    url: uri,
    cache: false,
    success: function(data) {
      const tBody = $("#todos");

      $(tBody).empty();

      getCount(data.length);

      $.each(data, function(key, item) {
        const tr = $("<tr></tr>")
          .append(
            $("<td></td>").append(
              $("<input/>", {
                type: "checkbox",
                disabled: true,
                checked: item.isComplete
              })
            )
          )
          .append($("<td></td>").text(item.name))
          .append(
            $("<td></td>").append(
              $("<button>Edit</button>").on("click", function() {
                editItem(item.id);
              })
            )
          )
          .append(
            $("<td></td>").append(
              $("<button>Delete</button>").on("click", function() {
                deleteItem(item.id);
              })
            )
          );

        tr.appendTo(tBody);
      });

      todos = data;
    }
  });
}

添加待办事项Add a to-do item

jQuery 发送 HTTP POST 请求,请求正文中包含待办事项。acceptscontentType 选项设置为 application/json,以便指定接收和发送的媒体类型。待办事项使用 JSON.stringify 转换为 JSON。当 API 返回成功状态的代码时,将调用 getData 函数来更新 HTML 表。

function addItem() {
  const item = {
    name: $("#add-name").val(),
    isComplete: false
  };

  $.ajax({
    type: "POST",
    accepts: "application/json",
    url: uri,
    contentType: "application/json",
    data: JSON.stringify(item),
    error: function(jqXHR, textStatus, errorThrown) {
      alert("Something went wrong!");
    },
    success: function(result) {
      getData();
      $("#add-name").val("");
    }
  });
}

更新待办事项Update a to-do item

更新待办事项与添加类似。url 更改为添加项的唯一标识符,并且 typePUT

$.ajax({
  url: uri + "/" + $("#edit-id").val(),
  type: "PUT",
  accepts: "application/json",
  contentType: "application/json",
  data: JSON.stringify(item),
  success: function(result) {
    getData();
  }
});

删除待办事项Delete a to-do item

若要删除待办事项,请将 AJAX 调用上的 type 设为 DELETE 并指定该项在 URL 中的唯一标识符。

$.ajax({
  url: uri + "/" + id,
  type: "DELETE",
  success: function(result) {
    getData();
  }
});

向 Web API 添加身份验证支持Add authentication support to a web API

ASP.NET Core 标识将用户界面 (UI) 登录功能添加到 ASP.NET Core Web 应用。若要保护 Web API 和 SPA,请使用以下项之一:

IdentityServer4 是适用于 ASP.NET Core 3.0 的 OpenID Connect 和 OAuth 2.0 框架。IdentityServer4 支持以下安全功能:

  • 身份验证即服务 (AaaS)
  • 跨多个应用程序类型的单一登录/注销 (SSO)
  • API 的访问控制
  • Federation Gateway

有关详细信息,请参阅欢迎使用 IdentityServer4

其他资源Additional resources

查看或下载本教程的示例代码请参阅如何下载

有关更多信息,请参见以下资源: