基于观察者模式在 asp.net Web 窗体中实现审核跟踪

本文关键字:实现 跟踪 窗体 Web 观察者模式 asp net | 更新日期: 2023-09-27 18:30:48

我想在我的ASP.NET Web Forms应用程序中跟踪一些用户活动。例如,我将使用方法protected void btnSearch_Click(object sender, EventArgs args)因为它代表了我的更多业务需求和编程问题。

只是为了更清楚。我想要实现的是,当执行btnSearch_Click时,对包含一些基本信息的数据库表进行INSERT,例如执行了哪个确切的操作,何时执行,谁执行了它以及搜索词是什么。我会有很多想要跟踪的操作,所以表格的结构仍有待确定,但这是总体思路。

我的第一个想法是使用一些静态类,例如:

public static class UserHistory
{
  public static void Log(string methodName, string user, string message)
  {
    //execute SQL INSERT
  }
}

然后在我需要保持历史记录跟踪的事件处理程序中,只需添加:

UserHistory.Log("btnSearch_Click", "SomeUser", "The user searched for cats");

事实上,这对我来说似乎已经可以接受,并不是我不喜欢这种方法中的特别之处,但首先 - 这是非常常见的任务,此类任务通常已经有一些模式帮助您以最佳方式实现逻辑,其次,我不是很有经验,我在这里看到了通过做一些事情来扩展我的知识的机会,在这种特殊情况下可能不是好处,但无论如何都很高兴知道。

因此,这让我想到了Observer模式。老实说,与代表一起工作对我来说总是很麻烦,但我也在努力学习,我知道证明你制作了自己的 effrots 来解决一些问题是无能为力的,所以我将解释我的理解我应该使用这种模式来做到这一点,但会理解一些我可以理解的完整答案。

我找到的简单示例是来自Jon Skeet的答案,他实现了两个类:

 class Observable
    {
        public event EventHandler SomethingHappened;
        public void DoSomething()
        {
            EventHandler handler = SomethingHappened;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }

class Observer
{
    public void HandleEvent(object sender, EventArgs args)
    {
        Console.WriteLine("Something happened to " + sender);
    }
}

展示其工作原理的具体示例是:

    static void Main(string[] args)
    {
        Observable observable = new Observable();
        Observer observer = new Observer();
        observable.SomethingHappened += observer.HandleEvent;
        observable.DoSomething();
    }

也许这包含足够的信息来为 Web 表单实现此模式,但我无法在 Web Forms 的 cotntext 中检测到不同的角色。

期望有一个地方来注册我想要跟踪的所有事件,如下所示:

    protected void Page_Load(object sender, EventArgs e)
    {
        observable.SomethingHappened += btnSearch_Click;
        observable.SomethingHappened += btnLogin_Click;
    }

但是如果我完全按照这样尝试,它实际上什么也没做。我什至不进去:

        public void DoSomething()
        {
            EventHandler handler = SomethingHappened;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

这是合乎逻辑的,但是如果我必须从要记录某些内容的每个方法中显式调用此 methid,那么使用普通的旧静态方法有什么区别?另外,我想传递一个参数(字符串消息),我也不知道该怎么做。因此,我对如何与代表/活动合作的理解很差,但如果有人帮助我实现这一点,我将不胜感激。例如,可以使用btnSearch_Click事件处理程序,我想在其中传递搜索字符串作为参数。

基于观察者模式在 asp.net Web 窗体中实现审核跟踪

在这种情况下,您不必自己实现观察者模式;ASP.NET 管道已经以应用程序对象的回调形式为您实现了它;在这种特殊情况下,PostAuthorizeRequest 回调应该可以很好地处理您的需求。

首先,WebForms管道上的快速侧边栏:在WebForms中处理UI交互的方式是通过称为PostBacks的抽象。在您看来,好像您正在处理从 .aspx/.ascx 控件引发的事件,而实际上正在发生的事情是请求(包含特殊命名字段的 POST)正在发送到 Web 服务器。将创建页面的新实例来处理该请求,WebForms 基础结构检测到它是回发,并调用事件处理程序。(有关详细信息,请参阅 MSDN 文档)

这对你意味着什么?好吧,如果您在 Global.asax 中(或在代码隐藏 Global.asax.cs 中挂接应用程序的 PostAuthorizeRequest 回调,则可以在识别用户并授予他们执行请求的授权后检查请求。特别是,您需要注意三件事:首先,这是什么类型的请求?如果它不是 POST,则它不是回发,您可以在此处停止自定义处理。其次,请求的实际路径 - 这是用户与之交互的 WebForms 页面;如果它不是你对审核事件感兴趣的页面,请在此处停止。最后,回发的目标是什么?这可以通过检查 HttpContext.Request.Form["__EVENTTARGET"] 的值来确定。如果它是你感兴趣的控件的名称,现在可以记录交互。

这在实践中会是什么样子?假设您有一个元组查找表,该查找表将页面 + 控件名称映射到消息。在我的示例中,我将使用变量 auditMap,这是一个 IDictionary,为了简单起见,我将使用页面路径和控件名称的串联(例如:"MyPage.aspx::btnSearch")来执行审计消息的查找:

void Application_PostAuthorizeRequest(object sender, EventArgs e)
{
    if (HttpContext.Current.Request.HttpMethod != "POST") return;
    String key = String.Concat(HttpContext.Current.Request.Path, "::", HttpContext.Current.Request.Form["__EVENTTARGET"]);
    String message;
    if (!auditMap.TryGetValue(key, out message) return;
    auditService.LogEvent(message);
    // Can also pass HttpContext.Current.User.Identity.Name into a format string, etc.
}