我們在開發企業Web應用程序時經常需要對用戶的操作記錄日誌,以便在發生突發事件後有據可查,比如要對用戶訪問的每一個頁面都做日誌記錄。通常的做法可能是編寫一個記錄日誌的方法(如:AddAccessLog),在每一個頁面的Page_Load事件中調用這個AddAccessLog方法,從而達到記錄頁面訪問日誌的目的。這樣的方法在頁面較少的時候可行,但是當項目變得越來越大,需要記錄日誌的頁面越來越多的時候,我們要在每個頁面中都調用這樣的方法,從而使得系統很難維護。有沒有簡單一點的辦法呢,何不用IHttpModule接口實現一個自定義的LogHttpModule來試試呢?
IHttpModule接口中定義了兩個方法:Init和Dispose。Init方法初始化一個模塊,併爲它做好處理請求的準備。這時,我們同意接受感興趣的事件通知。Dispose方法處置該模塊使用資源。Init方法接受一個服務該請求的HttpApplication對象的引用。使用該引用可以連接到系統事件。
class PageLoggerModule : IHttpModule //實現IHttpModule接口
{
public void Dispose()
{
}public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}void context_BeginRequest(object sender, EventArgs e)
{//在這裏實現具體代碼
HttpApplication app = (HttpApplication)sender;
HttpContext ctx = app.Context;//acquire session state
string userId = "Not Registered";
if (ctx.Session != null && ctx.Session["userId"] != null)
{
userId = ctx.Session["userId"].ToString();
}//acquire request string
string funcId = "Init funcId";
if (ctx.Request.QueryString["funcId"] != null)
{
funcId = ctx.Request.QueryString["funcId"].ToString();
}LogEntry log = new LogEntry(); // 這裏是自定義的一個類,屬性列表和數據庫中的字段一致
log.DateTime = System.DateTime.Now;
log.IpAddress = ctx.Request.UserHostAddress;
log.MachineName = ctx.Request.UserHostName;
log.UserId = userId;
log.FunctionId = funcId;string logString = log.DateTime.ToString() + "<br/>"
+ log.UserId + "<br/>"
+ log.IpAddress + "<br/>"
+ log.MachineName + "<br/>"
+ log.FunctionId + "<br/>";//you can add other codes here
ctx.Response.AppendHeader("Author", "Changyu Du");ctx.Response.Write(logString);
}
}
在Web.config中,System.Web節中增加一個HttpModule:
<httpModules>
<add name="PageLoggerHttpModule" type="PageLoggerHttpModule.PageLoggerModule,PageLoggerHttpModule"/>
</httpModules>
新建一個普通的aspx頁面,在頁面加載時把用戶信息保存到Session中,模擬一下項目應用中的情形:
protected void Page_Load(object sender, EventArgs e)
{
//Add userName into SESSION
if (Session["userId"] == null)
{
Session["userId"] = "1101";
}
else
{
Response.Write("SESSION :userId = "+Session["userId"].ToString());
}
}
爲了確保把用戶Id信息加入到session中,測試頁面上還隨便加了個button,button_click什麼都不幹,就是爲了讓頁面回發一下確保把userId存入到Session中去。F5運行一下,嗯,不錯,LogEntry的信息都輸出出來了,已經取得了訪問時間,IP地址之類的信息,基本成功!下來直接把LogEntry的信息保存到數據庫就OK了。
可是,別忙,怎麼總取不到Session中的用戶信息呢?記錄訪問日誌用戶Id這樣的信息肯定是需要記錄的啊!
後來放狗一搜才發現,還是對aspnet的事件處理流程不理解,Begin_Request時還沒有加載Session狀態呢,自然就取不到了。
下面是MSDN上提供的事件觸發順序:
在處理該請求時將由 HttpApplication 類執行以下事件。希望擴展 HttpApplication 類的開發人員尤其需要注意這些事件。
|
AcquireRequestState事件,當實際服務請求的處理程序獲得與該請求關聯的狀態信息時發生。在這個事件發生時才能取到Session中是userId信息。BeginRequest事件在AcquireRequestState之前發生,我們把取Session狀態的代碼放在BeginRequest中肯定是取不到的。
問題找到了,把日誌記錄代碼放在AcquireRequestState中就可以了,於是改成下面的樣子:
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(context_AcquireRequestState);
}
void context_AcquireRequestState(object sender, EventArgs e)
{
//原先context_BeginRequest中的代碼,不重複貼佔地方了 :)
//把LogEntry中的信息保存到數據庫
}
好了,這樣我們繼承了IHttpModule接口,實現了一個自定義的LogMudule,這樣在用戶方面每個頁面時,都會自動記錄用戶的信息記如訪問日誌數據庫中,再也不用到每個頁面的Page_Load中去寫了,維護起來也方便多了