我们在开发企业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中去写了,维护起来也方便多了! Yeah~
为方便我把源代码贴出来吧:其中保护我上一片blog《自己编写BuildProvider来实现ORM以及BuildProvider的调试》的代码:
CustomBuilderProvider.rar 299KB 2008/3/7 22:07:36
Download