ASP.NET学习记录-11-实践1-使用 ASP.NET Core 生成基于控制器的 Web API
1、初始化项目
本实践将创建以下 API:

| API | 说明 | 请求正文 | 响应正文 |
|---|---|---|---|
GET /api/todoitems |
获取所有待办事项 | 无 | 待办事项的数组 |
GET /api/todoitems/{id} |
按 ID 获取项 | 无 | 待办事项 |
POST /api/todoitems |
添加新项 | 待办事项 | 待办事项 |
PUT /api/todoitems/{id} |
更新现有项 | 待办事项 | 无 |
DELETE /api/todoitems/{id} |
删除项 | 无 | 无 |
1.初始化项目
首先要确保安装必备的相关内容,即ASP.NET和Web开发。

在此基础上,创建项目:
首先选择合理的项目模板


随后选择并输入名称,具体内容随意

依据需求选择,本次不使用
HTTPS,随后创建项目。
2.项目内容介绍:

该项目是一个典型的 ASP.NET Core Web 应用项目,采用标准的 MVC(模型-视图-控制器)分层架构。
1. 项目整体架构
- 分层逻辑:
- 控制器(Controllers):处理 HTTP 请求,协调业务逻辑与视图/数据返回。
- 模型(Models):定义数据结构(如
WeatherForecast.cs),用于数据传输与业务处理。 - 配置与入口(Program.cs / appsettings.json):全局配置与启动流程。
- 依赖管理(Dependencies):管理第三方库与框架依赖。
2. 关键文件与文件夹说明
**(1) **WebApp(项目根目录)
- 作用:项目主体代码存放位置,包含应用核心逻辑和配置。
**(2) **Connected Services
- 作用:管理与外部服务的连接配置(如 Azure 服务、数据库连接)
**(3) **Properties 与 launchSettings.json
launchSettings.json:作用:定义应用的启动配置(如开发环境变量、启动 URL、端口号)。
示例配置项
1
2
3
4
5
6
7
8
9
10"profiles": {
"WebApp": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
**(4) **Dependencies
- 作用:管理项目依赖项,包括:
- NuGet 包:第三方库(如 Entity Framework Core、Swagger)。
- 分析器(Analyzers):静态代码分析工具,帮助检测潜在代码问题。
- 框架(Frameworks):.NET Core SDK 和运行时依赖。
**(5) **Controllers/WeatherForecastController.cs
作用:控制器类,处理
/WeatherForecast相关的 HTTP 请求(如 GET/POST)。典型代码逻辑:
1
2
3
4
5
6
7
8
9
10[]
[]
public class WeatherForecastController : ControllerBase
{
[]
public IEnumerable<WeatherForecast> Get()
{
// 生成天气数据并返回
}
}
**(6) **appsettings.json
作用:存储应用配置(如数据库连接字符串、日志级别、自定义参数)。
示例配置:
1
2
3
4
5
6
7
8
9{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
**(7) **Program.cs
作用:程序入口,配置主机(Host)、服务容器(DI)、中间件管道。
核心代码流程:
1
2
3
4
5
6var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); // 注册控制器
var app = builder.Build();
app.UseHttpsRedirection();
app.MapControllers(); // 路由映射
app.Run();
**(8) **WeatherForecast.cs
作用:数据模型类,定义天气预测数据的结构(如日期、温度)。
示例代码:
1
2
3
4
5
6public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
}
**(9) **WebApp.http
作用:HTTP 请求测试文件(类似 Postman 的快捷脚本),用于快速调试 API 接口。
示例请求:
1
2GET https://localhost:5001/WeatherForecast
Accept: application/json
3. 协作流程示例
- 启动应用:根据
launchSettings.json配置启动环境与端口。 - 加载配置:
Program.cs读取appsettings.json,初始化服务容器。 - 处理请求:用户访问
/WeatherForecast,WeatherForecastController生成数据并返回 JSON。 - 依赖管理:通过
Dependencies中的包支持数据库操作或日志记录。
4.具体代码解析
1.WeatherForecast.cs
1 | namespace WebApp |
2.WeatherForecastController.cs
Controllers\WeatherForecastController.cs
1 | using Microsoft.AspNetCore.Mvc; |
3.Program.cs
Program.cs
1 | // 创建Web应用程序构建器,用于配置应用程序的服务和中间件 |

