前些天和張隊(善友),lemon(浩洋),斌哥(項斌)等MVP大咖一塊兒吃飯,大家聊到了lemon名下的AOP這個項目,我這小白聽得一臉懵逼,後面回來做了一下功課,查了下資料,在lemon的Github上把這個項目學習了一下,收獲頗豐,讓我這個沒有接觸過AOP的Coder歎為觀止,陷入了對lemon的深深崇拜,在這裡把學習的新的體會分享給大家.
在軟體業,AOP為Aspect Oriented Programming的縮寫,意為:面向切面程式設計,通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術。
有點深奧, 舉個栗子

如果說之前做的一個系統專門給内部的服務提供接口的,因為是在内網中通路,是以就沒有加上認證服務,現在這個系統要公開出來,同樣的那套接口要給外部系統服務了,那麼此時,就要進行認證,認證通過才能擷取接口的資料.
傳統的做法是,修改每一個接口.這樣就會造成代碼改動過大,很恐怖.
這個時候AOP就可以登場了,我們可以在這一類服務的前面,加上一個一系列上一刀,在切出來的裂縫裡面插入認證方法.
然而,怎麼插入這個切面是關鍵.AOP 實作會采用一些常見方法:
使用預處理器(如 C++ 中的預處理器)添加源代碼。
使用後處理器在編譯後的二進制代碼上添加指令。
使用特殊編譯器在編譯時添加代碼。
在運作時使用代碼攔截器攔截執行并添加所需的代碼。
但是常見還是後處理和代碼攔截兩種方式
後處理,或者叫 靜态織入
指使用 AOP 架構提供的指令進行編譯,進而在編譯階段就可生成 AOP 代理類,是以也稱為編譯時增強或靜态織入。
在dotnet 中一般在編譯時通過在MSBiuld執行自定義的Build Task來攔截編譯過程,在生成的程式集裡插入自己的IL。
dotnet 架構代表: PostSharp
代碼攔截,或者叫 動态代理
在運作時在記憶體中“臨時”生成 AOP 動态代理類,是以也被稱為運作時增強或動态代理。
在dotnet 中一般在運作時通過Emit技術生成動态程式集和動态代理類型進而對目标方法進行攔截。
dotnet 架構代表: Castle DynamicProxy 和 AspectCore
引用https://github.com/dotnetcore/AspectCore-Framework/blob/master/docs/0.AOP%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D.md
我這裡直接應用AOP Demo中的一段代碼來說說這個攔截.
代碼中,其實執行到 await next(context)的時候,才會真正去調用那個被攔截的方法.
這樣,我們就可以靈活地在代碼調用前,調用後做我們想做的事情了.甚至可以把代碼包在一個try…catch...中來捕獲異常.
建立一個web應用程式後,從 Nuget 安裝 <code>AspectCore.Extensions.DependencyInjection</code> 包.
然後.我們就可以來定義我們的攔截器了,我定義了一個這樣的攔截器.
當ApiRequest為空或者Name不等于admin的時候之家傳回并記錄未授權.
否則,調用該調用的方法并記錄ApiRequest中的Message.
然後,我定義一個UserService.
在Controler中調用注入UserServce并調用該Service.
注冊IUserservice并在<code>ConfigureServices</code>中配置建立代理類型的容器:
需要注意的是紅色背景處,預設的ConfigureService傳回類型是空的,我們要修改成為傳回類型是IServiceProvider.
我們在上面的ConfigureService配置的AuthenticateInterceptor預設情況下是全局的,即這裡的IUserService它會攔截,當然如果新增了一個IRoleServce它也是會攔截的.
我把程式運作起來用PostMan通路Api進行測試.下圖是Post的資料和傳回結果.
說明接口是正常工作的,成功地把傳過去的Name原樣傳回.
那麼攔截器有沒有生效呢?我看看CMD的輸出.
如果我們修改一下Name不等于Admin,預期應該是傳回空,并且日志列印出未授權,是不是這樣呢?
完美,與預期完全相同.
可以發現,這正是我們在攔截器中所作的工作,說明攔截器對該UserService生效了.
如果我們不想對所有Servce或是Method都攔截,隻攔截指定的Servce或者Method呢?
其實,我們是可以配置全局攔截器的作用域的.
我用以上方法配置為該過濾器隻對IRole開頭的Servce有效,那麼,當我們讓問IUserServce時,該攔截器肯定是不會生效的,事實是不是這樣呢?
即使Name不是admin,結果也傳回了,說明确實是沒有生效的.
還可以用以下方法指定過濾器作用于的Method.
忽略配置有兩種方法
一種是為Service或者Method打上[NonAspect] 标簽,那個過濾器就不會對該處生效了.
此時,即使Name不等于Admin,也是有結果傳回會的.
說明此時配置器對GetUserName方法确實沒有生效.
另外一種是 全局忽略配置,亦支援通配符:
對攔截器中有get和set權限的屬性标記<code>[AspectCore.Injector.FromContainerAttribute]</code>特性,即可自動注入該屬性.
public ILogger<AuthenticateInterceptor> Logger { get; set
也可以攔截器上下文<code>AspectContext</code>可以擷取目前Scoped的<code>ServiceProvider</code>
利用該ServiceProvider來對依賴項指派.
var logger = context.ServiceProvider.GetService<ILogger<AuthenticateInterceptor>>
本文隻是對AsceptCore最簡單的一套流程end to end 地進行了叙述,這還隻是AsceptCore的冰山一角.在此向開發處如此牛逼AOP架構的小夥緻敬!!
歡迎通路
https://github.com/dotnetcore/AspectCore-Framework/blob/master/docs/1.%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97.md
解鎖更多新姿勢!!!
本部落格Demo位址
https://github.com/liuzhenyulive/AspceptCoreDemo
AsceptCore位址
https://github.com/dotnetcore/AspectCore-Framework