异步HttpModule的实现方式

【用Asp.net写自己的服务框架】中, 我示范过如果编写一个HttpModule,通常只要我们实现IHttpModule接口,并在Init方法中订阅一些事件就可以了:

复制代码
internal class DirectProcessRequestMoudle : IHttpModule
{
    public void Init(HttpApplication app)
    {
        app.PostAuthorizeRequest += new EventHandler(app_PostAuthorizeRequest);
    }

HttpHandler有异步接口的IHttpAsyncHandler,但HttpModule却只有一个接口:IHttpModule,不管是同步还是异步。 异步HttpModule的实现方式并不是订阅HttpApplication的事件,而是调用HttpApplication的一些注册异步操作的方法来实现的**(还是在Init事件中)**, 这些方法可参考以下列表:

复制代码
// 将指定的 System.Web.HttpApplication.AcquireRequestState 事件
// 添加到当前请求的异步 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的示例代码如下:

复制代码
/// <summary>
/// 【示例代码】演示异步的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的结果:

复制代码
public partial class TestMyAsyncHttpModule : System.Web.UI.Page
{
    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一起出现。我们再来看一下如果将一个同步的服务方法改变成异步的方法。

注意:将方法由同步改成异步版本,是不影响客户端的。

以下代码是一个同步版本的服务方法:

复制代码
[WebMethod]
public string ExtractNumber(string str)
{
    //return ........
}

再来看一下最终的异步实现版本:

复制代码
[WebMethod]
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方法的传入

相关推荐
William_cl4 分钟前
WinForms EF Core 上下文频繁释放引发的异常排查与规范方案(MES 项目实录)
服务器·数据库·oracle
kingwebo'sZone10 分钟前
C#事件声明办法
java·前端·c#
战场小包17 分钟前
世界杯结束了,我用 AI 造了平行宇宙,这次结局你写
前端·人工智能·ai编程
冻感糕人~20 分钟前
大模型学习指南:收藏这份AI Agent四层工程地图(小白程序员必备)
java·大数据·人工智能·学习·大模型·agent·大模型学习
方方洛24 分钟前
AI 教程系列-TUI 应用开发教程03-TUI 框架与技术选型
前端·javascript·ui kit
第二个人41 分钟前
Python Web开发:从Flask到FastAPI,我经历了什么
前端·python·flask
一路向北North1 小时前
Spring AI(6) :对话机器人-会话历史
java·人工智能·spring
其实防守也摸鱼1 小时前
前端应用的离线暂停更新策略:构建稳定可靠的渐进式部署方案
服务器·前端·数据库·学习·ai·命令行·linux系统
Android系统攻城狮1 小时前
Linux PipeWire深度解析之pw_context_add_spa_lib调用流程与实战(二十九)
linux·运维·服务器·音频进阶·pipewire音频进阶
太平洋月光1 小时前
Antv G2中自定义技巧📊
前端·数据可视化