65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ASP.NET 和 AJAX 实现可扩展的工厂方法模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (6投票s)

2005年11月3日

6分钟阅读

viewsIcon

74926

downloadIcon

235

本文讨论了在服务器端构建 HTML 控件以进行 AJAX 调用的优势,并提出了一种可扩展的工厂模式。此外,还讨论了使用模块、处理程序和 ASPX 页面处理这些自定义调用的不同方法,以及 ASP.NET 库提供的优势。

引言

使用 AJAX(异步 JavaScript 和 XML)动态构建页面的通用方法是使用客户端脚本调用 Web 服务(或页面)来检索数据,并填充客户端上已有的控件。这种方法既有优点也有缺点。

优点是 GUI 元素已经放置,页面布局清晰。缺点是:

  • Web 服务或页面传输 XML,这需要在客户端和服务器之间有严格的 XML 契约(定义的模式)。
  • 需要客户端编码来使用 DOM 文档加载和解析 XML。
  • 将数据添加到 HTML 控件的代码对于每个 HTML 元素都不同。例如,下拉列表(SELECTOPTION)与表格(TABLETRTD)不同。

如果控件在服务器端创建,填充数据,并且 HTML 以及数据被序列化到客户端,这些问题就可以解决。客户端唯一的负担是为返回的 HTML 元素提供一个占位符。

ASP.NET Web 类库提供了出色的支持,可以将 HTML 元素创建为支持渲染方法的类,这为解决此问题提供了极好的机会。

为了实现工厂方法模式,我将每个调用分为两个部分:

  • 在服务器上要调用的方法作为名为“method”的键在查询字符串中发送。此方法名称由服务器端的处理器类检索,并使用反射动态调用(您也可以有一个类名)。
  • 方法所需的实际参数(为了简化设计)作为键值对在查询字符串中发送。这样,服务器端的方法就可以从 Request 对象中检索所需的参数。

为了在客户端容纳和渲染处理器返回的 HTML 元素,创建了一个 DIV 标签作为目标元素。异步调用返回后,脚本将获取 responseText 并使用 innerHTML 属性将此代码添加到 DIV 标签内,这将渲染页面上的控件。如果此动态创建的元素关联了客户端脚本,则可以选择在服务器端设置创建元素的 ID(这需要将其作为另一个参数在查询字符串中传递,如演示程序中的 setID)。

解决方案的代码可以分为以下几个部分:

  1. 初始 AJAX 调用序列。
  2. 在 ASP.NET 中处理代码(服务器端代码)。
  3. 在浏览器端处理响应。

初始客户端调用序列

  • 所有必需的参数都添加到查询字符串中(在我的示例中包括连接字符串)。这是方法特定的。例如,参见 GetTables() 方法。它只需要一个连接字符串。因此,客户端 JavaScript 将它们添加到查询字符串中,服务器端方法检索它们。
  • 查询字符串附加到 URL。URL 和扩展名取决于模块、处理程序或简单 ASPX 页面对服务器端代码的实现。
  • 调用者提供一个目标 DIV 标签来显示返回的结果。如果没有提供,则假定为默认 DIV。我在此演示中添加了一个“results” DIV
  • 使用带有回调函数的 XMLHTTP 对象进行异步调用。请务必在所有浏览器上验证这一点。DataBrowser.js 文件中的以下代码进行了此调用:
    function SendRequest(target)
    {
        AppendConnectionParameters();
        if (ajaxObj != null)
        {
            target_div.innerHTML = 
                ""<B>Already in call. Please wait ..."</B>";
            return;    // object is busy
    
        }
        target_div = (target == null) ? dataholder : target;
        target_div.innerHTML = 
             "<B><I>Getting data. Please wait ..."</I>"<B>";
    
        // for Mozilla, Firefox, Safari, and Netscape
        if (window.XMLHttpRequest)
            ajaxobj = new XMLHttpRequest();
    
        // for Internet Explorer
        if (window.ActiveXObject)
            ajaxObj = new ActiveXObject("microsoft.xmlhttp");
    
        ajaxObj.onreadystatechange = ProcessResponse; // set the call back
    
        // You can make the call in any of the following 3 ways.
        // (1) opening a aspx call, the default asp.net page 
        //     handler will be called to process aspx page
                // ajaxObj.open("GET", "AjaxCallPage.aspx?" + querystring);
        // (2) use a custom ASP.NET extension handler
                // ajaxObj.open("GET", "Data.acall?" + querystring);
        // (3) use a custome extension with a ASP.NET module installed, 
        //     no change in call from above
                // ajaxObj.open("GET", "Data.acall?" + querystring);
    
        ajaxObj.open("GET", "Data.acall?" + querystring);
        ajaxObj.send(null);                // send the request
    }

