C#自定義HttpFilter模塊完善實(shí)例
本文實(shí)例講述了C#自定義HttpFilter模塊完善的方法,分享給大家供大家參考。具體實(shí)現(xiàn)方法如下:
一、背景
近期由于要針對(duì)項(xiàng)目做用戶操作日志,但不想在每個(gè)方法里去增加代碼,寫入用戶日志。因?yàn)檫@樣具體的方法違背職責(zé)單一的原則,若后期日志內(nèi)容格式發(fā)生變更,或其他什么需求,該方法代碼主要一變?cè)谧?,故使用HttpModule模塊來(lái)完成此功能,感興趣的朋友可以參考:關(guān)于HttpHandler與HttpModule的理解和應(yīng)用方法
經(jīng)過(guò)實(shí)際運(yùn)用與完善,現(xiàn)在可以再次總結(jié)下。
二、攔截時(shí)機(jī)
現(xiàn)在的版本中,攔截的依據(jù)是,在每次請(qǐng)求發(fā)生的過(guò)程中,攔截控制器類請(qǐng)求,重定向http輸出流,并分析出Controller與Action,接下來(lái)查找是否有方法監(jiān)控了此控制器,若有,則分析出請(qǐng)求輸入?yún)?shù),與此次請(qǐng)求輸出內(nèi)容,存儲(chǔ)在FilterContext中,交給該方法,完成相應(yīng)邏輯。
由于在最初的寫法中,是針對(duì)所有的請(qǐng)求進(jìn)行流的重定向,在asmx下,會(huì)遇到問(wèn)題,只要重定向了,調(diào)用服務(wù)的客戶端會(huì)提示400 Http Bad Request 。這個(gè)具體的錯(cuò)誤原因,還不清楚,但正是由于該錯(cuò)誤,讓我發(fā)現(xiàn),我之前攔截的時(shí)機(jī)是錯(cuò)誤的,理應(yīng)放在請(qǐng)求之前,判斷是否滿足攔截的規(guī)則,若滿足,則重定向輸出流。
三、讀取用戶名
在Module模塊中總會(huì)出點(diǎn)問(wèn)題,最后使用了Cookie記住用戶名,并直接定義為FilterContext一個(gè)屬性。解釋下這樣做的原因:由于記住用戶名的方式有很多,如Session、Cookie,即讀取用戶名的方式是可變的,所以盡可能將變化的內(nèi)容在前面解決,這樣監(jiān)聽控制器的方法,直接根據(jù)該屬性獲取用戶名,否則用戶名的讀取時(shí)機(jī),放在每個(gè)監(jiān)聽控制器模塊之后,讀取方式一旦發(fā)生變更,所有的模塊都要改變,當(dāng)然也可以通過(guò)繼承一個(gè)base類來(lái)避免這么大的改變。
在這里我想表達(dá)的意思是:我們做類似底層庫(kù)的東西,盡可能穩(wěn)定,將變化點(diǎn)集中在庫(kù)本身,這樣依賴該庫(kù)的應(yīng)用才能穩(wěn)定。若.net版本更新過(guò)程中,API都不穩(wěn)定,想必我們也不會(huì)在去使用它。
四、應(yīng)用之寫入日志
典型例子如下:
public void Login(FilterContext context)
{
//解析輸出內(nèi)容,這里針對(duì)要監(jiān)聽的控制器和方法來(lái)寫的
var arr = context.OutputBody.Split('|');
var log = string.Format("userName:{0} password:{1}",arr);
FilterLog.Log.Info(log);
}
該方法表達(dá)的意思是,監(jiān)控LoginController的Login方法。由于我們需要分析請(qǐng)求輸出結(jié)果,所以分析的規(guī)則,與控制器是強(qiáng)依賴的,控制器的方法是怎么返回?cái)?shù)據(jù)的,我們此處就要根據(jù)規(guī)則解析。我在項(xiàng)目中使用的是Json,所以監(jiān)控的地方都需要Json的反序列化,這里僅僅是一個(gè)Demo。
另外一個(gè)方法可以監(jiān)聽一個(gè)控制器下的多個(gè)方法,或者多個(gè)控制器。這樣是旨在解決有很多Action,輸入?yún)?shù)和輸出參數(shù)都是相同的,可能由于業(yè)務(wù)不同,僅僅在方法名和內(nèi)部實(shí)現(xiàn)中有不同。
五、應(yīng)用之更新緩存
首先關(guān)于Cache的應(yīng)用,可以讀下此文章,Asp.Net Cache高級(jí)用法。
由于此處我沒(méi)有寫例子,先描述我在項(xiàng)目中運(yùn)用的情況。系統(tǒng)有很多數(shù)據(jù)字典,在請(qǐng)求該數(shù)據(jù)字典時(shí),程序首先從數(shù)據(jù)庫(kù)加載字典數(shù)據(jù),并放入緩存,此時(shí)放入緩存有個(gè)技巧,設(shè)置過(guò)期時(shí)間,并設(shè)置移除緩存前的回調(diào),我們來(lái)看看具體的方法定義:
// 摘要:
// 將對(duì)象與依賴項(xiàng)、到期策略以及可用于在從緩存中移除項(xiàng)之前通知應(yīng)用程序的委托一起插入到 System.Web.Caching.Cache 對(duì)象中。
//
// 參數(shù):
// key:
// 用于引用對(duì)象的緩存鍵。
//
// value:
// 要插入到緩存中的對(duì)象。
//
// dependencies:
// 該項(xiàng)的文件依賴項(xiàng)或緩存鍵依賴項(xiàng)。當(dāng)任何依賴項(xiàng)更改時(shí),該對(duì)象即無(wú)效,并從緩存中移除。如果沒(méi)有依賴項(xiàng),則此參數(shù)包含 null。
//
// absoluteExpiration:
// 所插入對(duì)象將到期并被從緩存中移除的時(shí)間。要避免可能的本地時(shí)間問(wèn)題(例如從標(biāo)準(zhǔn)時(shí)間改為夏時(shí)制),請(qǐng)使用 System.DateTime.UtcNow
// 而不是 System.DateTime.Now 作為此參數(shù)值。如果使用絕對(duì)到期,則 slidingExpiration 參數(shù)必須設(shè)置為 System.Web.Caching.Cache.NoSlidingExpiration。
//
// slidingExpiration:
// 緩存對(duì)象的上次訪問(wèn)時(shí)間和對(duì)象的到期時(shí)間之間的時(shí)間間隔。如果該值等效于 20 分鐘,則對(duì)象在最后一次被訪問(wèn) 20 分鐘之后將到期并被從緩存中移除。如果使用可調(diào)到期,則
// absoluteExpiration 參數(shù)必須設(shè)置為 System.Web.Caching.Cache.NoAbsoluteExpiration。
//
// onUpdateCallback:
// 從緩存中移除對(duì)象之前將調(diào)用的委托。可以使用它來(lái)更新緩存項(xiàng)并確保緩存項(xiàng)不會(huì)從緩存中移除。
//
// 異常:
// System.ArgumentNullException:
// key、value 或 onUpdateCallback 參數(shù)為 null。
//
// System.ArgumentOutOfRangeException:
// 將 slidingExpiration 參數(shù)設(shè)置為小于 TimeSpan.Zero 或大于一年的等效值。
//
// System.ArgumentException:
// 為要添加到 Cache 中的項(xiàng)設(shè)置 absoluteExpiration 和 slidingExpiration 參數(shù)。- 或 -dependencies
// 參數(shù)為 null,absoluteExpiration 參數(shù)設(shè)置為 System.Web.Caching.Cache.NoAbsoluteExpiration
// 并且 slidingExpiration 參數(shù)設(shè)置為 System.Web.Caching.Cache.NoSlidingExpiration。
public void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemUpdateCallback onUpdateCallback);
仔細(xì)看看onUpdateCallback參數(shù)的描述:從緩存中移除對(duì)象之前將調(diào)用的委托??梢允褂盟鼇?lái)更新緩存項(xiàng)并確保緩存項(xiàng)不會(huì)從緩存中移除。
我在把數(shù)據(jù)字典放入緩存的同時(shí)傳遞讀取緩存的委托,這樣在主動(dòng)移除緩存或者緩存過(guò)期時(shí)都將再次調(diào)用此委托,將數(shù)據(jù)字典再次放入緩存。所以一旦數(shù)據(jù)字典發(fā)生了變更,如增刪改,那么就主動(dòng)將字典緩存移除,它就可以自動(dòng)更新過(guò)來(lái),是不是很方便呢。
區(qū)別于寫操作日志,不過(guò)是處理邏輯發(fā)生了變化,他們都需要請(qǐng)求的輸入和輸出。
六、其他
1.由于使用HttpModule來(lái)完成此功能,如需正常運(yùn)行,需要在WebConfig中注冊(cè)該模塊。詳見Demo。
2.項(xiàng)目中使用了Log4Net記錄文本日志,并可以根據(jù)功能分類。詳見:Log4Net日志分類維護(hù)。
完整實(shí)例代碼點(diǎn)擊此處本站下載。
希望本文所述對(duì)大家的C#程序設(shè)計(jì)有所幫助。
相關(guān)文章
C#實(shí)現(xiàn)在兩個(gè)數(shù)字之間生成隨機(jī)數(shù)的方法
這篇文章主要介紹了C#實(shí)現(xiàn)在兩個(gè)數(shù)字之間生成隨機(jī)數(shù)的方法,在一些特殊場(chǎng)景會(huì)用到哦,需要的朋友可以參考下2014-08-08WPF程序?qū)⒖丶尸F(xiàn)的內(nèi)容保存成圖像
這篇文章介紹了WPF程序?qū)⒖丶尸F(xiàn)的內(nèi)容保存成圖像的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06C# Winfom 中ListBox的簡(jiǎn)單用法詳解
這篇文章主要介紹了C# Winfom 中ListBox的簡(jiǎn)單用法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12C#利用iTextSharp組件給PDF文檔添加圖片/文字水印
這篇文章主要給大家介紹了關(guān)于如何C#利用iTextSharp組件給PDF文檔添加圖片/文字水印的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10淺談Visual Studio 2019 Vue項(xiàng)目的目錄結(jié)構(gòu)
這篇文章主要介紹了Visual Studio 2019 Vue項(xiàng)目 目錄結(jié)構(gòu),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03DevExpress獲取TreeList可視區(qū)域節(jié)點(diǎn)集合的實(shí)現(xiàn)方法
這篇文章主要介紹了DevExpress獲取TreeList可視區(qū)域節(jié)點(diǎn)集合的實(shí)現(xiàn)方法,有一定實(shí)用價(jià)值,需要的朋友可以參考下2014-08-08