将项目按照SpringBoot的模板进行文件内容重构,以适应架构内容。

1.VO(View Object)视图对象
用于展示层(如Web或移动端界面),根据前端需求聚合或格式化数据。例如:
1
2
3
4 public class UserVO {
private String displayName;
private String formattedEmail; // 例如"xxx@xxx.com"
}VO的字段可能与数据库字段不完全一致,且可能包含界面特有的逻辑(如日期格式化)
2.DTO(Data Transfer Object)数据传输对象
用于跨层(如服务层与控制器层)或跨服务(如微服务调用)传输数据,通常用于减少网络传输量或隐藏敏感字段。例如:
1
2
3
4 public class UserDTO {
private String name;
private String email; // 不暴露密码字段
}DTO的设计注重网络效率和安全性
2、新建模型类
- 在“解决方案资源管理器”中,右键单击项目。 选择添加>新建文件夹。 将该文件夹命名为
Models。 - 右键单击文件夹,然后选择添加>类。 将类命名为
TodoItem,然后选择“添加”。

Pojo\DTO\TodoItem.cs
1 | namespace WebApp.Pojo.DTO |
3、添加数据库上下文
数据库上下文是为数据模型协调 Entity Framework 功能的主类。 此类由 Microsoft.EntityFrameworkCore.DbContext 类派生而来。因此需要先安装Microsoft.EntityFrameworkCore.DbContextNuGet包。
- 右键单击文件夹,然后选择添加>类。 将类命名为 TodoContext,然后单击“添加”。
Mapper\TodoContext.cs
1 | using Microsoft.EntityFrameworkCore; |
4、注册数据库上下文
在 ASP.NET Core 中,服务(如数据库上下文)必须向依赖关系注入 (DI) 容器进行注册。 该容器向控制器提供服务。在使用之前,需要安装Microsoft.EntityFrameworkCore.InMemory。
Program.cs
1 | // 引入所需的命名空间 |
5、构建控制器
右键单击
Controllers文件夹。选择添加>New Scaffolded Item。
选择“其操作使用实体框架的 API 控制器”,然后选择“添加”。
在“添加其操作使用实体框架的 API 控制器”对话框中:
在“模型类”中选择“TodoItem (TodoApi.Models)”。

在“数据上下文类”中选择“TodoContext (TodoAPI.Models)”。

选择“添加”,会自动下载安装所需内容,并且会自动生成一系列代码。


Controllers\TodoItemsController.cs
1 | using System; |
生成的代码:
- 使用
[ApiController]属性标记类。 此属性指示控制器响应 Web API 请求。 有关该属性启用的特定行为的信息,请参阅使用 ASP.NET Core 创建 Web API。 - 使用 DI 将数据库上下文 (
TodoContext) 注入到控制器中。 数据库上下文将在控制器中的每个 CRUD 方法中使用。
ASP.NET Core 模板:
- 具有视图的控制器在路由模板中包含
[action]。 - API 控制器不在路由模板中包含
[action]。
[action] 令牌不在路由模板中时,终结点中不包含 action 名称(方法名称)。 也就是说,不会在匹配的路由中使用操作的关联方法名称。

