异步HttpModule的实现方式
在【用Asp.net写自己的服务框架】中,我示范
{
public void Init(HttpApplication app)
{
app.PostAuthorizeRequest += new EventHandler(app_PostAuthorizeRequest);
}
过如果编写一个HttpModule,通常只要我们实现IHttpModule接口,并在Init方法中订阅一些事件就可以了:
HttpHandler有异步接口的IHttpAsyncHandler,但HttpModule却只有一个接口:IHttpModule,不管是同步还是异步。异步HttpModule的实现方式并不是订阅HttpApplication的事件,而是调用HttpApplication的一些注册异步操作的方法来实现的(还是在Init事件中),这些方法可参考以下列表:
// 添加到当前请求的异步 System.Web.HttpApplication.AcquireRequestState事件处理程序的集合。
public void AddOnAcquireRequestStateAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.AuthenticateRequest 事件
// 添加到当前请求的异步 System.Web.HttpApplication.AuthenticateRequest事件处理程序的集合。
public void AddOnAuthenticateRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.AuthorizeRequest 事件
// 添加到当前请求的异步 System.Web.HttpApplication.AuthorizeRequest事件处理程序的集合。
public void AddOnAuthorizeRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.BeginRequest 事件
// 添加到当前请求的异步 System.Web.HttpApplication.BeginRequest事件处理程序的集合。
public void AddOnBeginRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.EndRequest 事件
// 添加到当前请求的异步 System.Web.HttpApplication.EndRequest事件处理程序的集合。
public void AddOnEndRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
public void AddOnLogRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
public void AddOnMapRequestHandlerAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.PostAcquireRequestState 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostAcquireRequestState事件处理程序的集合。
public void AddOnPostAcquireRequestStateAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.PostAuthenticateRequest 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostAuthenticateRequest事件处理程序的集合。
public void AddOnPostAuthenticateRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.PostAuthorizeRequest 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostAuthorizeRequest事件处理程序的集合。
public void AddOnPostAuthorizeRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
public void AddOnPostLogRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.PostMapRequestHandler 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostMapRequestHandler事件处理程序的集合。
public void AddOnPostMapRequestHandlerAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.PostReleaseRequestState 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostReleaseRequestState事件处理程序的集合。
public void AddOnPostReleaseRequestStateAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.PostRequestHandlerExecute 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostRequestHandlerExecute事件处理程序的集合。
public void AddOnPostRequestHandlerExecuteAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.PostResolveRequestCache 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostResolveRequestCache事件处理程序的集合。
public void AddOnPostResolveRequestCacheAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.PostUpdateRequestCache 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostUpdateRequestCache事件处理程序的集合。
public void AddOnPostUpdateRequestCacheAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.PreRequestHandlerExecute 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PreRequestHandlerExecute事件处理程序的集合。
public void AddOnPreRequestHandlerExecuteAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.ReleaseRequestState 事件
// 添加到当前请求的异步 System.Web.HttpApplication.ReleaseRequestState事件处理程序的集合。
public void AddOnReleaseRequestStateAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.ResolveRequestCache 事件处理程序
// 添加到当前请求的异步 System.Web.HttpApplication.ResolveRequestCache事件处理程序的集合。
public void AddOnResolveRequestCacheAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
// 将指定的 System.Web.HttpApplication.UpdateRequestCache 事件
// 添加到当前请求的异步 System.Web.HttpApplication.UpdateRequestCache事件处理程序的集合。
public void AddOnUpdateRequestCacheAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);
每个方法的含义从它们的名字是可以看出。异步HttpModule的实现方式需要将异步对应的Begin/End二个方法分别做为委托参数传入这些方法中。
注意:这些方法的签名与Page.AddOnPreRenderCompleteAsync()是一致的,因此它们的具体用法也与Page.AddOnPreRenderCompleteAsync()一样。
为什么这里不设计成订阅事件的方式?
我想是因为:如果采用事件模式,调用者可以只订阅其中的一个事件,ASP.NET不容易控制,还有"object state"这个参数不便于在订阅事件时传入。
异步HttpModule的示例代码如下:
/// 【示例代码】演示异步的HttpModule
/// 说明:这个示例一丁点意义也没有,纯粹是为了演示。
/// </summary>
public class MyAsyncHttpModule : IHttpModule
{
public static readonly object HttpContextItemsKey = new object();
private static readonly string s_QueryDatabaseListScript =
@"select dtb.name from master.sys.databases as dtb order by 1";
private static readonly string s_ConnectionString =
@"server=localhost\sqlexpress;Integrated Security=SSPI;Asynchronous Processing=true";
public void Init(HttpApplication app)
{
// 注册异步事件
app.AddOnBeginRequestAsync(BeginCall, EndExecuteReader, null);
}
private IAsyncResult BeginCall(object sender, EventArgs e, AsyncCallback cb, object extraData)
{
SqlConnection connection = new SqlConnection(s_ConnectionString);
connection.Open();
SqlCommand command = new SqlCommand(s_QueryDatabaseListScript, connection);
CallbackParam cbParam = new CallbackParam {
Command = command,
Context = HttpContext.Current
};
return command.BeginExecuteReader(cb, cbParam);
}
private class CallbackParam
{
public SqlCommand Command;
public HttpContext Context;
}
private void EndExecuteReader(IAsyncResult ar)
{
CallbackParam cbParam = (CallbackParam)ar.AsyncState;
StringBuilder sb = new StringBuilder();
try {
using( SqlDataReader reader = cbParam.Command.EndExecuteReader(ar) ) {
while( reader.Read() ) {
sb.Append(reader.GetString(0)).Append("; ");
}
}
}
catch( Exception ex ) {
cbParam.Context.Items[HttpContextItemsKey] = ex.Message;
}
finally {
cbParam.Command.Connection.Close();
}
if( sb.Length > 0 )
cbParam.Context.Items[HttpContextItemsKey] = "数据库列表:" + sb.ToString(0, sb.Length - 2);
}
public void Dispose()
{
}
}
页面可以使用如下方式获得MyAsyncHttpModule的结果:
{
protected void Page_Load(object sender, EventArgs e)
{
string result = (string)HttpContext.Current.Items[MyAsyncHttpModule.HttpContextItemsKey]
?? "没有开启MyAsyncHttpModule,请在web.config中启用它。";
Response.Write(result);
}
}
说明:管线处理过程中,可能有多个HttpModule,但是异步的HttpModule在执行时,只是在一个阶段内,所有的HttpModule采用异步方式工作。当进入下一个阶段前,必须要等到所有HttpModule全部在当前阶段内执行完毕。
通常情况下,是没有必要写异步的HttpModule的。这是我写的第一个异步HttpModule。
异步的 Web Service
由于Web Service也是受ASP.NET支持,且随着ASP.NET一起出现。我们再来看一下如果将一个同步的服务方法改变成异步的方法。
注意:将方法由同步改成异步版本,是不影响客户端的。
以下代码是一个同步版本的服务方法:
public string ExtractNumber(string str)
{
//return ........
}
再来看一下最终的异步实现版本:
public IAsyncResult BeginExtractNumber(string str, AsyncCallback cb, object state)
{
MyHttpClient<string, string> http = new MyHttpClient<string, string>();
http.UserData = "Begin ThreadId: " + Thread.CurrentThread.ManagedThreadId.ToString();
return http.BeginSendHttpRequest(ServiceUrl, str, cb, http);
}
[WebMethod]
public string EndExtractNumber(IAsyncResult ar)
{
MyHttpClient<string, string> http = (MyHttpClient<string, string>)ar.AsyncState;
try{
return http.EndSendHttpRequest(ar) +
", " + http.UserData.ToString() +
", End ThreadId: " + Thread.CurrentThread.ManagedThreadId.ToString();
}
catch(Exception ex){
return ex.ToString();
}
}
其实,要做的修改与IHttpHandler到IHttpAsyncHandler的工作差不多,在原有的同步方法后面加二个与异步操作有关的参数,并且返回值改为IAsyncResult,然后再添加一个EndXxxx方法就可以了,当然了,EndXxxx方法的传入参数只能是一个IAsyncResult类型的参数。