创建一个抽象的 AJAX 用户控件
本文介绍了创建抽象类所需的步骤,该类已处理了派生控件的 AJAX 请求。
引言
在开发使用 AJAX 功能的 ASP.NET 网站时,迟早您会意识到开发基于 AJAX 的 Web 控件的过程开始变得重复:您最终会编写相同的 JavaScript 来创建和处理客户端以及服务器端的 XmlHttpRequest
。此时,您可能已经有一个外部 JavaScript 文件,该文件已在您的母版页中引用,**然后**您会发现所有内容都开始演变成一个巨大的 JavaScript 怪物。好吧,至少,这就是我所经历的。当我最终不得不面对一个控件需要在页面上实例化几次的问题时,我停下来思考一个更优雅的解决方案。如果我有一个抽象的 AJAXControl
类,我的控件可以从中派生,并且只添加该控件显式使用的 JavaScript(和服务器端处理)呢?
背景
要完全理解本文,您需要了解 AJAX 的工作原理。我不会详细介绍,我将向您展示如何创建一个基础类,该类可以以最小的努力实现 AJAX 开发。您将看到单个控件如何同时渲染其内容**和**处理客户端 JavaScript 请求,如何将 JavaScript 嵌入自定义控件 DLL,以及如何注册它。
使用代码
只需解压源代码。有主项目 AbstractAjaxControl,其中包含抽象控件以及一个派生自我们的抽象基类的演示控件(因此可以实例化),还有一个使用所述演示控件的简单网站。
捕获 AJAX 请求
我只打算介绍使用 HTTP_POST 发送的 AJAX 请求。AJAX POST 请求由两条 JavaScript 语句触发,然后再实际发送数据
httpRequest.open('POST', uri, true);
其中 'POST' 指定我们将使用 HTTP_POST,uri
是我们正在发布到的文档的地址,最后一个参数将请求设置为阻塞(false
,同步)或非阻塞(true
,异步)。
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
这告诉 JavaScript 发送请求,就像表单被提交一样。通过单击“提交”按钮发送表单与通过这种方式发送 XmlHttpRequest
之间没有区别,除了一个,这就是我们所使用的:ASP.NET 确实识别 Request.Form
属性,但它**不**识别回发事件。这在 ASP.NET 驱动的网站上是一种非常罕见的条件,这就是为什么我们将使用它的原因。
我们希望我们的抽象基类能够处理所述条件;我们希望派生类仅实现如何响应 AJAX 请求。为此,我声明了一个 private
事件 AjaxRequest
private delegate void AjaxRequestEventHandler(object sender, AjaxRequestEventArgs e);
private event AjaxRequestEventHandler AjaxRequest;
private void OnAjaxRequest()
{
if (AjaxRequest != null)
AjaxRequest(this, new AjaxRequestEventArgs(Page.Request.Form));
}
AjaxRequestEventArgs
是一个 EventArgs
类,它在其公共属性 Parameters
中存储 Request.Form NameValueCollection
中的所有表单元素。
问题是:何时应触发事件?
- 没有回发事件
- 我们的
Request.Form NameValueCollection
中有内容 - 这些内容是相关的,例如,在控件的范围内
- 在页面的其余部分初始化(或呈现)之前
请参阅 c) 点,我们需要一个变量来设置范围,并且我们需要所有发送数据的 JavaScript 来提供此数据的范围。我是在服务器端完成的,通过声明一个 private
成员 _scope
,并具有一个公开可用的属性 Scope
(该属性可以并且应该由派生类声明性地设置)。如果页面上存在一个以上派生自我们的抽象类的控件,Scope
将非常有用。
由于 d) 条件,找到触发 AjaxRequest
的正确时机很容易。我们需要重写 OnInit
方法。
if (!Page.IsPostBack && Page.Request.Form["scope"] == _scope)
OnAjaxRequest();
base.OnInit(e);
就 AJAX 请求的服务器端处理而言,就这样。不过,不要忘记订阅该事件,并告知所有派生类它们绝对必须实现一个方法来处理 AJAX 请求
public AjaxControl()
{
AjaxRequest += new AjaxRequestEventHandler(HandleAjaxRequest);
}
protected abstract void HandleAjaxRequest();
我们需要多少 JavaScript?
我们已经完成了一半。记住,我们不希望任何需要引用母版页的庞大 JavaScript 文件?我们想要一个所有派生自我们抽象基类的控件都使用的基本 JavaScript,再加上定义派生控件行为的少量 JavaScript 片段,并且仅随这些控件一起部署。想一想:您能想到所有 AJAX 控件有什么共同之处?哪些 JavaScript 函数属于基本功能?
- 声明一个(全局)
XmlHttpRequest
对象 - 一个用于创建此类对象的函数
- 一个用于准备给定数据并发送它们的功能
所有 AJAX JavaScript 的不同之处在于响应的处理,因此这不是我们能放在基本 JavaScript 文件中的。剩下的就是这个
var httpRequest = null;
function CreateRequest()
{
if (window.XMLHttpRequest)
httpRequest = new XMLHttpRequest();
else if (window.ActiveXObject)
httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
}
function SendRequest(parameters, responseHandler)
{
if(httpRequest == null)
CreateRequest();
httpRequest.open('POST', document.location.href, true);
httpRequest.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded');
httpRequest.onreadystatechange = responseHandler;
strPostData = "";
for(i = 0; i < parameters.length; i++)
{
strPostData += parameters[i] + "=";
i++;
strPostData += parameters[i];
if(i + 1 != parameters.length)
strPostData += "&";
}
httpRequest.send(strPostData);
}
我的全局 XmlHttpRequest
对象是 httpRequest
,它在(首次需要时)被创建一次。在 CreateRequest
方法中,我首先检查 XmlHttpRequest
是否已知(IE 7、Opera、Mozilla 为 true),如果失败则回退到旧的 ActiveXObject
。SendRequest
接受一个键值对形式的参数数组(即:"key1", "value1", "key2", "value2"...等等),将参数准备为要发送的字符串,并使用 HTTP_POST 异步发送到调用它的页面(记住:我们的控件处理自己的 AJAX 请求——这就是为什么请求被发送到的 URI 是包含我们控件的页面的 URI)。
嵌入您的脚本
我最头疼的部分是如何将脚本嵌入我的 DLL 并从那里使用它。事实证明,如果您知道如何做,这并不难。这需要四个步骤
- 为您的项目创建一个资源文件(右键单击项目,属性 -> 资源)
- 将一个 .js 文件添加到您的资源中
- 将您的 .js 文件的“生成”属性设置为“嵌入式资源”:右键单击 js 文件(通常放在名为“resources”的子文件夹中),然后您就完成了
- 将
assemby:webresource
属性设置为
[assembly: WebResource("ProjectName.Resources.Filename.js", "text/javascript")]
此属性直接插入到基类文件中的命名空间声明之上。
尽量避免在 JavaScript 文件名中使用任何点。相信我,这可以为您节省几年寿命。
嵌入脚本后,您可以将其注册到控件所在的 ASP 页面。使用属性中使用的确切名称进行此操作;例如,我在重写的 OnInit
方法中注册基本的 AJAX JavaScript,在调用 base.OnInit
之后立即进行。
Page.ClientScript.RegisterClientScriptResource(typeof(AjaxControl),
"AbstractAjaxControl.Resources.AjaxBase.js");
您可以在控件代码中的几乎任何点注册它。
演示控件
由于如果您尝试实例化一个抽象类(毕竟,这就是它抽象的原因)可能会感到失望,因此我包含了一个派生自 AjaxControl
的类来向您展示如何做到这一点。这相当直接
- 添加一个新类
- 将其派生自
AjaxControl
- 像编写普通的 ASP.NET 控件一样实现控件(也就是说,负责
RenderContents()
方法,或者如果您更像我,则负责CreateChildControls()
方法)。 - 实现继承的
HandleAjaxRequest()
方法(并确保它调用Response.End()
,我们不希望页面发生任何进一步的处理)。 - 编写一个 JavaScript 方法来处理
XmlHttpResponse
,以及一个用于准备和调用SendRequest()
方法的方法。 - 如果您愿意,可以将 JavaScript 嵌入到您的 DLL 中。不过,您不必这样做。
就是这样。
关注点
的确,这种方法比使用 ASP.NET AJAX 扩展要费力得多。另一方面,它的开销要小得多,并且您可以完全控制您的页面做什么和不做什么。而且在没有安装 ASP.NET AJAX 的环境中,安装选项也不可行。这就是最初促使我手动完成所有 AJAX 工作的原因。
如果可能,请尝试使用嵌入式资源中的 JavaScript。不再维护不断增长的 JavaScript 文件夹确实是值得的。
历史
- 第一个版本,无历史记录:2007/07/27。