httpmodule和httphandler的原理,我记得在stackoverflow里有个形象的比喻,请求如果是辆火车,httpmodule就是中途的站点, handler就是终点站。详细原理可参考https://www.cnblogs.com/caoyc/p/6409062.html
使用HttpModule可以在不改动现有代码的情况下实现一些功能,比如监听URL请求,获取session的值,并写入到数据库里,下面说说方法和例子
1.创建一个.net的dll项目
2. 创建一个实现IHttpModule的类,例子代码如下
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Hosting;
using System.Web.SessionState;
using System.Xml.Linq;
namespace LogRequestModule
{
public class LogHttpModule : IHttpModule, IRegisteredObject
{
public void Dispose()
{
}
public class LogData
{
public string url { get; set; }
public string sessionvalue1 { get; set; }
}
public static AutoResetEvent logEvent = new AutoResetEvent(true);
public static ConcurrentQueue<LogData> queue = new ConcurrentQueue<LogData>();
public static bool runningWriteLog = true;
public static void WriteLog()
{
//Get connection string from web.config file
var connStringSetting = ConfigurationManager.ConnectionStrings["LogModuleDBConnection"];
if (connStringSetting == null)
return;
while (runningWriteLog)
{
try
{
LogData logData;
using (var conn = new SqlConnection(connStringSetting.ConnectionString))
{
using (var cmd = new SqlCommand("SP_Log", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("url", SqlDbType.NVarChar, 1024);
cmd.Parameters.Add("sessionvalue1", SqlDbType.NVarChar, 32);
conn.Open();
while (queue.TryDequeue(out logData))
{
cmd.Parameters["url"].Value = logData.url;
cmd.Parameters["sessionvalue1"].Value = logData.sessionvalue1;
cmd.ExecuteNonQuery();
}
}
conn.Close();
}
logEvent.WaitOne(3000);
}
catch (Exception ex)
{
}
}
}
public void Init(HttpApplication context)
{
HostingEnvironment.RegisterObject(this);
Task.Factory.StartNew(WriteLog);
context.PostAcquireRequestState += new EventHandler(Application_PostAcquireRequestState);
context.PostMapRequestHandler += new EventHandler(Application_PostMapRequestHandler);
}
private void Application_PostMapRequestHandler(object source, EventArgs e)
{
HttpApplication app = (HttpApplication) source;
if (app.Context.Handler is IReadOnlySessionState || app.Context.Handler is IRequiresSessionState)
{
// no need to replace the current handler
return;
}
// swap the current handler
app.Context.Handler = new TempSessionHttpHandler(app.Context.Handler);
}
private void Application_PostAcquireRequestState(object source, EventArgs e)
{
HttpApplication app = (HttpApplication) source;
var resourceHttpHandler = HttpContext.Current.Handler as TempSessionHttpHandler;
if (resourceHttpHandler != null)
{
// set the original handler back
HttpContext.Current.Handler = resourceHttpHandler.OriginalHandler;
}
// -> at this point session state should be available
// Debug.Assert(app.Session != null, "it did not work :(");
//Log operation goes here
HttpContext context = app.Context;
string url = context.Request.Url.OriginalString;
var reqData = new LogData
{
url = url,
sessionvalue1 = app.Session["sessionvalue1"] as string,
};
queue.Enqueue(reqData);
logEvent.Set();
}
public void Stop(bool immediate)
{
runningWriteLog = false;
}
}
}
如果要获取session值,还需要定义一个handler,例子代码如下
// a temp handler used to force the SessionStateModule to load session state
using System;
using System.Web;
using System.Web.SessionState;
namespace LogModule
{
public class TempSessionHttpHandler : IHttpHandler, IRequiresSessionState
{
internal readonly IHttpHandler OriginalHandler;
public TempSessionHttpHandler(IHttpHandler originalHandler)
{
OriginalHandler = originalHandler;
}
public void ProcessRequest(HttpContext context)
{
// do not worry, ProcessRequest() will not be called, but let's be safe
throw new InvalidOperationException("MyHttpHandler cannot process requests.");
}
public bool IsReusable
{
// IsReusable must be set to false since class has a member!
get { return false; }
}
}
}
3. 编译好后生成dll, 部署到要监听的站点的bin目录下
4. 在web.config增加module配置
修改该站点的web.config文件
在modules配置节点下增加一条记录,如下
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" >
<add name="LogHttpModule" type="LogModule.LogHttpModule,LogModule"/>
</modules>
</system.webServer>
注意:如果该站点下如果有子站点,需要在各个子站点增加remove记录,否则会导致子站点继承父站点配置而找不到module报错。如下
<!--Web Server-->
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" >
<remove name="LogHttpModule" />
</modules>
</system.webServer>
5. 添加其他配置,如数据库连接,创建数据库存储过程等,这里就不再赘述。