切面编程: 在项目中的某一处,执行我们自定义的代码!
- 比如说
自定义异常
,先简单写一个接口,触发系统自带异常
using Microsoft.AspNetCore.Mvc;namespace WebApplicationAboutCustomCode.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class Test1Controller : ControllerBase{[HttpGet]public string Test1(){// 访问一个并不存在的文件string s = System.IO.File.ReadAllText("d:/123.txt");return s;}}
}- 结果,触发系统自带异常: System.IO.FileNotFoundException:“Could not find file 'd:\123.txt'.”
- 现在,自定义异常,比如统一返回
{"code":500,"message":"xxx"}
这种格式
// MyExceptionFilter.csusing Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;namespace WebApplicationAboutCustomCode
{public class MyExceptionFilter:IAsyncExceptionFilter{private readonly IWebHostEnvironment hostEnv;public MyExceptionFilter(IWebHostEnvironment hostEnv){this.hostEnv = hostEnv;}public Task OnExceptionAsync(ExceptionContext context){string msg;if (hostEnv.IsDevelopment()){msg = context.Exception.ToString();Console.WriteLine("触发开发环境异常");}else{msg = "服务端发生未知处理异常";}ObjectResult objResult = new ObjectResult(new { code = 500, message = msg });context.Result = objResult;context.ExceptionHandled = true;return Task.CompletedTask;}}
}// LogExceptionFilter.csusing Microsoft.AspNetCore.Mvc.Filters;namespace WebApplicationAboutCustomCode
{public class LogExceptionFilter: IAsyncExceptionFilter{public Task OnExceptionAsync(ExceptionContext context){return File.AppendAllTextAsync("d:/error.log", context.Exception.ToString());}}
}// Program.cs
......
builder.Services.AddSwaggerGen();builder.Services.Configure<MvcOptions>(opt =>
{ // 这里注意顺序opt.Filters.Add<MyExceptionFilter>();opt.Filters.Add<LogExceptionFilter>();});// 接口程序using Microsoft.AspNetCore.Mvc;namespace WebApplicationAboutCustomCode.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class Test1Controller : ControllerBase{[HttpGet]public string Test1(){// 主动触发异常string s = System.IO.File.ReadAllText("d:/123.txt");return s;}}
}
切面编程(AOP)在 ASP.NET Core 中的实现
这段代码演示了如何使用过滤器(Filters) 在 ASP.NET Core 中实现切面编程(AOP)的概念。
核心概念解释
什么是切面编程(AOP)?
切面编程是一种编程范式,允许开发者在不修改原有业务代码的情况下,在程序的特定执行点插入自定义逻辑。就像"切一刀"进去执行额外代码。
代码详细解析
1. 原始问题代码
[HttpGet]
public string Test1()
{// 访问一个并不存在的文件,会抛出异常string s = System.IO.File.ReadAllText("d:/123.txt");return s;
}
问题:直接抛出系统异常,用户体验差,没有统一的错误处理。
2. 自定义异常过滤器
MyExceptionFilter - 主要异常处理器
public class MyExceptionFilter : IAsyncExceptionFilter
{private readonly IWebHostEnvironment hostEnv;public MyExceptionFilter(IWebHostEnvironment hostEnv){this.hostEnv = hostEnv;}public Task OnExceptionAsync(ExceptionContext context){string msg;// 根据环境决定错误信息详细程度if (hostEnv.IsDevelopment()){msg = context.Exception.ToString(); // 开发环境:详细错误Console.WriteLine("触发开发环境异常");}else{msg = "服务端发生未知处理异常"; // 生产环境:友好提示}// 创建统一格式的响应ObjectResult objResult = new ObjectResult(new { code = 500, message = msg });context.Result = objResult; // 设置返回结果context.ExceptionHandled = true; // 标记异常已处理,阻止继续传播return Task.CompletedTask;}
}
作用:
- 捕获控制器中未处理的异常
- 根据运行环境返回不同的错误信息
- 统一响应格式:
{ code: 500, message: "..." }
LogExceptionFilter - 日志记录器
public class LogExceptionFilter : IAsyncExceptionFilter
{public Task OnExceptionAsync(ExceptionContext context){// 将异常信息写入日志文件return File.AppendAllTextAsync("d:/error.log", context.Exception.ToString());}
}
作用:
- 专门负责记录异常日志
- 将异常详细信息写入文件
3. 注册过滤器
builder.Services.Configure<MvcOptions>(opt =>
{opt.Filters.Add<MyExceptionFilter>(); // 第一个执行opt.Filters.Add<LogExceptionFilter>(); // 第二个执行
});
执行顺序很重要:
MyExceptionFilter
先执行,处理异常并设置响应LogExceptionFilter
后执行,记录日志
执行流程
sequenceDiagramparticipant Client as 客户端participant Controller as Test1Controllerparticipant Filter1 as MyExceptionFilterparticipant Filter2 as LogExceptionFilterparticipant Response as 响应Client->>Controller: GET /api/Test1/Test1Controller->>Controller: 执行Test1()方法Controller->>Controller: 抛出FileNotFoundExceptionNote over Controller: 异常发生,开始过滤器链Controller->>Filter1: 触发OnExceptionAsyncNote over Filter1: 处理异常<br/>设置统一响应格式<br/>标记异常已处理Filter1->>Filter2: 继续执行下一个过滤器Note over Filter2: 记录异常到日志文件Filter2->>Response: 返回处理后的响应Response->>Client: { "code": 500, "message": "..." }
切面编程的优势
1. 关注点分离
- 业务代码:只关注核心逻辑(读取文件)
- 横切关注点:异常处理、日志记录单独处理
2. 代码复用
// 这个过滤器会自动应用到所有Controller的所有Action
// 不需要在每个方法中写try-catch
3. 维护性
修改异常处理逻辑只需修改过滤器,不影响业务代码。
4. 一致性
所有异常都按照统一格式返回,前端处理更简单。
实际效果对比
修改前:
HTTP/1.1 500 Internal Server Error
System.IO.FileNotFoundException: Could not find file 'd:\123.txt'.
修改后:
{"code": 500,"message": "System.IO.FileNotFoundException: Could not find file 'd:\\123.txt'."
}
扩展应用场景
这种切面编程思想还可以用于:
- 身份认证:在进入业务方法前验证用户权限
- 性能监控:记录每个方法的执行时间
- 缓存:在方法执行前检查缓存
- 数据验证:统一验证请求参数
- 事务管理:自动管理数据库事务
总结
这个例子完美展示了切面编程的核心价值:
- ✅ 非侵入式:不改动原有业务代码
- ✅ 集中管理:异常处理逻辑集中在一处
- ✅ 灵活配置:根据不同环境返回不同信息
- ✅ 职责分离:业务逻辑与横切关注点分离
通过过滤器机制,ASP.NET Core 提供了强大的 AOP 支持,让开发者能够优雅地处理这类"横切关注点"问题。