合肥網(wǎng)站建站推廣瀏覽廣告賺錢的平臺(tái)
目錄
什么是Filter?
Exception Filter
實(shí)現(xiàn)
注意
ActionFilter
注意
案例:自動(dòng)啟用事務(wù)的篩選器
事務(wù)的使用
TransactionScopeFilter的使用
什么是Filter?
- 切面編程機(jī)制,在ASP.NET Core特定的位置執(zhí)行我們自定義的代碼。
- ASP.NET Core中的Filter的五種類型:Authorization filter、Resource filter、Action filter、Exception filter、Result filter。本書中重點(diǎn)講解Exception filter和Action filter。
- 所有篩選器一般有同步和異步兩個(gè)版本,比如IActionFilter、IAsyncActionFilter接口。
Exception Filter
當(dāng)系統(tǒng)中出現(xiàn)未經(jīng)處理的異常的時(shí)候,異常篩選器就會(huì)執(zhí)行。
實(shí)現(xiàn)
- 當(dāng)系統(tǒng)中出現(xiàn)未處理異常的時(shí)候,我們需要統(tǒng)一給客戶端返回如下格式的響應(yīng)報(bào)文:{“code”:”500”,”message”:”異常信息”}。
對(duì)于開發(fā)環(huán)境中message是異常堆棧,對(duì)于其他環(huán)境message用一個(gè)general的報(bào)錯(cuò)信息。 - 實(shí)現(xiàn)IAsyncExceptionFilter接口。注入IHostEnvironment得知運(yùn)行環(huán)境。
public class LogExceptionFilter : IAsyncExceptionFilter
{public Task OnExceptionAsync(ExceptionContext context){return File.AppendAllTextAsync("F:/error.log", context.Exception.ToString());}
}public class MyExceptionFilter : IAsyncExceptionFilter
{private readonly IWebHostEnvironment hostEnv;public MyExceptionFilter(IWebHostEnvironment hostEnv){this.hostEnv = hostEnv;}public Task OnExceptionAsync(ExceptionContext context){//context.Exception代表異常信息對(duì)象//context.ExceptionHandled為true時(shí),表示異常已經(jīng)被處理,其他ExceptionFilter將不會(huì)再處理//context.Result的值會(huì)返回給客戶端string msg;if (hostEnv.IsDevelopment()){msg = context.Exception.Message;}else{msg = "服務(wù)器發(fā)生了未處理異常";}ObjectResult objresult = new ObjectResult(new { code = 500, message = msg });context.Result = objresult;context.ExceptionHandled = true;return Task.CompletedTask;}
}
注意
ExceptionFilter執(zhí)行順序與注冊(cè)順序有關(guān),后注冊(cè)的先執(zhí)行
builder.Services.Configure<MvcOptions>(opt =>
{opt.Filters.Add<MyExceptionFilter>();opt.Filters.Add<LogExceptionFilter>();
});
ActionFilter
IAsyncActionFilter接口
多個(gè)Action Filter的鏈?zhǔn)綀?zhí)行。
注意
ActionFilter執(zhí)行順序與注冊(cè)順序有關(guān),先注冊(cè)的先執(zhí)行
public class MyActionFilter1 : IAsyncActionFilter
{public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){Console.WriteLine("MyActionFilter1前代碼");ActionExecutedContext result = await next();if (result.Exception != null){Console.WriteLine("MyActionFilter1捕獲到異常");}else{Console.WriteLine("MyActionFilter1后代碼");}}
}
builder.Services.Configure<MvcOptions>(opt =>
{opt.Filters.Add<MyActionFilter1>();opt.Filters.Add<MyActionFilter2>();
});
案例:自動(dòng)啟用事務(wù)的篩選器
- 數(shù)據(jù)庫事務(wù):要么全部成功、要么全部失敗。
- 自動(dòng)化:啟動(dòng)、提交以及回滾事務(wù)。
- 當(dāng)一段使用EF Core進(jìn)行數(shù)據(jù)庫操作的代碼放到TransactionScope聲明的范圍中的時(shí)候,這段代碼就會(huì)自動(dòng)被標(biāo)記為“支持事務(wù)”。
- TransactionScope實(shí)現(xiàn)了IDisposable接口,如果一個(gè)TransactionScope的對(duì)象沒有調(diào)用Complete()就執(zhí)行了Dispose()方法,則事務(wù)會(huì)被回滾,否則事務(wù)就會(huì)被提交。
- TransactionScope還支持嵌套式事務(wù)。
- .NET Core中的TransactionScope不像.NET FX一樣有MSDTC分布式事務(wù)提升的問題。請(qǐng)使用最終一致性事務(wù)。
事務(wù)的使用
[HttpPost]
public async Task<string> Test2()
{using (TransactionScope tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)){dbctx.Books.Add(new Book { Title = "計(jì)算機(jī)網(wǎng)絡(luò)", Price = 12.6 });await dbctx.SaveChangesAsync();dbctx.People.Add(new Person { Name = "張三", Age = 20 });await dbctx.SaveChangesAsync();tx.Complete();return "OK";}
}[HttpPost]
public string Test1()
{using (TransactionScope tx = new TransactionScope()){dbctx.Books.Add(new Book { Title = "計(jì)算機(jī)網(wǎng)絡(luò)", Price = 12.6 });dbctx.SaveChangesAsync();dbctx.People.Add(new Person { Name = "張三", Age = 20 });dbctx.SaveChangesAsync();tx.Complete();return "OK";}
}
TransactionScopeFilter的使用
對(duì)于強(qiáng)制不進(jìn)行事務(wù)控制的Action方法,請(qǐng)標(biāo)注NotTransactionalAttribute。
開發(fā)篩選器TransactionScopeFilter;把TransactionScopeFilter注冊(cè)到Program.cs中。
NotTransationAttribute.cs:
[AttributeUsage(AttributeTargets.Method)]
public class NotTransationAttribute:Attribute
{
}TransactionScopeFilter.cs:
public class TransactionScopeFilter : IAsyncActionFilter
{public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){//context.ActionDescriptor中是當(dāng)前被執(zhí)行的Action的描述信息//context.ActionArguments中是當(dāng)前被執(zhí)行的Action的參數(shù)信息ControllerActionDescriptor controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;//if (controllerActionDescriptor == null)//不是一個(gè)MVC的Actionbool isTX = false;//是否進(jìn)行事務(wù)控制if (controllerActionDescriptor != null){//獲取當(dāng)前Action是否有NotTransationAttribute特性bool hasNotTransationAttribute = controllerActionDescriptor.MethodInfo.GetCustomAttributes(typeof(NotTransationAttribute), false).Any();//如果沒有NotTransationAttribute特性,則進(jìn)行事務(wù)控制isTX = !hasNotTransationAttribute;}if (isTX){//創(chuàng)建一個(gè)異步的事務(wù)范圍using (TransactionScope tx=new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)){var r= await next();if (r.Exception==null){tx.Complete();}}}else{await next();}}
}Program.cs:
builder.Services.Configure<MvcOptions>(opt =>
{opt.Filters.Add<TransactionScopeFilter>();
});
?案例:開發(fā)請(qǐng)求限流器
需求
- Action Filter可以在滿足條件的時(shí)候終止操作方法的執(zhí)行。
- 在Action Filter中,如果我們不調(diào)用await next(),就可以終止Action方法的執(zhí)行了。
- 為了避免惡意客戶端頻繁發(fā)送大量請(qǐng)求消耗服務(wù)器資源,我們要實(shí)現(xiàn)“一秒鐘內(nèi)只允許最多有一個(gè)來自同一個(gè)IP地址的請(qǐng)求”。
public class ratelimitActionFilter : IAsyncActionFilter
{private readonly IMemoryCache memcache;public ratelimitActionFilter(IMemoryCache memcache){this.memcache = memcache;}public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){//從HTTP請(qǐng)求的上下文中獲取客戶端的遠(yuǎn)程IP地址string removeIP = context.HttpContext.Connection.RemoteIpAddress.ToString();//context.ActionDescriptor中是當(dāng)前被執(zhí)行的Action的描述信息,轉(zhuǎn)換為ControllerActionDescriptor類型ControllerActionDescriptor controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;//構(gòu)建緩存的Keystring cacheKey = $"LastVisitTick_{controllerActionDescriptor.ControllerName}_{removeIP}";//從緩存中獲取上次訪問的時(shí)間戳long? lastTick = memcache.Get<long?>(cacheKey);//如果上次訪問的時(shí)間戳不存在或者距離當(dāng)前時(shí)間已經(jīng)超過1秒if (lastTick == null || Environment.TickCount64 - lastTick > 1000){//設(shè)置當(dāng)前的時(shí)間戳到緩存中memcache.Set(cacheKey, Environment.TickCount64, TimeSpan.FromSeconds(10));//避免長(zhǎng)期不訪問的用戶一直占用緩存return next();}else{context.Result = new ContentResult{StatusCode = 429,Content = "訪問太頻繁,請(qǐng)稍后再試!"};return Task.CompletedTask;}}
}