ASP.NET Core 中 MVC 控制器与 API 控制器的路由设计差异解析
在 ASP.NET Core 中,带有视图的控制器(传统 MVC) 和 API 控制器(Web API) 的路由模板设计存在显著差异,主要体现在是否包含
[action]占位符。这种差异源于两者不同的应用场景和设计目标。以下从 设计原则、路由配置 和 实际影响 三方面详细解释:
1. 设计原则与目标
类型 核心目标 路由设计导向 MVC 控制器 渲染视图页面,支持用户交互(如表单提交、页面跳转) 明确区分不同 Action 对应的视图路径 API 控制器 提供数据接口,遵循 RESTful 规范(资源化操作) 通过 HTTP 方法区分操作,隐藏 Action 名
2. 路由模板对比
(1) 传统 MVC 控制器(包含
[action])
默认路由模板:
1
2
3 app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");URL 示例:
/Home/Index→HomeController.Index()/Product/Details/5→ProductController.Details(int id)必要性:
- 每个 Action 对应独立的视图文件(如
Index.cshtml、Details.cshtml),需通过 URL 路径明确指定 Action 名称以渲染正确视图。- 支持多 Action 多视图的灵活跳转(如导航菜单链接到不同页面)。
(2) API 控制器(不包含
[action])
典型路由配置:
1
2
3
4
5
6
7
8
9
10 []
[] // 路由模板中不包含 [action]
public class WeatherForecastController : ControllerBase
{
[] // 通过 HTTP 方法区分操作
public IEnumerable<WeatherForecast> Get() { ... }
[]
public IActionResult Create([FromBody] WeatherForecast data) { ... }
}URL 示例:
GET /WeatherForecast→ 获取天气数据POST /WeatherForecast→ 创建新天气数据设计优势:
- 符合 RESTful 规范:URL 表示资源(如
/WeatherForecast),HTTP 方法(GET、POST)表示操作类型(读、写)。- 简化 URL 结构:避免暴露内部方法名,提升接口可读性和安全性。
- 依赖 HTTP 语义:利用标准方法(GET/POST/PUT/DELETE)而非 URL 路径区分功能。
3. 实际影响与配置差异
(1) 路由注册方式
- MVC 控制器:
通常使用 约定路由(MapControllerRoute)全局定义模板,依赖[action]映射视图。- API 控制器:
优先使用 属性路由([Route]+[HttpMethod]),直接为每个 Action 指定端点,无需依赖[action]。(2)
[ApiController]特性的作用
- 自动推断路由:若未显式指定
[Route],默认生成/ControllerName的路由。- 禁用视图查找:强制 API 控制器仅返回数据(如 JSON),而非视图。
- 模型验证:自动响应 400 Bad Request 验证错误。
(3) 如何强制 API 控制器包含
[action](不推荐)若需兼容旧版风格,可手动添加
[action],但违背 RESTful 设计:
1
2
3
4
5
6
7 []
[] // 显式包含 [action]
public class WeatherForecastController : ControllerBase
{
[]
public IEnumerable<WeatherForecast> List() { ... } // URL: /WeatherForecast/List
}
4. 项目结构图中的体现
在您提供的项目图中,
WeatherForecastController.cs属于 API 控制器:
- 未在路由模板中包含
[action]:
- 通过
[Route("[controller]")]定义基础路径/WeatherForecast。- 使用
[HttpGet]等特性标识方法,无需 URL 路径区分操作。- 符合 RESTful 风格:
- 所有操作均围绕
/WeatherForecast资源展开,通过 HTTP 方法(GET/POST)区分功能。
总结
- MVC 控制器:需通过
[action]明确指定视图路径,支持多页面交互。- API 控制器:省略
[action]以简化 URL,强调资源与 HTTP 方法的语义化操作,符合现代 API 设计趋势。- 最佳实践:
- API 设计:优先使用属性路由,避免
[action]暴露内部逻辑。- 混合项目:若同时存在 MVC 和 API 控制器,可通过
MapControllers()和MapControllerRoute()分别注册路由。
6、路由和 URL 路径
[HttpGet] 属性表示响应 HTTP GET 请求的方法。 每个方法的 URL 路径构造如下所示:
在控制器的
Route属性中以模板字符串开头:1
2
3
4
5
6/// <summary>
/// 待办事项控制器,提供待办事项的CRUD API接口
/// </summary>
[] // 定义API路由为 "api/TodoItems"
[] // 标识此类为API控制器
public class TodoItemsController : ControllerBase将
[controller]替换为控制器的名称,按照惯例,在控制器类名称中去掉“Controller”后缀。 对于此示例,控制器类名称为“TodoItems”控制器,因此控制器名称为“TodoItems”。 ASP.NET Core 路由不区分大小写。如果
[HttpGet]属性具有路由模板(例如[HttpGet("products")]),则将它追加到路径。 此示例不使用模板。 有关详细信息,请参阅[使用 Http Verb] 特性的特性路由。
在下面的 GetTodoItem 方法中,"{id}" 是待办事项的唯一标识符的占位符变量。 调用 GetTodoItem 时,URL 中 "{id}" 的值会在 id 参数中提供给方法。
1 | /// <summary> |
7、返回值
GetTodoItems 和 GetTodoItem 方法的返回类型是 ActionResult 类型。 ASP.NET Core 自动将对象序列化为 JSON,并将 JSON 写入响应消息的正文中。 此返回类型的响应代码为 200 OK(假设没有未处理的异常)。 未经处理的异常将转换为 5xx 错误。
ActionResult 返回类型可以表示大范围的 HTTP 状态代码。 例如,GetTodoItem 可以返回两个不同的状态值:
8、防止过度公开
目前,示例应用公开了整个 TodoItem 对象。 生产应用通常使用模型的子集来限制输入和返回的数据。 这背后有多种原因,但安全性是主要原因。 模型的子集通常称为数据传输对象 (DTO)、输入模型或视图模型。 本教程使用了 DTO。
DTO 可用于:
- 防止过度发布。
- 隐藏客户端不应查看的属性。
- 省略一些属性以缩减有效负载大小。
- 平展包含嵌套对象的对象图。 对客户端而言,平展的对象图可能更方便。
将TodoItem 拆分为DTO和Entity
Pojo\DTO\TodoItemDTO.cs
1 | namespace WebApp.Pojo.DTO |
Pojo\Entity\TodoItem.cs
1 | namespace WebApp.Pojo.Entity |
Mapper\TodoContext.cs
1 | using Microsoft.EntityFrameworkCore; |
随后更新 TodoItemsController 以使用 TodoItemDTO:
Controllers\TodoItemsController.cs
1 | using System; |
9、从ASP.NET Core空构建最简化项目
1、初始化项目
在初始化项目的时候,采用
ASP.NET Core空初始化项目,并取消HTTPS

