.NET内置的Ajax工作原理
在期望不执行回发(postback)而从客户端运行服务器代码的情况下,可以使用ClientScriptManager类来调用客户端回调(callback)。这称为对服务器执行带外回调。在客户端回调中,客户端脚本函数项ASP.NET网页发送异步请求。网页修改其正常生命周期来处理回调。
![](https://images.cnblogs.com/cnblogs_com/gisland/ajax3.jpg)
![](https://images.cnblogs.com/cnblogs_com/gisland/ajax4.jpg)
回调的过程如下:
(1)客户端发出请求,请求内容包括:指定更新控件和传递参数。
(2)服务端响应、处理。
(3)服务器将处理后的内容以字符串返回。
(4)客户端指定一个函数接受返回值。
(5)客户端更新指定控件。
使用回调的步骤如下:
(1)在控件或page类中实现ICallbackEventHandler接口。该接口有两个方法,分别是RaiseCallbackEvent和GetCallbackResult。RaiseCallbackEvent方法是回调执行的方法,该方法处理回调的内容。它没有返回值,而是从浏览器接受一个字符串作为事件的参数,即该方法接受客户端JavaScript所传递的参数。注意它是首先触发的。接下来触发的就是GetCallbackResult方法,它将所得到的结果传回给客户端的脚本。
(2)生成调用该回调的客户端脚本。可以通过ClientScriptManager类的GetCallbackEventReference方法来生成。
(3)编写代码调用(2)中生成的客户端脚本。
以下通过一个例子来说明回调过程。。。
新建一ASP.NET 应用程序,设置Default窗体界面如下:
![](https://images.cnblogs.com/cnblogs_com/gisland/ajax3.jpg)
1,在Defalut.aspx.cs类实现ICallbackEventHandler接口,代码如下
public partial class _Default : System.Web.UI.Page,ICallbackEventHandler
{
string callbackResult;
#region ICallbackEventHandler 成员
//这是RaiseCallbackEvent方法触发之后触发的,它将得到的结果返回给客户端的脚本《《《《《《《《222222
//客户端脚本的onCallbackComplete(result,context)方法里面的result在这里其实就是指callbackResult
public string GetCallbackResult()
{
return callbackResult;
}
//回调执行的方法,首先触发的,里面的一个参数接受从客户端传来的值《《《《《《1111111
public void RaiseCallbackEvent(string eventArgument)
{
try
{
callbackResult = WebService.getResult(eventArgument);//由于这里调用的是webservice的getResult的方法,所以也可以把getResult方法放在这里。
}
catch (Exception e)
{
Trace.Write(e.Message);
throw new Exception("调用失败");
}
}
#endregion
}
{
string callbackResult;
#region ICallbackEventHandler 成员
//这是RaiseCallbackEvent方法触发之后触发的,它将得到的结果返回给客户端的脚本《《《《《《《《222222
//客户端脚本的onCallbackComplete(result,context)方法里面的result在这里其实就是指callbackResult
public string GetCallbackResult()
{
return callbackResult;
}
//回调执行的方法,首先触发的,里面的一个参数接受从客户端传来的值《《《《《《1111111
public void RaiseCallbackEvent(string eventArgument)
{
try
{
callbackResult = WebService.getResult(eventArgument);//由于这里调用的是webservice的getResult的方法,所以也可以把getResult方法放在这里。
}
catch (Exception e)
{
Trace.Write(e.Message);
throw new Exception("调用失败");
}
}
#endregion
}
2,生成调用该回调的客户端脚本。在Default类的page_Load事件中加入如下代码:
public partial class _Default : System.Web.UI.Page,ICallbackEventHandler
{
protected void Page_Load(object sender, EventArgs e)
{
if (IsCallback)
{
return;
}
//根据传入的参数返回实际的回调脚本《《《《《《《《33333333
//target处理客户端回调的服务器 Control 的名称。该控件必须实现 ICallbackEventHandler 接口并提供 RaiseCallbackEvent 方法。
//argument从客户端脚本传递给服务器端的一个参数
//clientCallback一个客户端事件处理程序的名称,该处理程序接收成功的服务器端事件的结果。
//context启动回调之前在客户端计算的客户端脚本。脚本的结果传回客户端事件处理程序。
//clientErrorCallback客户端事件处理程序的名称,该处理程序在服务器端事件处理程序出现错误时接收结果。
//useAsynctrue 表示同步执行回调;false 表示异步执行回调。
string callBackFunctionCall = ClientScript.GetCallbackEventReference(this, "getTextValue()", "onCallbackComplete", null, "onCallbackError", true);
//注册客户端脚本《《《《《《《《444444
string csname = "CallBack";//要注册的客户端脚本的键。
if (!ClientScript.IsClientScriptBlockRegistered(GetType(), csname))
{
//type要注册的客户端脚本的类型。
//key要注册的客户端脚本的键。
//script要注册的客户端脚本文本。
//addScriptTags指示是否添加脚本标记的布尔值。
ClientScript.RegisterClientScriptBlock(GetType(), csname, "function DoClientCallBack(){" + callBackFunctionCall + "}", true);
}
}
}
{
protected void Page_Load(object sender, EventArgs e)
{
if (IsCallback)
{
return;
}
//根据传入的参数返回实际的回调脚本《《《《《《《《33333333
//target处理客户端回调的服务器 Control 的名称。该控件必须实现 ICallbackEventHandler 接口并提供 RaiseCallbackEvent 方法。
//argument从客户端脚本传递给服务器端的一个参数
//clientCallback一个客户端事件处理程序的名称,该处理程序接收成功的服务器端事件的结果。
//context启动回调之前在客户端计算的客户端脚本。脚本的结果传回客户端事件处理程序。
//clientErrorCallback客户端事件处理程序的名称,该处理程序在服务器端事件处理程序出现错误时接收结果。
//useAsynctrue 表示同步执行回调;false 表示异步执行回调。
string callBackFunctionCall = ClientScript.GetCallbackEventReference(this, "getTextValue()", "onCallbackComplete", null, "onCallbackError", true);
//注册客户端脚本《《《《《《《《444444
string csname = "CallBack";//要注册的客户端脚本的键。
if (!ClientScript.IsClientScriptBlockRegistered(GetType(), csname))
{
//type要注册的客户端脚本的类型。
//key要注册的客户端脚本的键。
//script要注册的客户端脚本文本。
//addScriptTags指示是否添加脚本标记的布尔值。
ClientScript.RegisterClientScriptBlock(GetType(), csname, "function DoClientCallBack(){" + callBackFunctionCall + "}", true);
}
}
}
3,编写客户端代码,在Default.aspx文件中添加如下JavaScript脚本:
<script type ="text/javascript" >
function getTextValue()//要传递给服务器的参数
{
var arg1=document.getElementById ("Text1").value;
var arg2=document.getElementById ("Text2").value;
var arg="argA="+arg1+"&argB="+arg2;//注意这里的传递方式
return arg;
}
function onCallbackComplete(result,context)//回调成功
{
// alert(result);
document.getElementById ("TextArea1").value=result;
}
function onCallbackError()//回调失败
{
alert("回调失败!");
}
</script>
function getTextValue()//要传递给服务器的参数
{
var arg1=document.getElementById ("Text1").value;
var arg2=document.getElementById ("Text2").value;
var arg="argA="+arg1+"&argB="+arg2;//注意这里的传递方式
return arg;
}
function onCallbackComplete(result,context)//回调成功
{
// alert(result);
document.getElementById ("TextArea1").value=result;
}
function onCallbackError()//回调失败
{
alert("回调失败!");
}
</script>
最后补充在(1)中的RaiseCallbackEvent方法中调用的类webservice的getResult方法,在项目中添加一名为WebService.asmx的web服务页面。在WebService.asmx.cs文件中定义一个名为getResult的方法,这段代码其实也可以直接放置在RaiseCallbackEvent方法中,但是为了实现调用webservice的效果还是把它单独放置到一个webservice文件中,具体代码如下:
public class WebService : System.Web.Services.WebService
{
[WebMethod]
public static string getResult(string name)//静待函数
{
string[] keyValuePairs;//含有”=“的数组
string[] keyValue;//只有字符串的数组
NameValueCollection m_queryString= new NameValueCollection();//键值对的集合
keyValuePairs = name.Split("&".ToCharArray ());
if (keyValuePairs.Length > 0)//如果传递的参数是argA=arg1&argB=arg2……形式的,也就是说参数不止一个
{
for (int i = 0; i < keyValuePairs.Length; i++)
{
keyValue = keyValuePairs.GetValue(i).ToString().Split("=".ToCharArray());
m_queryString.Add(keyValue[0], keyValue[1]);
}
}
else//传递的参数只有一个
{
keyValue = name.Split("=".ToCharArray());
if (keyValue.Length > 0)//如果有参数传递过来的话
{
m_queryString.Add(keyValue[0], keyValue[1]);
}
}
//通过htQueryString["argA"]这种方式调用参数的值
return "第一个参数是: " + m_queryString["argA"].ToString() + "\n第二个参数是: " + m_queryString["argB"].ToString();
}
}
{
[WebMethod]
public static string getResult(string name)//静待函数
{
string[] keyValuePairs;//含有”=“的数组
string[] keyValue;//只有字符串的数组
NameValueCollection m_queryString= new NameValueCollection();//键值对的集合
keyValuePairs = name.Split("&".ToCharArray ());
if (keyValuePairs.Length > 0)//如果传递的参数是argA=arg1&argB=arg2……形式的,也就是说参数不止一个
{
for (int i = 0; i < keyValuePairs.Length; i++)
{
keyValue = keyValuePairs.GetValue(i).ToString().Split("=".ToCharArray());
m_queryString.Add(keyValue[0], keyValue[1]);
}
}
else//传递的参数只有一个
{
keyValue = name.Split("=".ToCharArray());
if (keyValue.Length > 0)//如果有参数传递过来的话
{
m_queryString.Add(keyValue[0], keyValue[1]);
}
}
//通过htQueryString["argA"]这种方式调用参数的值
return "第一个参数是: " + m_queryString["argA"].ToString() + "\n第二个参数是: " + m_queryString["argB"].ToString();
}
}
执行结果如下:
![](https://images.cnblogs.com/cnblogs_com/gisland/ajax4.jpg)
该页面的用户交互与上一篇博客Ajax打开三种页面的请求的页面是一样的,但是代码大大减少了,因为我们不需要编写代码来解析XML或处理XMLHttpRequest对象。