## 1.Asp Net Core Filter(過濾器)
1.1 Filter簡介
(1)面向切面編程
(2)ASP.NET Core中的Filter的五種類型:Authorization filter、Resource filter、Action filter、Exception filter、Result filter。所有篩選器一般有同步和異步兩個版本,比如IActionFilter、IAsyncActionFilter接口。
1.2 Exception Filter
當系統中出現未經處理的異常的時候,異常篩選器就會執行
(1)繼承IAsyncExceptionFilter
public class MyExceptionFilter : IAsyncExceptionFilter
{
private readonly ILogger<MyExceptionFilter> logger;
private readonly IHostEnvironment env;
public MyExceptionFilter(ILogger<MyExceptionFilter> logger, IHostEnvironment env)
{
this.logger = logger;
this.env = env;
}
public Task OnExceptionAsync(ExceptionContext context)
{
Exception exception = context.Exception;
logger.LogError(exception, "UnhandledException occured");
string message;
if (env.IsDevelopment())
{
message = exception.ToString();
}
else
{
message = "程序中出現未處理異常";
}
ObjectResult result = new ObjectResult(new { code = 500, message = message });
result.StatusCode = 500;
context.Result = result;
//設置剩下的IAsyncExceptionFilter不執行
context.ExceptionHandled = true;
return Task.CompletedTask;
}
}
(2)IServiceCollection注入
builder.Services.Configure<MvcOptions>(options => {
options.Filters.Add<MyExceptionFilter>();
});
1.3 ActionFilter
多個Action Filter的鏈式執行
(1)繼承IAsyncActionFilter
ActionFilter1
using Microsoft.AspNetCore.Mvc.Filters;
public class ActionFilter1 : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
Console.WriteLine("ActionFilter1:開始執行");
ActionExecutedContext r = await next();
if (r.Exception != null)
{
Console.WriteLine("MyActionFilter 1:執行失敗");
}
else
{
Console.WriteLine("MyActionFilter 1:執行成功");
}
}
}
ActionFilter2
using Microsoft.AspNetCore.Mvc.Filters;
public class ActionFilter2 : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
Console.WriteLine("ActionFilter2:開始執行");
ActionExecutedContext r = await next();
if (r.Exception != null)
{
Console.WriteLine("MyActionFilter 2:執行失敗");
}
else
{
Console.WriteLine("MyActionFilter 2:執行成功");
}
}
}
(2)IServiceCollection服務注入
builder.Services.Configure<MvcOptions>(opt => {
opt.Filters.Add<ActionFilter1>();
opt.Filters.Add<ActionFilter2>();
});
(3)訪問接口,查看ActionFilter執行順序
1.4 自動啟用事務的ActionFilter
(1)TransactionScope
- TransactionScope實現了IDisposable接口,如果一個TransactionScope的對象沒有調用Complete()就執行了Dispose()方法,則事務會被回滾,否則事務就會被提交
- TransactionScope還支持嵌套式事務
- .NET Core中的TransactionScope不像.NET FX一樣有MSDTC分布式事務提升的問題。請使用最終一致性事務
(2)繼承IAsyncActionFilter
這個特性標注在不需要執行事務的方法上
/// <summary>
/// 只允許寫在方法上
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class NotTransactionalAttribute : Attribute
{
}
TransactionScopeFilter
public class TransactionScopeFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
bool hasNotTransactionalAttribute = false;
if (context.ActionDescriptor is ControllerActionDescriptor)
{
var actionDesc = (ControllerActionDescriptor)context.ActionDescriptor;
hasNotTransactionalAttribute = actionDesc.MethodInfo
.IsDefined(typeof(NotTransactionalAttribute));
}
if (hasNotTransactionalAttribute)
{
await next();
return;
}
using var txScope =
new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
var result = await next();
if (result.Exception == null)
{
txScope.Complete();
}
}
}
(3)IServiceCollection服務注入
builder.Services.AddDbContext<MyDbContext>(opt => {
string connStr = builder.Configuration.GetConnectionString("Default");
opt.UseSqlServer(connStr);
});
builder.Services.Configure<MvcOptions>(options =>
{
options.Filters.Add<TransactionScopeFilter>();
});
1.5請求限流的ActionFilter
實現“一秒鍾內只允許最多有一個來自同一個IP地址的請求”
(1)繼承IAsyncActionFilter
public class RateLimiterFilter : IAsyncActionFilter
{
private readonly IMemoryCache memCache;
public RateLimiterFilter(IMemoryCache memCache)
{
this.memCache = memCache;
}
public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
string removeIP = context.HttpContext.Connection.RemoteIpAddress.ToString();
string cacheKey = $"LastVisitTick_{removeIP}";
long? lastTick = memCache.Get<long?>(cacheKey);
//如果上次訪問時間不存在,或者訪問間隔一秒內
if (lastTick == null || Environment.TickCount64 - lastTick > 1000)
{
//設置過期時間10s
memCache.Set(cacheKey, Environment.TickCount64, TimeSpan.FromSeconds(10));
return next();
}
else
{
context.Result = new ContentResult { StatusCode = 429, Content = "訪問頻繁" };
return Task.CompletedTask;
}
}
}
(2)IServiceCollection服務注入
builder.Services.AddMemoryCache();
builder.Services.Configure<MvcOptions>(options =>
{
options.Filters.Add<RateLimiterFilter>();
});