并添加一系列预留文件夹,模板参照SpingBoot

2、创建TodoItem 相关的类
Pojo\DTO\TodoItemDTO.cs
1 | namespace WebApp.Pojo.DTO |
Pojo\Entity\TodoItem.cs
1 | namespace WebApp.Pojo.Entity |
Pojo\VO\TodoItemVO.cs
1 | namespace WebApp.Pojo.VO |
3、构建TodoItemController类
创建TodoItemController,并继承ControllerBase,并利用“注解”标注为[ApiController]和[Route("api/[controller]")]。
1 | using Microsoft.AspNetCore.Mvc; |
4、注册使用“控制器服务”
在Program.cs中使用控制器。builder.Services.AddControllers();、app.MapControllers();
1 | // 创建一个 Web 应用程序构建器,用于配置应用程序的服务和中间件 |
这个时候启动便可以通过”api/TodoItem“查看是否启动成功。

5、构建详细内容
1、数据库
在DB/TodoItemDataBase.cs中模拟数据库相关操作。
1 | using WebApp.Pojo.Entity; |
2、Mapper
ITodoItemMapper 接口详细介绍
- 设计目的与原则
ITodoItemMapper 接口是应用程序数据访问层的核心组件,其设计遵循以下关键原则:
- 数据映射器模式(Data Mapper Pattern):将数据层与领域模型分离,负责数据在持久化存储和内存对象之间的转换
- 单一职责原则:专注于待办事项数据的访问操作,不涉及业务逻辑
- 依赖倒置原则:高层模块依赖于抽象接口,而非具体实现
- 接口隔离原则:只提供必要的方法,保持接口简洁明确
方法详解
GetAllTodoItems()
- 功能:检索系统中的所有待办事项记录
- 返回值:包含所有 TodoItem 对象的列表
- 应用场景:用户访问待办事项列表页面时,需要展示所有任务GetTodoItemById(long id)
- 功能:根据唯一标识查找单个待办事项
- 参数:待办事项的ID
- 返回值:找到的 TodoItem 对象,如果不存在则返回 null
- 应用场景:查看特定待办事项详情、编辑前获取数据、验证项目存在性AddTodoItem(TodoItem todoItem)
- 功能:创建新的待办事项记录
- 参数:完整填充的 TodoItem 实体
- 返回值:无,操作成功时无返回值
- 应用场景:用户创建新任务时UpdateTodoItem(long id, TodoItem todoItem)
- 功能:更新现有待办事项的信息
- 参数:
- id:要更新的待办事项标识
- todoItem:包含更新数据的待办事项对象
- 返回值:无,操作成功时无返回值
- 应用场景:用户编辑任务内容、标记任务完成等DeleteTodoItem(long id)
- 功能:删除指定的待办事项
- 参数:待删除项的 ID
- 返回值:无,操作成功时无返回值
- 应用场景:用户删除不再需要的任务在应用架构中的位置
ITodoItemMapper 在应用的多层架构中处于数据访问层:
1 | 表示层(Controller) → 业务逻辑层(Service) → 数据访问层(Mapper) → 数据存储层(DB) |
在这个架构中:
- 控制器接收用户请求
- 服务层实现业务逻辑并调用 Mapper
- Mapper 实现(如 TodoItemMapperImpl) 执行实际的数据操作
- 数据最终存储在数据库或内存集合中
- 接口优势与扩展性
- 测试友好:可以轻松创建模拟实现用于单元测试
- 实现替换:可以在不修改业务逻辑的情况下切换数据源(如从内存存储切换到SQL数据库)
- 代码组织:清晰定义了数据操作的边界,使代码结构更加有序
- 分离关注点:数据访问逻辑与业务规则分离
- 总结
ITodoItemMapper 接口代表了一种清晰的关注点分离设计,通过定义数据操作的契约,使应用程序能够以松耦合、可测试和可扩展的方式处理待办事项数据。这种设计方式促进了代码的可维护性,同时提供了实现替换的灵活性。
1 | using WebApp.Pojo.Entity; |
TodoItemMapperImpl 类是 ITodoItemMapper 接口的具体实现,采用内存存储方式管理待办事项数据。它提供了待办事项的基本CRUD(创建、读取、更新、删除)操作的实现,使用静态类 TodoItemDataBase 作为数据源。
设计特点
内存数据存储模式:
- 使用 TodoItemDataBase.TodoItems 静态集合作为数据源
- 所有操作直接在内存中执行,无需数据库连接
- 适合原型开发、测试和小型应用场景
简单直接的实现:
- 代码简洁清晰,每个方法直接操作静态集合
- 没有额外的数据验证或异常处理
- 利用C#内置的LINQ功能(如Find方法)进行数据查询
接口实现:
- 完全实现了 ITodoItemMapper 接口定义的所有方法
- 保持了方法签名的一致性
- 遵循了依赖倒置原则,允许通过接口引用使用此实现
方法分析
- GetAllTodoItems()
- 实现逻辑:直接返回静态集合的引用 - 性能特点:O(1)时间复杂度,非常高效- GetTodoItemById(long id)
- 实现逻辑:使用LINQ的Find方法查找匹配ID的第一个项目 - 性能特点:O(n)时间复杂度,需要遍历集合- AddTodoItem(TodoItem todoItem)
- 实现逻辑:直接将项目添加到静态集合中 - 性能特点:O(1)时间复杂度,高效- UpdateTodoItem(long id, TodoItem todoItem)
- 实现逻辑:查找匹配项并逐个更新属性 - 性能特点:O(n)时间复杂度,需要遍历集合- DeleteTodoItem(long id)
1. 实现逻辑:查找匹配项并从集合中移除 2. 性能特点:O(n)时间复杂度,需要遍历集合- 在架构中的位置
TodoItemMapperImpl 在应用程序的分层架构中处于数据访问层,具体位置如下:1
2
3表示层 (Controller) → 业务逻辑层 (Service) → 数据访问层 (Mapper) → 数据存储
↓
TodoItemMapperImpl → TodoItemDataBase服务层通过接口引用调用此实现,不直接依赖于具体类,从而实现松耦合设计。
1 | using WebApp.DB; |
3、Services
ITodoItemService 接口详细介绍
- 设计目的与定位
ITodoItemService 接口是 WebApp 应用程序中业务逻辑层的核心组件,它定义了与待办事项相关的所有业务操作。该接口采用了以下设计原则:
- 业务逻辑封装:将所有待办事项相关的业务规则和操作集中在一个接口中管理
- 转换逻辑处理:负责在不同数据模型(DTO、Entity、VO)之间进行转换
- 面向接口编程:通过接口定义服务契约,支持依赖注入和松耦合设计
- 单一职责原则:专注于待办事项这一业务实体的操作
数据模型关系
ITodoItemService 接口处理三种不同的数据模型,每种模型有特定用途:TodoItemDTO (Data Transfer Object):
- 用于接收客户端提交的数据
- 包含最少必要的字段 (Id, Name, IsComplete)
- 适用于数据输入和修改操作
TodoItem (Entity):
- 表示数据库中的实体
- 包含完整的数据字段,包括内部字段 (如 TodoId)
- 用于持久化存储
TodoItemVO (View Object):
- 用于向客户端返回数据
- 包含展示所需的全部字段
- 隐藏内部实现细节,适合前端展示
方法详解
GetAllTodoItems()
- 业务含义:获取系统中所有可见的待办事项
- 处理流程:
- 调用数据访问层获取所有实体
- 将实体集合转换为视图对象集合
- 可能进行过滤(如排除已删除项)或排序
- 返回说明:返回TodoItemVO的列表,确保数据适合前端展示
- 业务规则:可能实现分页、排序或筛选等功能
GetTodoItemById(long id)
- 业务含义:查询特定待办事项的详细信息
- 处理流程:
- 验证ID的有效性
- 调用数据访问层查找对应实体
- 如果找到,将实体转换为视图对象
- 如果未找到,可能抛出业务异常
- 返回说明:返回单个TodoItemVO对象
- 业务规则:确保返回的是有效且未删除的待办事项
AddTodoItem(TodoItemDTO todoItemDTO)
- 业务含义:创建新的待办事项
- 处理流程:
- 验证DTO中的数据有效性
- 创建新的TodoItem实体
- 设置自动生成的字段(如创建时间)
- 调用数据访问层保存实体
- 将保存后的实体转换为视图对象
- 返回说明:返回创建成功的TodoItemVO对象,包含系统生成的ID和时间戳
- 业务规则:处理数据验证、ID生成、重复项检查等业务逻辑
UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
- 业务含义:更新现有待办事项的信息
- 处理流程:
- 验证ID有效性和DTO数据合法性
- 查找现有实体
- 用DTO中的数据更新实体属
- 更新修改时间戳
- 调用数据访问层保存更改
- 将更新后的实体转换为视图对象
- 返回说明:返回更新后的TodoItemVO对象
- 业务规则:处理并发控制、部分更新、状态转换等业务逻辑
DeleteTodoItem(long id)
- 业务含义:删除指定的待办事项
- 处理流程:
- 验证ID有效性
- 查找现有实体并保存副本
- 执行删除操作(可能是物理删除或逻辑删除)
- 将被删除的实体转换为视图对象
- 返回说明:返回删除前的TodoItemVO对象,便于客户端确认删除的内容
- 业务规则:可能实现软删除(标记DeletedAt)而非物理删除
在应用架构中的位置
ITodoItemService 在应用的分层架构中处于业务逻辑层:
1 | 表示层(Controller) → 业务逻辑层(Service) → 数据访问层(Mapper) → 数据存储层(DB) |
- 向上:为控制器提供业务功能,接收DTO并返回VO
- 向下:调用数据访问层接口进行实际的数据操作
- 内部:处理数据转换、业务规则验证和错误处理
- 设计优势
- 关注点分离:业务逻辑与数据访问清晰分离
- 模型转换:专门处理不同数据模型之间的转换
- 统一业务入口:为待办事项提供统一的业务功能入口
- 可测试性:便于对业务逻辑进行单元测试
- 灵活性:通过接口设计支持不同实现,如缓存、事务等扩展
- 总结
ITodoItemService接口是应用程序业务逻辑层的关键组件,负责待办事项的所有业务操作。它通过清晰定义的方法契约,将表示层与数据访问层解耦,同时处理不同数据模型间的转换。这种设计支持更好的代码组织、测试和维护,符合现代企业应用架构的最佳实践。
1 | using WebApp.Pojo.DTO; |
TodoItemServiceImpl 类详细介绍
设计目的与职责
TodoItemServiceImpl 是 ITodoItemService 接口的具体实现,它在 WebApp 应用的分层架构中扮演着业务逻辑层的核心角色。主要职责包括:业务逻辑处理:实现所有待办事项相关的业务操作和规则
数据转换:在三种数据模型间进行转换
- 从 DTO(客户端输入)到 Entity(数据存储)
- 从 Entity(数据存储)到 VO(客户端展示)
数据验证:验证操作的有效性,处理异常情况
数据访问协调:通过依赖注入的 ITodoItemMapper 进行数据访问
架构定位与关系
在应用的分层架构中,TodoItemServiceImpl 的位置如下:
1 | 控制器层 (TodoItemController) |
- 向上:为控制器提供业务服务,处理请求参数和响应
- 向下:通过 ITodoItemMapper 接口访问数据
- 依赖注入设计
类采用了构造函数注入的依赖注入模式:
1 | private readonly ITodoItemMapper _todoItemMapper; |
这种设计有以下优势:
- 松耦合:不直接依赖具体实现,而是依赖抽象接口
- 可测试性:便于在单元测试中注入模拟对象
- 灵活性:可以轻松替换底层数据访问实现
方法分析
GetAllTodoItems()
- 数据流:Mapper → Entity集合 → VO集合 → 控制器
- 转换逻辑:使用LINQ的Select方法批量转换实体为VO
- 性能考虑:适用于中小型数据集,大数据集可能需要分页GetTodoItemById(long id)
- 异常处理:明确处理了项目不存在的情况
- 数据流:ID → Mapper查询 → Entity → VO → 控制器
- 验证逻辑:确保返回有效的待办事项AddTodoItem(TodoItemDTO todoItemDTO)
- 数据创建:从DTO创建新的Entity,设置初始值UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
- 业务流程:查找 → 验证 → 更新 → 保存 → 验证 → 返回
- 状态管理:自动更新UpdatedAt时间戳DeleteTodoItem(long id)
- 业务设计:返回删除前的对象,便于客户端确认删除的内容
- 实现策略:物理删除而非逻辑删除
1 | using WebApp.Mapper; |
4、Controllers
TodoItemController 类详细介绍
设计目的与定位
TodoItemController 是 WebApp 应用程序的表示层组件,作为 ASP.NET Core Web API 控制器,负责处理与待办事项相关的 HTTP 请求。它的主要职责包括:请求接收:接收并处理来自客户端的 HTTP 请求
参数解析:从 URL、查询字符串和请求体中提取参数
服务调用:调用业务逻辑层(ITodoItemService)处理具体业务操作
响应构造:将处理结果包装为 HTTP 响应返回给客户端
作为 MVC/Web API 架构中的 “C”(控制器),它是连接客户端和服务层的桥梁。路由与 API 端点
控制器通过类级别和方法级别的特性定义了 RESTful API 端点:
| HTTP 方法 | 路由 | 方法名称 | 描述 |
|---|---|---|---|
| GET | /api/TodoItem | GetAllTodoItems | 获取所有待办事项列表 |
| GET | /api/TodoItem/{id} | GetTodoItemById | 获取特定ID的待办事项 |
| POST | /api/TodoItem | AddTodoItem | 创建新的待办事项 |
| PUT | /api/TodoItem/{id} | UpdateTodoItem | 更新指定ID的待办事项 |
| DELETE | /api/TodoItem/{id} | DeleteTodoItem | 删除指定ID的待办事项 |
这些端点遵循 RESTful API 设计原则,使用标准的 HTTP 方法对资源进行操作。
- 依赖注入设计
控制器采用构造函数注入的方式获取 ITodoItemService 实例:
1 | private readonly ITodoItemService _todoItemService; |
这种设计有以下优点:
- 松耦合:控制器依赖于抽象接口而非具体实现
- 可测试性:便于在单元测试中注入模拟服务
- 维护性:服务实现可以独立更改,不影响控制器代码
- 数据流与模型使用
控制器处理三种不同的数据模型:
- DTO (Data Transfer Object):从客户端接收数据时使用
- VO (View Object):向客户端返回数据时使用
- Entity:在服务层内部使用,控制器不直接接触
- 数据流如下:
1. 输入:客户端请求 → DTO → 控制器 → 服务层
2. 输出:服务层 → VO → 控制器 → HTTP 响应 → 客户端
这种设计分离了内部数据模型和外部接口,增强了系统的安全性和灵活性。
- 方法详解
1. GetAllTodoItems()
- 作用:获取所有待办事项列表
- 请求格式:GET api/TodoItem
- 响应格式:
- 状态码:200 OK 或服务层抛出的异常
2. GetTodoItemById(long id)
- 作用:获取单个待办事项
- 请求格式:GET api/TodoItem/1
- 响应格式:
- 状态码:200 OK 或找不到时的异常
3. AddTodoItem(TodoItemDTO todoItem)
- 作用:创建新的待办事项
- 请求格式: POST api/TodoItem
- 响应格式:创建后的待办事项VO
- 状态码:当前返回 200 OK
4. UpdateTodoItem(long id, TodoItemDTO todoItem)
- 作用:更新现有待办事项
- 请求格式: PUT api/TodoItem/1
- 响应格式:更新后的待办事项VO
- 状态码:200 OK 或找不到时的异常
5. DeleteTodoItem(long id)
- 作用:删除待办事项
- 请求格式:DELETE api/TodoItem/1
- 响应格式:删除的待办事项信息
- 状态码:200 OK 或找不到时的异常
1 | using Microsoft.AspNetCore.Mvc; |
5、Config
DependencyInjectionExtensions 类详细介绍
设计目的与意义
DependencyInjectionExtensions 类是 WebApp 应用程序中的依赖注入配置中心,它通过扩展方法扩展了 ASP.NET Core 的 IServiceCollection 接口,提供了集中管理和注册应用程序服务的能力。这种设计有以下优点:关注点分离:将服务注册逻辑从启动代码 (Program.cs) 中分离出来
代码组织:按功能或层次分组服务注册,提高代码可读性
可扩展性:可以轻松添加新的服务分类和注册方法
简化主程序:Program.cs 中只需一行代码即可注册所有服务
扩展方法原理
该类使用了 C# 的扩展方法特性,允许向现有类型添加方法而无需修改原始类型:
1 | public static IServiceCollection RegisterApplicationServices(this IServiceCollection services) |
关键点:
- static 修饰符:扩展方法必须是静态的
- this 关键字:表明这是一个扩展方法,作用于 IServiceCollection 类型
- 返回值类型:返回相同的 IServiceCollection 实例,支持方法链式调用
- 服务注册模式
该类采用了层次化的服务注册模式:
顶层方法:RegisterAllServices 作为主入口点,被 Program.cs 直接调用
分组方法:RegisterApplicationServices 处理特定类别的服务注册
扩展能力:预留了添加其他服务分类的位置
这种结构支持应用程序随着规模增长,可以在不修改主方法的情况下添加新的服务分类。依赖注入生命周期
在当前实现中,使用了 AddScoped 方法注册服务,这意味着:
- 在同一个 HTTP 请求作用域内,将获得相同的服务实例
- 不同的 HTTP 请求将获得不同的服务实例
- 适合于大多数业务服务和数据访问服务
- 其他可用的生命周期选项:
- AddTransient:每次请求服务时创建新实例
- AddSingleton:整个应用程序生命周期内共享同一实例
- 服务层次与依赖关系
代码注册了两个主要的服务类型,反映了应用程序的分层架构:
业务服务层:ITodoItemService → TodoItemServiceImpl
- 处理业务逻辑和规则
- 依赖于数据访问层
数据访问层:ITodoItemMapper → TodoItemMapperImpl
- 处理数据持久化和检索
- 通常与数据库或其他存储机制交互
这种注册方式体现了依赖倒置原则,高层模块(业务服务)依赖于抽象(接口),而非具体实现。
适用场景与扩展性
当前实现适用于中小型应用程序,但代码结构已经考虑了扩展性:
- 新服务类型:可以在 RegisterApplicationServices 中添加新的服务注册
- 新服务分类:可以创建新的注册方法(如 RegisterInfrastructureServices)并在 RegisterAllServices 中调用
- 批量注册:可以添加反射或约定注册方法,自动扫描并注册符合特定模式的服务
- 实际应用流程
在应用程序启动过程中,这些扩展方法的调用流程如下:
Program.cs 调用 builder.Services.RegisterAllServices()
RegisterAllServices 调用 RegisterApplicationServices
RegisterApplicationServices 注册具体的服务实现
ASP.NET Core 依赖注入容器配置完成
当控制器或其他组件请求服务时,依赖注入容器提供相应的实例
总结
DependencyInjectionExtensions 类是应用程序依赖注入配置的核心组件,通过扩展方法模式提供了集中、组织化和可扩展的服务注册能力。它采用了层次化的结构设计,支持应用程序按需扩展服务类型和分类,同时保持 Program.cs 的简洁。这种设计符合关注点分离原则,提高了代码的可读性和可维护性。
1 | using Microsoft.Extensions.DependencyInjection; |
6、Program.cs
1 | // 导入WebApp.Config命名空间,提供依赖注入扩展方法 |





