Callback vs Atlas
首先,谈谈Atlas。很多朋友可能会觉得奇怪,已经有了Callback,为什么又要出Atlas呢?关于这个问题,Atlas的作者怎么解释,我倒没有去调查。只不过从我个人对callback和atlas的使用感受来讲,觉得,callback作为一个接口和postback非常类似的实现,肯定是为了让用户类似使用postback来使用它。但是,它的这个类似postback的机制,应该说使用上还不是特别方便,也不易扩展,当然这是相比于其他的AJAX框架实现来说的。因此,微软方面借鉴了许多的已有的AJAX实现,如Prototype,Backbase以及AJAX.NET,并结合ASP.NET2.0的部分特有功能,发明了这样一个博采众长的AJAX框架。基于Atlas来开发AJAX应用有多好,很难量化的来说,但至少不比其他的这些AJAX框架来的差是肯定的,加上微软这个后台,以及像live.com这样的重量级站点的应用推广,其影响当然是值得期待的。
不过,这也不是说callback实现没一无是处了,作为程序员,我们需要有正确的态度,在正确的使用情形,使用最正确的技术。没有哪一个框架是万能的,是适合任何使用环境的;就像大家都在争论那个软件开发方法最好,CMMi,RUP,XP,AGILE~~,其实,没有最好,最合适的才是最好的。我们最应该做的,是了解各种方案的原理和优缺点,从而,合理的使用正确的工具来解决实际问题。
Begin from Client Script
我们都知道,凡是AJAX,从底层来讲,无外乎两种实现机制:XMLHTTP以及IFRAME。在AJAX这个词获得广泛关注之前,其实,基于这两种底层实现的功能框架,或者基于这两种技术的无刷新效果实现就已经被广泛的使用了。当然,发展到今天,在使用接口方面,这些底层机制的细节往往被框架给隐藏了,使用接口变得越来越简单,用户只要调用这些简单接口,没有必要知道具体是怎么实现效果的了。
不过,这里我们既然是要解析callback的实现机制,那还是让我们从一个callback调用的客户端脚本调用开始,看看,微软是怎么实现这个callback机制的。
1、ClientScript.GetCallbackEventReference(...)
要激发一个callback,首先,当然需要在客户端本中发出一个调用。一个典型的调用语法如下:
function any_script_function(arg, context)
{
<%= ClientScript.GetCallbackEventReference(this, "arg", "ReceiveServerData", "context")%>;
}
</ script >
ClientScript.GetCallbackEventReference(...)将根据传入的参数返回实际的回调脚本。这个函数有多个重载版本,因此,这些参数的含义,大家可以参考MSDN。以具体的上面这段示例代码中的参数来说:
- this表示执行回调的的服务端控件是当前这个Page,当前的Page必须实现ICallbackEventHandler接口,包括必须实现string GetCallbackResult()和void RaiseCallbackEvent(eventArgument)这两个接口函数,这个参数也可以是指向某个WEB控件的引用,当然,这个空间也必须实现ICallbackEventHandler接口;
- "arg"是将被传给RaiseCallbackEvent的参数eventArgument的值,可以使人以自定义格式的字符串;
- "ReceiveServerData"是当回调成功之后,处理返回内容的客户端脚本函数的名称,这个函数必须存在于执行回调的页面,并且这个函数可以包含两个参数,例如:
function ReceiveServerData(result, context)
{
}
</ script >
这两个参数,分别是回调的返回数据result,和原封不动被返回的我们激发回调时的这个context参数,当然,这两个参数都是字符串类型的。
- "context"就不用多解释了,记得这个参数会被原封不动的传给指定的返回数据处理函数就行了。MSDN的官方文档说,context一般可用来传递需要在客户端的返回数据处理函数中用来调用的脚本代码,不过实际上,你传什么都可以,把它看成一种从客户端回调的的激发端,到处理返回数据的接收段之间的参数传递通道就行了。
2、WebForm_DoCallback(...)
Ok,明白了以上代码的含义,下面我们来看看,前面的这条“<%= ClientScript.GetCallbackEventReference(this, "arg", "ReceiveServerData", "context")%>;”在运行时会被解析成什么样子呢?我们只要在页面运行时察看页面源码就可以看到,实际上服务器帮我们生成了下面这段script代码:
function any_script_function()
{
WebForm_DoCallback('__Page',arg,ReceiveServerData,context,null,false);
}
</ script >
这段代码是什么意思呢?很显然的他调用了一个系统与定义的script函数:WebForm_DoCallback。我们要把这个函数找出来看看它具体为我们干了什么。在运行时的页面源码中,我们很容易可以找到这段脚本的出处。我们注意到有一个script,src="/TestCallbackWeb/WebResource.axd?d=HEcYmh-7_szSIu1D_mHSEw2&t=632661779991718750",这里就定义了WebForm_DoCallback。让我们把它用flashget下载下来,将扩展名改为.js。看看源码吧,没有被混淆的,所以很容易看明白。我这里就只把和callback相关的部分贴出来解释一下,见代码中的注释:
var __pendingCallbacks = new Array();
var __synchronousCallBackIndex = - 1 ;
// 回调主函数WebForm_DoCallback
function WebForm_DoCallback(eventTarget, eventArgument, eventCallback, context, errorCallback, useAsync) {
//构造回调参数,回调参数包括了原来页面上的formpostdata和我们传递的目标控件、eventArgument和部分验证信息
var postData = __theFormPostData +
"__CALLBACKID=" + WebForm_EncodeCallback(eventTarget) +
"&__CALLBACKPARAM=" + WebForm_EncodeCallback(eventArgument);
if (theForm["__EVENTVALIDATION"]) {
postData += "&__EVENTVALIDATION=" + WebForm_EncodeCallback(theForm["__EVENTVALIDATION"].value);
}
//下面实例化XMLHTTP对象,如果浏览器支持XMLHTTP则直接用XMLHTTP执行异步回调
var xmlRequest,e;
try {
xmlRequest = new XMLHttpRequest();
}
catch(e) {
try {
xmlRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e) {
}
}
var setRequestHeaderMethodExists = true;
try {
setRequestHeaderMethodExists = (xmlRequest && xmlRequest.setRequestHeader);
}
catch(e) {}
var callback = new Object();
callback.eventCallback = eventCallback;
callback.context = context;
callback.errorCallback = errorCallback;
callback.async = useAsync;
//获取对应的回调对象
var callbackIndex = WebForm_FillFirstAvailableSlot(__pendingCallbacks, callback);
if (!useAsync) {
if (__synchronousCallBackIndex != -1) {
__pendingCallbacks[__synchronousCallBackIndex] = null;
}
__synchronousCallBackIndex = callbackIndex;
}
if (setRequestHeaderMethodExists) {
xmlRequest.onreadystatechange = WebForm_CallbackComplete;
callback.xmlRequest = xmlRequest;
xmlRequest.open("POST", theForm.action, true);
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlRequest.send(postData);
return;
}
//万一浏览器不支持XMLHTTP的话,我们IFRAME方案代替,在一个隐藏的IFRAME中执行Postback
callback.xmlRequest = new Object();
var callbackFrameID = "__CALLBACKFRAME" + callbackIndex;
var xmlRequestFrame = document.frames[callbackFrameID];
if (!xmlRequestFrame) {
xmlRequestFrame = document.createElement("IFRAME");
xmlRequestFrame.width = "1";
xmlRequestFrame.height = "1";
xmlRequestFrame.frameBorder = "0";
xmlRequestFrame.id = callbackFrameID;
xmlRequestFrame.name = callbackFrameID;
xmlRequestFrame.style.position = "absolute";
xmlRequestFrame.style.top = "-100px"
xmlRequestFrame.style.left = "-100px";
try {
if (callBackFrameUrl) {
xmlRequestFrame.src = callBackFrameUrl;
}
}
catch(e) {}
document.body.appendChild(xmlRequestFrame);
}
var interval = window.setInterval(function() {
xmlRequestFrame = document.frames[callbackFrameID];
if (xmlRequestFrame && xmlRequestFrame.document) {
window.clearInterval(interval);
xmlRequestFrame.document.write("");
xmlRequestFrame.document.close();
xmlRequestFrame.document.write('<html><body><form method="post"><input type="hidden" name="__CALLBACKLOADSCRIPT" value="t"></form></body></html>');
xmlRequestFrame.document.close();
xmlRequestFrame.document.forms[0].action = theForm.action;
var count = __theFormPostCollection.length;
var element;
for (var i = 0; i < count; i++) {
element = __theFormPostCollection[i];
if (element) {
var fieldElement = xmlRequestFrame.document.createElement("INPUT");
fieldElement.type = "hidden";
fieldElement.name = element.name;
fieldElement.value = element.value;
xmlRequestFrame.document.forms[0].appendChild(fieldElement);
}
}
var callbackIdFieldElement = xmlRequestFrame.document.createElement("INPUT");
callbackIdFieldElement.type = "hidden";
callbackIdFieldElement.name = "__CALLBACKID";
callbackIdFieldElement.value = eventTarget;
xmlRequestFrame.document.forms[0].appendChild(callbackIdFieldElement);
var callbackParamFieldElement = xmlRequestFrame.document.createElement("INPUT");
callbackParamFieldElement.type = "hidden";
callbackParamFieldElement.name = "__CALLBACKPARAM";
callbackParamFieldElement.value = eventArgument;
xmlRequestFrame.document.forms[0].appendChild(callbackParamFieldElement);
if (theForm["__EVENTVALIDATION"]) {
var callbackValidationFieldElement = xmlRequestFrame.document.createElement("INPUT");
callbackValidationFieldElement.type = "hidden";
callbackValidationFieldElement.name = "__EVENTVALIDATION";
callbackValidationFieldElement.value = theForm["__EVENTVALIDATION"].value;
xmlRequestFrame.document.forms[0].appendChild(callbackValidationFieldElement);
}
var callbackIndexFieldElement = xmlRequestFrame.document.createElement("INPUT");
callbackIndexFieldElement.type = "hidden";
callbackIndexFieldElement.name = "__CALLBACKINDEX";
callbackIndexFieldElement.value = callbackIndex;
xmlRequestFrame.document.forms[0].appendChild(callbackIndexFieldElement);
xmlRequestFrame.document.forms[0].submit();
}
}, 10);
}
// 该函数在每次回调结束后会调用来检查当前的回调列表中的回调的执行情况,如果,执行完毕的,则从列表中删除回调对象,并删除临时建立的IFRAME
function WebForm_CallbackComplete() {
for (i = 0; i < __pendingCallbacks.length; i++) {
callbackObject = __pendingCallbacks[i];
if (callbackObject && callbackObject.xmlRequest && (callbackObject.xmlRequest.readyState == 4)) {
WebForm_ExecuteCallback(callbackObject);
if (!__pendingCallbacks[i].async) {
__synchronousCallBackIndex = -1;
}
__pendingCallbacks[i] = null;
var callbackFrameID = "__CALLBACKFRAME" + i;
var xmlRequestFrame = document.getElementById(callbackFrameID);
if (xmlRequestFrame) {
xmlRequestFrame.parentNode.removeChild(xmlRequestFrame);
}
}
}
}
// 该函数执行我们在回调激发端指定的处理返回数据的script函数,如我们上面范例代码中的ReceiveServerData函数
function WebForm_ExecuteCallback(callbackObject) {
var response = callbackObject.xmlRequest.responseText;
if (response.charAt(0) == "s") {
if ((typeof(callbackObject.eventCallback) != "undefined") && (callbackObject.eventCallback != null)) {
callbackObject.eventCallback(response.substring(1), callbackObject.context);
}
}
else if (response.charAt(0) == "e") {
if ((typeof(callbackObject.errorCallback) != "undefined") && (callbackObject.errorCallback != null)) {
callbackObject.errorCallback(response.substring(1), callbackObject.context);
}
}
else {
var separatorIndex = response.indexOf("|");
if (separatorIndex != -1) {
var validationFieldLength = parseInt(response.substring(0, separatorIndex));
if (!isNaN(validationFieldLength)) {
var validationField = response.substring(separatorIndex + 1, separatorIndex + validationFieldLength + 1);
if (validationField != "") {
var validationFieldElement = theForm["__EVENTVALIDATION"];
if (!validationFieldElement) {
validationFieldElement = document.createElement("INPUT");
validationFieldElement.type = "hidden";
validationFieldElement.name = "__EVENTVALIDATION";
theForm.appendChild(validationFieldElement);
}
validationFieldElement.value = validationField;
}
if ((typeof(callbackObject.eventCallback) != "undefined") && (callbackObject.eventCallback != null)) {
callbackObject.eventCallback(response.substring(separatorIndex + validationFieldLength + 1), callbackObject.context);
}
}
}
}
}
// 获取对应的回调对象
function WebForm_FillFirstAvailableSlot(array, element) {
var i;
for (i = 0; i < array.length; i++) {
if (!array[i]) break;
}
array[i] = element;
return i;
}
// 再下面是一些辅助函数和与callback关系不大的函数,我就不列出来了,有兴趣的朋友可以自己看看
//
从以上代码我们可以很明白的看到,系统判断您的浏览器是否支持XMLHTTP或IFRAME,如果至少支持其中之一,则用相应的方法执行回调,否则当然就是提示错误了。回调的时候,采用post的方式,异步post到当前页面,然后等待回调结束,此时,由我们指定的返回数据处理script函数来处理返回的数据。
看到这里,我还不知道服务端怎么处理这个根据传过来的参数解析、执行,并返回数据的过程。但是,我们已经知道,WebForm_DoCallback(...)将会将当前页面的web控件的信息都post回去,这就意味着,我们在服务端有可能可以访问到这些web控件的value,这还不错,方便了我们处理当前数据。另一方面,eventArgument既然是一个任意格式的字符串参数,我们肯定要在服务段自己解析它的。
Serverside Callback Operation & Render
好了,那么接下来就让我们来看看在服务端,ASP.NET都为我们做了些什么。
首先,我们知道,当前的Page是必须实现ICallbackEventHandler这个接口的,也就是其包含的两个函数:string GetCallbackResult()和void RaiseCallbackEvent(eventArgument)。根据MSDN的文档,我们知道,在一个callback被post到服务端时,Page将会首先将post回来的form data绑定到当前页面的服务端web控件,接着判断本次post是callback还是postback,如果是postpost,那么自然是原来的那个机制;
如果是callback,则将回调用触发本次callback的控件(在本例中,我们在激发这个callback时,第一个参数指定的是this也就是当前的Page,那么这里当前的Page就是这个触发控件)的RaiseCallbackEvent(eventArgument),当然,eventArgument也将会正确的传过来,在这个函数的实现代码里我们可以对这个参数进行解析处理,并在某个地方,存储我们准备返回的数据,或者待处理的已经被解析出来的参数;
接着,系统将调用string GetCallbackResult(),在这个函数的实现代码中,我们可以直接返回我们在RaiseCallback函数中存储的准备返回的数据,或者根据待处理的已经被解析出来的参数处理这些参数,并返回结果。这个返回的字符串,自然将以脚本的形式被render回客户端。被返回的脚本细节如下(反编译Page.RenderCallback()的源码), 我们可以看到,返回的结果除了我们需要的结果数据和相应的脚本,没有多余的数据,因此,callback的执行效率应该说还是不错的:
{
bool flag1 = !string.IsNullOrEmpty(this._requestValueCollection["__CALLBACKLOADSCRIPT"]);
try
{
string text1 = null;
if (flag1)
{
text1 = this._requestValueCollection["__CALLBACKINDEX"];
if (string.IsNullOrEmpty(text1))
{
throw new HttpException(SR.GetString("Page_CallBackInvalid"));
}
for (int num1 = 0; num1 < text1.Length; num1++)
{
if (!char.IsDigit(text1, num1))
{
throw new HttpException(SR.GetString("Page_CallBackInvalid"));
}
}
this.Response.Write("<script>parent.__pendingCallbacks[");
this.Response.Write(text1);
this.Response.Write("].xmlRequest.responseText=/"");
}
if (this._callbackControl != null)
{
string text2 = this._callbackControl.GetCallbackResult();
if (this.EnableEventValidation)
{
string text3 = this.ClientScript.GetEventValidationFieldValue();
this.Response.Write(text3.Length.ToString(CultureInfo.InvariantCulture));
this.Response.Write('|');
this.Response.Write(text3);
}
else
{
this.Response.Write('s');
}
this.Response.Write(flag1 ? Util.QuoteJScriptString(text2) : text2);
}
if (flag1)
{
this.Response.Write("/";parent.__pendingCallbacks[");
this.Response.Write(text1);
this.Response.Write("].xmlRequest.readyState=4;parent.WebForm_CallbackComplete();</script>");
}
}
catch (Exception exception1)
{
this.Response.Clear();
this.Response.Write('e');
if (this.Context.IsCustomErrorEnabled)
{
this.Response.Write(SR.GetString("Page_CallBackError"));
return;
}
this.Response.Write(flag1 ? Util.QuoteJScriptString(HttpUtility.HtmlEncode(exception1.Message)) : HttpUtility.HtmlEncode(exception1.Message));
}
}
另外,才发现原来System.Web.UI.Utils这个类中有那么多有用的方便的函数如QuateJScriptString(),以前一直自己手写这样功能的函数呢~~真傻呀~~
Conclusion
至此,我们已经基本上清楚明白callback的前台幕后了。如果您对服务段的处理过程的细节还觉得不够,您也可以自行反编译Page对象,看看其实现代码的细节,还是很有意思的。
总体而言,我们发现,callback无论是兼容性(XMLHTTP或IFRAME我想大多数浏览器都支持吧),还是性能(没有返回不需要的数据),还是使用的便利性(因为ASP.NET帮我们绑定了页面上的当前的Web控件的数据,这就意味着我们可以在callback后的服务端,象postback时一样来写代码,也方便我们移植原来的postback的代码到callback方式的代码)都是非常优秀的。我们也完全可以扩展现有的控件,或者写我们自己的控件以支持这样的callback效果,并且,混合使用callback控件和原来的postback方式的控件也是非常可靠和容易的。这对我们升级原来的基于postback为主的代码,是非常有利的,如果用Atlas来做同样的代码升级和与postback方式的控件混合使用,我可以跟您说,会有很多问题。不信你自己可以试试~~
posted on 2005-11-28 16:17 Teddy's Knowledge Base 阅读(4863) 评论(23) 编辑 收藏 引用 收藏至365Key 所属分类: Ajax 、Web Dev.
# re: 深度解析Asp.Net2.0中的Callback机制 2005-11-28 21:01 syl5005
很好,细细研究 一下 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2005-11-28 22:48 baal
兄弟,你主要是做什么的?感觉你好厉害 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2005-11-28 22:54 Kozen
请问Teddy:“回调参数包括了原来页面上的formpostdata”具体包含哪些数据?
既然是Callback到Page,怎么又会有Post的数据?
谢谢! 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2005-11-29 08:17 Teddy's Knowledge Base
@kozen:
实际上,不管是XMLHTTP还是IFRAME方式的callback,在后台,客户端脚本都要和服务器基于http取得通讯。这样得通讯一般就是两种方式:get和post。如果,用get,就相当于在后台通过一个url访问一个页面,参数也是带在url上发送的,我们知道,这样一方面不是很安全,一方面可能会有url长度限制。所以,一般来讲,都会通过post的方式来和服务端通讯。对服务器来讲,也就相当于你有一个web页面上的form,通过method设为get还是post来submit。
对callback来说,它也是通过这样的方式post的,只是post时,除了包含页面上所有的有name属性的field(也就是通常,我们post时能在服务端取得数据的那些Web控件)之外,还会附加一些和callback相关的参数。所以本质上,特是一种特殊的postback。
当然,实际上,用get方式也是可以实现相当的ajax效果的,只是略有限制而已。例如,在我的ajaxhelper的sharepoint示例中(http://teddyma.cnblogs.com/archive/2005/06/19/176859.html),因为一般情况下,sharepoint是不允许在webpart中获得postback数据的,只能获得从url传过来的querystring参数,所以,在那个ajax范例中,我就是通过get来实现,但是使用接口和postback是没区别的。 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2005-11-29 21:26 Kozen
@Teddy:
谢谢你的释疑。
但我还有个疑问,你在文章最后提及的“因为ASP.NET帮我们绑定了页面上的当前的Web控件的数据,这就意味着我们可以在callback后的服务端,象postback时一样来写代码,也方便我们移植原来的postback的代码到callback方式的代码”。
是不是可以这样理解:假设有个简单的需求,页面上有两个联动的下拉选择框,一个是选取国家,一个选取城市,当国家的下拉框改变时通过Callback的方式在服务端获取相关城市的列表来填充城市下拉框,最后重绘城市下拉框控件到客户端,以实现局部刷新。那么在服务端具体处理Callback请求以填充城市信息时,是否能够通过国家下拉框控件对象的当前值属性直接获得所选国家的信息呢?Postback时的确可以获得,因为整个页面都被提交,国家下拉框控件的属性已被界面上的值更新,而Callback时如果我不把当前选中国家的信息作为回调参数发回服务端,我能够像Postback时那样方便的直接访问国家下拉框的当前值属性吗?(我试过,不行,属性仍处于提交前的状态)
若不能的话,有无其它好的建议,请告知。先谢了! 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2005-11-30 08:12 Teddy's Knowledge Base
@Kozen:
callback后被绑定到控件变量的数据实际上是上次render时的最后数据,包括上次postback时如果有改变,它是会帮你自动绑定。本次callback时,如果像你说的修改了某个select的选项,必须通过Request.Form["name_of_select"]来访问。比如你的select的name如果是selectCity,那么就通过Request.Form["selectCity"]来访问。一个web控件的name和他的初始ID可能会不同,特别是当它是其他一个usercontrol或者自定义webcontrol的子控件的时候,此时一般会被加上前缀,所以此时的name属性值你要查看运行时的web页面的html源码。 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2005-11-30 17:38 Kozen
@Teddy:
通过Request.Form[xxx]的方法的确可以访问到部分信息,但还有很多控件的属性(如CheckBox.Checked等)是无法这样获取的,看来是没法象Postback的服务端处理方式那样方便的写代码了。
谢谢你的答复,欢迎继续指点。:) 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2005-11-30 19:08 Teddy's Knowledge Base
实际上,checkbox也是一个input,它的知当然是能够通过Request.Form访问到的。实际上,只要一个控件本质上是input,select,area等这些能像后台提交数据的控件,当然她必须要有name属性,这些质都能在服务端访问到。但是,的确,如果是保存在viewstate中的数据,此时不容易访问到。 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2005-11-30 22:25 Kozen
我做了测试,Postback时可以通过Request.Form访问到Checkbox,而Callback时访问不到Checkbox,即触发页面回发时Request.Form["checkBox1"]为null。
补充:Callback后在服务端获取页面完整Post数据的问题解决啦!关键在于需要在回发之前调用WebForm_InitCallback(); 重新搜集页面所有输入域的更新值!:) 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2005-12-14 15:15 悠悠我心
各位都是强人啊!
回复
# re: 深度解析Asp.Net2.0中的Callback机制 2005-12-23 12:34 swift
问一个问题:
你的例子中,实现回调接口用的是this所代表的页面。一直搞不明白,在ASP.NET中,这个页面对象的生命周期是多久呢?
回调回来之后,this是原来生成页面的那个实例,还是重新创建生成的一个实例呢?
与POSTBACK回来一样,是新实例吗?
如果是新实例,this不是显得很不达意了? 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2006-01-27 18:20 tigertang
我怎么用flashget下载不下来WebResource.axd文件?
你是怎么下载的?
谢谢! 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2006-04-15 23:18 梁广永
学习, 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2006-07-06 15:47 学习.NET
=================
Kozen
我做了测试,Postback时可以通过Request.Form访问到Checkbox,而Callback时访问不到Checkbox,即触发页面回发时Request.Form["checkBox1"]为null。
补充:Callback后在服务端获取页面完整Post数据的问题解决啦!关键在于需要在回发之前调用WebForm_InitCallback(); 重新搜集页面所有输入域的更新值!:)
==================
能不能具体解释一下,如何“Callback后在服务端获取页面完整Post数据”,怎么调用WebForm_InitCallback();
我试了一下,不知WebForm_InitCallback(); 应该放到什么地方,能否具体讲解一下,谢谢。 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2006-07-06 15:54 Teddy's Knowledge Base
@学习.NET
可以试试象下面这样执行回调,就是在WebForm_DoCallback之前保证执行WebForm_InitCallback():
<script language="javascript" type="text/javascript">
function any_script_function()
{
WebForm_InitCallback();
WebForm_DoCallback('__Page',arg,ReceiveServerData,context,null,false);
}
</script>
回复
# re: 深度解析Asp.Net2.0中的Callback机制 2006-07-06 16:59 学习.NET
搞定了,WebForm_InitCallback(); 之前要将__theFormPostData和__theFormPostCollection清零。
谢谢。 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2006-07-06 17:19 学习.NET
Teddy's Knowledge Base回复的真快。
这样即可:
<script language="javascript" type="text/javascript">
function any_script_function()
{
// 没有这两行竟然不行。等我有时间时看看原因
__theFormPostData = “”;
__theFormPostCollection = new Array(); WebForm_InitCallback();
WebForm_InitCallback();
WebForm_DoCallback('__Page',arg,ReceiveServerData,context,null,false);
}
</script>
回复
# re: 深度解析Asp.Net2.0中的Callback机制 2006-07-06 17:22 学习.NET
我原先就是这样调用:
WebForm_InitCallback();
WebForm_DoCallback('__Page',arg,ReceiveServerData,context,null,false);
死活都不行,加了这两句:
__theFormPostData = “”;
__theFormPostCollection = new Array();
就行了。 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2006-07-21 11:29 Ada
客户端代码是这样的
<script type="text/javascript">
function ReceiveServerData(result, context)
{
}
</script>
为什么解析后是这样
<script language="javascript" type="text/javascript">
function any_script_function()
{
WebForm_DoCallback('__Page',arg,ReceiveServerData,context,null,false);
}
</script>
函数any_script_function的参数为什么没有了呢?我试了一下,应该是有的啊?
回复
# re: 深度解析Asp.Net2.0中的Callback机制 2006-07-21 11:32 Ada
不好意思第一个函数名写错了,应该是any_script_function 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2006-07-21 11:37 Teddy's Knowledge Base
@Ada
你说得没错,生成的代码也应该是有参数的,文中的代码中漏掉了。 回复
# re: 深度解析Asp.Net2.0中的Callback机制 2006-08-10 20:30 netmole
@学习.NET
WebForm_InitCallback(); 的作用就是加载网页的状态post数据,而往__theFormPostData加载的方式是以在现有post字符后追加的方式添加的具体看源代码:
function WebForm_InitCallbackAddField(name, value)
{
var nameValue = new Object();
nameValue.name = name;
nameValue.value = value;
__theFormPostCollection[__theFormPostCollection.length] = nameValue;
__theFormPostData += name + "=" + WebForm_EncodeCallback(value) + "&";}
那么
WebForm_InitCallback(); 前必须加
__theFormPostData = "";
__theFormPostCollection = new Array();
是因为在网页在浏览器加载后发生了WebForm_InitCallback();调用,为__theFormPostData ,__theFormPostCollection填充了当前页初始状态的post数据,如果你在回调前不加这两句的话 __theFormPostData就会在原来的基础上再添加现有的网页状态post数据,这样可以说现在回调时你已包含了两份post数据,但回调时,服务器方仍然把它当作一份看待,结果服务器分析不出post数据,只给你返回了一个网页状态已损坏的消息具体如下:
response
"<html>
<head>
<title>此页的状态信息无效,可能已损坏。</title>
<style>
回复
评论
期待下一篇和你的MVC框架
alex