前序
需要实现一个控制器操作日志的功能并且可以局部使用,参考java拦截器spring aop注解;原先考虑使用操作过滤器、但是操作过滤器包裹范围没有异常过滤器的范围大,抛出异常后就不执行了。然后考虑使用中间件,使用中间件存在一个问题;中间件没有局部使用的功能。采用特性去标记控制器,在中间件中获取到控制器上标记的特性存在标记就执行,没有标记跳过日志记录逻辑。
实现思路:
- 自定义特性;
- 在每个控制器方法上加上自定义特性;
- 通过中间件,在每个控制器方法执行完后,获取该特性信息,写入数据库;
注意 : 有坑的地方,在中间件获取控制器的特性,获取控制器(端点)一直为 null 这是根据程序配置入口使用顺序相关。
问题
program.cs
这是官方文档的说明
官方文档地址
代码
特性 LoggingAttribute
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public class LoggingAttribute : Attribute
{
public string name;
public LoggingAttribute(string s)
{
this.name = s;
}
}
全局异常拦截 GlobalExceptionFilter
public class GlobalExceptionFilter : IAsyncExceptionFilter
{
private readonly ILogger<GlobalExceptionFilter> _logger;
public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
{
_logger = logger;
}
public Task OnExceptionAsync(ExceptionContext context)
{
switch (context.Exception)
{
default:
context.Result = new ObjectResult("500");
_logger.LogError(context.Exception.Message);
break;
}
context.ExceptionHandled = true;
return Task.CompletedTask;
}
}
日志中间 LogMiddleware
public class LogMiddleware
{
private readonly RequestDelegate _next;
public LogMiddleware(RequestDelegate next)
{
this._next = next;
}
public async Task InvokeAsync(HttpContext context)
{
Debug.WriteLine("----------入口----");
// todo 这里获取端点根据程序启动入口配置顺序有关,未配置和否则端点一直为null,踩了一天的坑官网有解释
var log = context.GetEndpoint()?.Metadata.GetMetadata<LoggingAttribute>();
if (log == null)
{
await _next(context);
return;
}
// todo 保存起始日志
await _next(context);
// todo 执行完成日志
Debug.WriteLine("----------出口----");
}
}
测试控制器 TestController
[ApiController]
[Route("[controller]/[action]")]
public class TestController : ControllerBase
{
[HttpGet]
public string get()
{
return "100";
}
[Logging("测试接口")]
[HttpGet]
public string get1()
{
throw new Exception("200");
return "100";
}
}
程序入口 Program
var builder = WebApplication.CreateBuilder(args);
/*builder.Services.Configure<MongodbSettings>(
builder.Configuration.GetSection("BookStoreDatabase"));*/
// 注册筛选器
builder.Services.Configure<MvcOptions>(opt =>
{
opt.Filters.Add<GlobalExceptionFilter>();
});
builder.Services.AddControllers();
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseRouting();
app.MapControllers();
// 注意:使用顺序
app.UseMiddleware<LogMiddleware>();
app.UseEndpoints(e => e.MapControllers());
app.Run();