实现IHttpModule接口获取Session来实现页面访问日志功能。

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:


我们在开发企业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 类的开发人员尤其需要注意这些事件。

  1. 对请求进行验证,将检查浏览器发送的信息,并确定其是否包含潜在恶意标记。有关更多信息,请参见 ValidateRequest 和脚本侵入概述

  2. 如果已在 Web.config 文件的 UrlMappingsSection 节中配置了任何 URL,则执行 URL 映射。

  3. 引发 BeginRequest 事件。

  4. 引发 AuthenticateRequest 事件。

  5. 引发 PostAuthenticateRequest 事件。

  6. 引发 AuthorizeRequest 事件。

  7. 引发 PostAuthorizeRequest 事件。

  8. 引发 ResolveRequestCache 事件。

  9. 引发 PostResolveRequestCache 事件。

  10. 根据所请求资源的文件扩展名(在应用程序的配置文件中映射),选择实现 IHttpHandler 的类,对请求进行处理。如果该请求针对从 Page 类派生的对象(页),并且需要对该页进行编译,则 ASP.NET 会在创建该页的实例之前对其进行编译。

  11. 引发 PostMapRequestHandler 事件。

  12. 引发 AcquireRequestState 事件。

  13. 引发 PostAcquireRequestState 事件。

  14. 引发 PreRequestHandlerExecute 事件。

  15. 为该请求调用合适的 IHttpHandler 类的 ProcessRequest 方法(或异步版 BeginProcessRequest)。例如,如果该请求针对某页,则当前的页实例将处理该请求。

  16. 引发 PostRequestHandlerExecute 事件。

  17. 引发 ReleaseRequestState 事件。

  18. 引发 PostReleaseRequestState 事件。

  19. 如果定义了 Filter 属性,则执行响应筛选。

  20. 引发 UpdateRequestCache 事件。

  21. 引发 PostUpdateRequestCache 事件。

  22. 引发 EndRequest 事件。

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



本文转自峻祁连. Moving to Cloud/Mobile博客园博客,原文链接:http://www.cnblogs.com/junqilian/archive/2008/03/07/1095454.html ,如需转载请自行联系原作者
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
打赏
0
0
0
0
23
分享
相关文章
Tauri 开发实践 — Tauri 日志记录功能开发
本文介绍了如何为 Tauri 应用配置日志记录。Tauri 是一个利用 Web 技术构建桌面应用的框架。文章详细说明了如何在 Rust 和 JavaScript 代码中设置和集成日志记录,并控制日志输出。通过添加 `log` crate 和 Tauri 日志插件,可以轻松实现多平台日志记录,包括控制台输出、Webview 控制台和日志文件。文章还展示了如何调整日志级别以优化输出内容。配置完成后,日志记录功能将显著提升开发体验和程序稳定性。
221 1
Tauri 开发实践 — Tauri 日志记录功能开发
除了实时性能监控,Hyper-V还支持日志记录和警报功能你知道吗?
Hyper-V不仅支持实时性能监控,还具备强大的日志记录和警报功能。通过事件查看器可访问详细的日志文件,涵盖虚拟机管理、配置及Hypervisor事件,帮助故障排查和性能分析。警报功能支持预定义和自定义规则,可通过多种方式通知管理员,确保及时响应问题,保障虚拟化环境的稳定运行。
启用Linux防火墙日志记录和分析功能
为iptables启用日志记录对于监控进出流量至关重要
要统计Nginx的客户端IP,可以通过分析Nginx的访问日志文件来实现
要统计Nginx的客户端IP,可以通过分析Nginx的访问日志文件来实现
262 3
PHP中的设计模式:单例模式的深入探索与实践在PHP的编程实践中,设计模式是解决常见软件设计问题的最佳实践。单例模式作为设计模式中的一种,确保一个类只有一个实例,并提供全局访问点,广泛应用于配置管理、日志记录和测试框架等场景。本文将深入探讨单例模式的原理、实现方式及其在PHP中的应用,帮助开发者更好地理解和运用这一设计模式。
在PHP开发中,单例模式通过确保类仅有一个实例并提供一个全局访问点,有效管理和访问共享资源。本文详细介绍了单例模式的概念、PHP实现方式及应用场景,并通过具体代码示例展示如何在PHP中实现单例模式以及如何在实际项目中正确使用它来优化代码结构和性能。
79 2
日志框架log4j打印异常堆栈信息携带traceId,方便接口异常排查
日常项目运行日志,异常栈打印是不带traceId,导致排查问题查找异常栈很麻烦。
SLS 虽然不是直接使用 OSS 作为底层存储,但它凭借自身独特的存储架构和功能,为用户提供了一种专业、高效的日志服务解决方案。
【9月更文挑战第2天】SLS 虽然不是直接使用 OSS 作为底层存储,但它凭借自身独特的存储架构和功能,为用户提供了一种专业、高效的日志服务解决方案。
242 9
函数计算发布功能问题之用户在使用主流函数计算产品的日志服务时可能会遇到使用成本的问题如何解决
函数计算发布功能问题之用户在使用主流函数计算产品的日志服务时可能会遇到使用成本的问题如何解决
Java应用结构规范问题之AllLoggers接口获取异常日志的Logger实例的问题如何解决
Java应用结构规范问题之AllLoggers接口获取异常日志的Logger实例的问题如何解决
函数计算发布功能问题之查看函数的调用日志的问题如何解决
函数计算发布功能问题之查看函数的调用日志的问题如何解决
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等