在 ASP.NET 内部处理代码(服务器端代码)

ASP.NET 提供的模块、过滤器、页面模型非常灵活,为扩展 ASP.NET 功能提供了绝佳的机会。

来自客户端的自定义调用可以通过三种方式处理:

  1. 使用 ASPX 页面 - 这是最简单的方式,涉及的开发工作最少。在提供的示例代码中,调用在 Init() 方法中处理,并关闭响应流。这避免了页面级别加载、渲染等其他事件的开销。
  2. 提供自定义处理程序 - 这绕过了默认的 ASP.NET 页面处理程序。一旦 machine.configweb.config 中配置的所有模块完成处理,调用将交给我们的处理程序。这种方法具有自定义扩展的优点,并绕过了默认页面处理程序的任何开销。这涉及在 IIS 中添加扩展,并在 CONFIG 文件中添加处理程序(见下文)。
  3. 编写自定义模块 - 自定义模块可以在 ASP.NET 内部的事件序列中非常早地处理调用,并且还提供了绕过某些模块和处理程序的优势。编写自定义模块有两种情况。BeginRequestRequest 对象可用于模块的最早事件。如果您不希望进行任何身份验证或授权,这是将数据返回给客户端的最快方式。但是,如果您的目标是安全性,请订阅 AuthenticatedAuthorized 事件并处理请求。这种方法与处理程序一样,需要向 IIS 注册扩展,并将模块添加到 CONFIG 文件中(如下所示)。
    <configuration>
      <system.web>
         <!-- registering a module -->
         <httpModules>
             <ADD type="DataBrowser.AjaxCallModule,DataBrowser" 
                                                name="ajaxcalls" />
         </httpModules>
         <!-- registering a handler -->
         <httpHandlers>
             <ADD type="DataBrowser.AjaxCallHandler,DataBrowser" 
                                                path="*.acall" verb="*"/>
         </httpHandlers>
    
          .......
    
      <system.web>
    <configuration>

为了使上述所有情况成为可能,提供了 AjaxCallProcessor 类来处理所有逻辑。此类使用反射动态调用方法,该方法返回动态创建并填充数据(根据从 Request 对象读取的客户端参数)的 GUI 元素。

下面的代码显示了 AjaxCallProcessor 类上的 ProcessAjaxCall() 方法。这也可以是一个 static 方法。我将其设为实例方法以简化代码。

internal void ProcessAjaxCall()
{
    // clear any headers added to the response 

    // header by ASP.NET HTTP pipe

    _response.Clear();
    try
    {
        // prepare connection string, part of business 

        // logic in this demo

        PrepareConnectionString(); 

        // invoke the method requested by the client 

        // dynamically using reflection

        typeof(AjaxCallProcessor).GetMethod(_request["method"],
          BindingFlags.NonPublic|BindingFlags.Instance).Invoke(this, null);

        // create a HTMLTextWriter for the response stream 

        // and let the dynamic control to create it

        HtmlTextWriter hw = new HtmlTextWriter(_response.Output);

        // let the asp.net control render itself

        dynamicControl.RenderControl(hw);
        hw.Close();
        
        if (sqlConn.State == ConnectionState.Open) // just in case :-)

            sqlConn.Close();
    }
    catch (Exception ex)
    {
        // writing back an error message as HTML to response stream

        _response.Write("<STRONG><FONT color=red>Error : ");
        _response.Write(ex.Message);
        _response.Write("</FONT></STRONG>");
    }

    // close the response stream to end the call immediately

    // this will bypass any delay in ASP.NET HTTP pipe

    _response.End();
}

在浏览器端处理响应

  • 在回调函数内部,检查异步调用的响应。
  • 如果 readyState 为 4,则调用已完成,对象的 responseText 返回服务器的回复。
  • 响应预计是完整的 HTML(如设计)。获取 responseText 并使用 innerText 属性将其添加到提供的 DIV 标签内。

    注意:有时您可能不希望在服务器端发生异常时丢失实际的控件。您可以在 responseText 中检查前缀或其他内容,并相应地处理它。例如,不是传递 HTML,而是在 <CUSTOM_ERRROR> 标签中传递错误消息,并在客户端检查此标签。

    function ProcessResponse()
    {
        // target_div.innerHTML += ajaxObj.readyState; // for testing only
    
        if (ajaxObj.readyState == 4)    // request completed
    
        {
            target_div.innerHTML = ajaxObj.responseText;
            ajaxObj = null;
            target_div = null;
        }
    }

随附的演示程序演示了此技术的使用。请参见下面的屏幕截图,它们显示了如何在客户端替换控件。

参考文献

© . All rights reserved.