添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
重情义的鸵鸟  ·  Python IDLE Shell ...·  1 年前    · 
重情义的甘蔗  ·  visual studio 2017 ...·  1 年前    · 
行走的豆芽  ·  Spark SQL case ...·  2 年前    · 

作者: Tim Deschryver Rick Anderson

本教程介绍生成使用数据库的基于控制器的 Web API 的基础知识。 在 ASP.NET Core 中创建 API 的另一种方法是创建最小 API 。 有关在最小 API 和基于控制器的 API 之间进行选择的帮助,请参阅 API 概述 。 有关创建最小 API 的教程,请参阅 教程:使用 ASP.NET Core 创建最小 API

本教程将创建以下 API:

  • .NET 9.0 SDK
  • 可以按照 macOS、Linux 或 Windows 上的 Visual Studio Code 指南进行操作。 如果使用 Visual Studio Code 以外的集成开发环境(IDE),可能需要更改。

    创建 Web API 项目

    Visual Studio Visual Studio Code
  • 在搜索框中输入“Web API”
  • 选择“ASP.NET Core Web API” 模板,然后选择“下一步”
  • 在“配置新项目” 对话框中,将项目命名为“TodoApi” ,然后选择“下一步”
  • 在“其他信息” 对话框中:
    • 确认 Framework .NET 9.0(标准期限支持)
    • 确认已选中“启用 OpenAPI 支持” 复选框。
    • 确认已选中“使用控制器(取消选中以使用最小 API)”复选框
    • 选择“创建”
    • 添加 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.jsontasks.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.DesignMicrosoft.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;
      

      GetTodoItemsGetTodoItem 方法的返回类型是 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();
      

      PutTodoItemPostTodoItem 类似,但使用的是 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 MAUIUnity)的 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.jsontasks.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;
        

        GetTodoItemsGetTodoItem 方法的返回类型是 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();
        

        PutTodoItemPostTodoItem 类似,但使用的是 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.jsontasks.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
          
        • [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;
          

          GetTodoItemsGetTodoItem 方法的返回类型是 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();
          

  •