forked from NewLifeX/X
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUserBehaviorModule..cs
More file actions
214 lines (169 loc) · 7.2 KB
/
UserBehaviorModule..cs
File metadata and controls
214 lines (169 loc) · 7.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#if !__CORE__
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.UI;
using NewLife.Collections;
using NewLife.Log;
using NewLife.Model;
using NewLife.Threading;
using NewLife.Web;
using XCode.Membership;
namespace XCode.Web
{
/// <summary>用户行为模块,在线和操作记录</summary>
public class UserBehaviorModule : IHttpModule
{
#region IHttpModule Members
void IHttpModule.Dispose() { }
/// <summary>初始化模块,准备拦截请求。</summary>
/// <param name="context"></param>
void IHttpModule.Init(HttpApplication context)
{
context.AcquireRequestState += OnSession;
//context.PreRequestHandlerExecute += OnSession;
context.PostRequestHandlerExecute += OnPost;
context.Error += OnPost;
context.BeginRequest += OnInit;
context.PostReleaseRequestState += OnEnd;
}
private void OnSession(Object sender, EventArgs e)
{
// 会话状态已创建一个会话 ID,但由于响应已被应用程序刷新而无法保存它
// 避免后面使用SessionID时报错
var sid = HttpContext.Current?.Session?.SessionID;
}
#endregion
/// <summary>Web在线</summary>
public static Boolean WebOnline { get; set; }
/// <summary>访问记录</summary>
public static Boolean WebBehavior { get; set; }
/// <summary>访问统计</summary>
public static Boolean WebStatistics { get; set; }
void OnInit(Object sender, EventArgs e) => ManageProvider.UserHost = GetIP();
void OnEnd(Object sender, EventArgs e) => ManageProvider.UserHost = null;
void OnPost(Object sender, EventArgs e)
{
if (!WebOnline && !WebBehavior && !WebStatistics) return;
var ctx = HttpContext.Current;
if (ctx == null) return;
var req = ctx.Request;
if (req == null) return;
var user = ctx.User?.Identity as IManageUser ?? ManageProvider.User as IManageUser;
var sid = ctx.Session?.SessionID;
var ip = GetIP();
var page = GetPage(req);
// 过滤后缀
var ext = Path.GetExtension(page);
if (!ext.IsNullOrEmpty() && ExcludeSuffixes.Contains(ext)) return;
var title = GetTitle(ctx, req);
var msg = GetMessage(ctx, req, title);
ThreadPoolX.QueueUserWorkItem(() =>
{
try
{
// 统计网页状态
if (WebOnline && !sid.IsNullOrEmpty()) UserOnline.SetWebStatus(sid, page, msg, user, ip);
// 记录用户访问的Url
if (WebBehavior) SaveBehavior(user, ip, page, msg);
// 每个页面的访问统计
if (WebStatistics) SaveStatistics(ctx, user, ip, page, title);
}
catch (Exception ex)
{
XTrace.WriteException(ex);
}
});
}
String GetMessage(HttpContext ctx, HttpRequest req, String title)
{
var sb = Pool.StringBuilder.Get();
if (!title.IsNullOrEmpty()) sb.Append(title + " ");
sb.AppendFormat("{0} {1}", req.HttpMethod, req.RawUrl);
var err = GetError(ctx)?.Message;
if (!err.IsNullOrEmpty()) sb.Append(" " + err);
var ts = DateTime.Now - ctx.Timestamp;
sb.AppendFormat(" {0:n0}ms", ts.TotalMilliseconds);
return sb.Put(true);
}
String GetTitle(HttpContext ctx, HttpRequest req)
{
var title = ctx.Items["Title"] + "";
if (title.IsNullOrEmpty())
{
if (ctx.Handler is Page page) title = page.Title;
}
// 有些标题是 Description,需要截断处理
if (title.Contains(",")) title = title.Substring(null, ",");
if (title.Contains(",")) title = title.Substring(null, ",");
if (title.Contains("。")) title = title.Substring(null, "。");
return title;
}
String GetPage(HttpRequest req)
{
var p = req.Path;
if (p.IsNullOrEmpty()) return null;
var ss = p.Split('/');
if (ss.Length == 0) return p;
// 如果最后一段是数字,则可能是参数,需要去掉
if (ss[ss.Length - 1].ToInt() > 0) p = "/" + ss.Take(ss.Length - 1).Join("/");
return p;
}
/// <summary>忽略的后缀</summary>
public static HashSet<String> ExcludeSuffixes { get; set; } = new HashSet<String>(StringComparer.OrdinalIgnoreCase) {
".js", ".css", ".png", ".jpg", ".gif", ".ico", // 脚本样式图片
".woff", ".woff2", ".svg", ".ttf", ".otf", ".eot" // 字体
};
void SaveBehavior(IManageUser user, String ip, String page, String msg)
{
if (page.IsNullOrEmpty()) return;
LogProvider.Provider?.WriteLog("访问", "记录", msg, user?.ID ?? 0, user + "", ip);
}
void SaveStatistics(HttpContext ctx, IManageUser user, String ip, String page, String title)
{
var ts = DateTime.Now - ctx.Timestamp;
// 访问统计
//VisitStat.Add(page, title, (Int32)ts.TotalMilliseconds, user?.ID ?? 0, ip, GetError(ctx)?.Message);
var model = new VisitStatModel
{
Time = DateTime.Now,
Page = page,
Title = title,
Cost = (Int32)ts.TotalMilliseconds,
User = user + "",
IP = ip,
Error = GetError(ctx)?.Message,
};
VisitStat.Process(model);
}
/// <summary>获取错误,排除HttpException</summary>
/// <param name="ctx"></param>
/// <returns></returns>
Exception GetError(HttpContext ctx)
{
var ex = ctx?.Error;
if (ex != null && ex is HttpException && ex.InnerException != null) ex = ex.InnerException;
return ex;
}
String GetIP()
{
var ctx = HttpContext.Current;
if (ctx == null) return null;
var req = ctx.Request;
if (req == null) return null;
var ip = (String)ctx.Items["UserHostAddress"];
if (ip.IsNullOrEmpty()) ip = req.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (ip.IsNullOrEmpty()) ip = req.ServerVariables["X-Real-IP"];
if (ip.IsNullOrEmpty()) ip = req.ServerVariables["X-Forwarded-For"];
if (ip.IsNullOrEmpty()) ip = req.ServerVariables["REMOTE_ADDR"];
if (ip.IsNullOrEmpty()) ip = req.UserHostName;
if (ip.IsNullOrEmpty()) ip = req.UserHostAddress;
return ip;
}
}
}
#endif