深度解析Asp.Net2.0中的Callback机制

看到不少朋友最近在写使用callback的文章,也有点手痒,也来涂鸦一下,挖掘挖掘callback的潜力。callback的一般使用方法还算简单,直接参照msdn的帮助和范例就足够了。但是想要真正用好、用精,或者想开发一些基于callback机制的WEB组件,那么,就要先深入了解callback的实现机制了。在本文中,Teddy将和您一起解析callback的整个调用、反馈机制,相信对于帮助您更好的使用callback,将能有一定的益处。

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,首先,当然需要在客户端本中发出一个调用。一个典型的调用语法如下:

ExpandedBlockStart.gif ContractedBlock.gif < script  language ="javascript"  type ="text/javascript" > dot.gif
InBlock.gif
function any_script_function(arg, context)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif    
<%= ClientScript.GetCallbackEventReference(this"arg""ReceiveServerData""context")%>;    
ExpandedBlockEnd.gif}

None.gif
</ script >

ClientScript.GetCallbackEventReference(...)将根据传入的参数返回实际的回调脚本。这个函数有多个重载版本,因此,这些参数的含义,大家可以参考MSDN。以具体的上面这段示例代码中的参数来说:

- this表示执行回调的的服务端控件是当前这个Page,当前的Page必须实现ICallbackEventHandler接口,包括必须实现string GetCallbackResult()和void RaiseCallbackEvent(eventArgument)这两个接口函数,这个参数也可以是指向某个WEB控件的引用,当然,这个空间也必须实现ICallbackEventHandler接口;

- "arg"是将被传给RaiseCallbackEvent的参数eventArgument的值,可以使人以自定义格式的字符串;

- "ReceiveServerData"是当回调成功之后,处理返回内容的客户端脚本函数的名称,这个函数必须存在于执行回调的页面,并且这个函数可以包含两个参数,例如:

ExpandedBlockStart.gif ContractedBlock.gif < script  type ="text/javascript" > dot.gif  
InBlock.gif    
function ReceiveServerData(result, context) 
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif
InBlock.gif        dot.gif 
ExpandedBlockEnd.gif    }
 
None.gif
</ script >

这两个参数,分别是回调的返回数据result,和原封不动被返回的我们激发回调时的这个context参数,当然,这两个参数都是字符串类型的。

- "context"就不用多解释了,记得这个参数会被原封不动的传给指定的返回数据处理函数就行了。MSDN的官方文档说,context一般可用来传递需要在客户端的返回数据处理函数中用来调用的脚本代码,不过实际上,你传什么都可以,把它看成一种从客户端回调的的激发端,到处理返回数据的接收段之间的参数传递通道就行了。

2、WebForm_DoCallback(...)

Ok,明白了以上代码的含义,下面我们来看看,前面的这条“<%= ClientScript.GetCallbackEventReference(this, "arg", "ReceiveServerData", "context")%>;”在运行时会被解析成什么样子呢?我们只要在页面运行时察看页面源码就可以看到,实际上服务器帮我们生成了下面这段script代码:

ExpandedBlockStart.gif ContractedBlock.gif < script  language ="javascript"  type ="text/javascript" > dot.gif
InBlock.gif
function any_script_function()
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif    WebForm_DoCallback('__Page',arg,ReceiveServerData,context,
null,false);    
ExpandedBlockEnd.gif}

None.gif
</ script >

这段代码是什么意思呢?很显然的他调用了一个系统与定义的script函数:WebForm_DoCallback。我们要把这个函数找出来看看它具体为我们干了什么。在运行时的页面源码中,我们很容易可以找到这段脚本的出处。我们注意到有一个script,src="/TestCallbackWeb/WebResource.axd?d=HEcYmh-7_szSIu1D_mHSEw2&amp;t=632661779991718750",这里就定义了WebForm_DoCallback。让我们把它用flashget下载下来,将扩展名改为.js。看看源码吧,没有被混淆的,所以很容易看明白。我这里就只把和callback相关的部分贴出来解释一下,见代码中的注释:

None.gif // 用于存放所有未完成的callback对象的数组__pendingCallbacks
None.gif
var  __pendingCallbacks  =   new  Array();
None.gif
var  __synchronousCallBackIndex  =   - 1 ;
None.gif
None.gif
// 回调主函数WebForm_DoCallback
ExpandedBlockStart.gifContractedBlock.gif
function  WebForm_DoCallback(eventTarget, eventArgument, eventCallback, context, errorCallback, useAsync)  dot.gif {
InBlock.gif
InBlock.gif    
//构造回调参数,回调参数包括了原来页面上的formpostdata和我们传递的目标控件、eventArgument和部分验证信息
InBlock.gif
    var postData = __theFormPostData +
InBlock.gif                
"__CALLBACKID=" + WebForm_EncodeCallback(eventTarget) +
InBlock.gif                
"&__CALLBACKPARAM=" + WebForm_EncodeCallback(eventArgument);
ExpandedSubBlockStart.gifContractedSubBlock.gif    
if (theForm["__EVENTVALIDATION"]) dot.gif{
InBlock.gif        postData 
+= "&__EVENTVALIDATION=" + WebForm_EncodeCallback(theForm["__EVENTVALIDATION"].value);
ExpandedSubBlockEnd.gif    }

InBlock.gif   
InBlock.gif   
//下面实例化XMLHTTP对象,如果浏览器支持XMLHTTP则直接用XMLHTTP执行异步回调
InBlock.gif
    var xmlRequest,e;
ExpandedSubBlockStart.gifContractedSubBlock.gif    
try dot.gif{
InBlock.gif        xmlRequest 
= new XMLHttpRequest();
ExpandedSubBlockEnd.gif    }

ExpandedSubBlockStart.gifContractedSubBlock.gif    
catch(e) dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
try dot.gif{
InBlock.gif            xmlRequest 
= new ActiveXObject("Microsoft.XMLHTTP");
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
catch(e) dot.gif{
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif    
var setRequestHeaderMethodExists = true;
ExpandedSubBlockStart.gifContractedSubBlock.gif    
try dot.gif{
InBlock.gif        setRequestHeaderMethodExists 
= (xmlRequest && xmlRequest.setRequestHeader);
ExpandedSubBlockEnd.gif    }

ExpandedSubBlockStart.gifContractedSubBlock.gif    
catch(e) dot.gif{}
InBlock.gif    
var callback = new Object();
InBlock.gif    callback.eventCallback 
= eventCallback;
InBlock.gif    callback.context 
= context;
InBlock.gif    callback.errorCallback 
= errorCallback;
InBlock.gif    callback.async 
= useAsync;
InBlock.gif    
InBlock.gif   
//获取对应的回调对象 
InBlock.gif
    var callbackIndex = WebForm_FillFirstAvailableSlot(__pendingCallbacks, callback);
ExpandedSubBlockStart.gifContractedSubBlock.gif    
if (!useAsync) dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
if (__synchronousCallBackIndex != -1dot.gif{
InBlock.gif            __pendingCallbacks[__synchronousCallBackIndex] 
= null;
ExpandedSubBlockEnd.gif        }

InBlock.gif        __synchronousCallBackIndex 
= callbackIndex;
ExpandedSubBlockEnd.gif    }

ExpandedSubBlockStart.gifContractedSubBlock.gif    
if (setRequestHeaderMethodExists) dot.gif{
InBlock.gif        xmlRequest.onreadystatechange 
= WebForm_CallbackComplete;
InBlock.gif        callback.xmlRequest 
= xmlRequest;
InBlock.gif        xmlRequest.open(
"POST", theForm.action, true);
InBlock.gif        xmlRequest.setRequestHeader(
"Content-Type""application/x-www-form-urlencoded");
InBlock.gif        xmlRequest.send(postData);
InBlock.gif        
return;
ExpandedSubBlockEnd.gif    }

InBlock.gif   
InBlock.gif   
//万一浏览器不支持XMLHTTP的话,我们IFRAME方案代替,在一个隐藏的IFRAME中执行Postback   
InBlock.gif
    callback.xmlRequest = new Object();
InBlock.gif    
var callbackFrameID = "__CALLBACKFRAME" + callbackIndex;
InBlock.gif    
var xmlRequestFrame = document.frames[callbackFrameID];
ExpandedSubBlockStart.gifContractedSubBlock.gif    
if (!xmlRequestFrame) dot.gif{
InBlock.gif        xmlRequestFrame 
= document.createElement("IFRAME");
InBlock.gif        xmlRequestFrame.width 
= "1";
InBlock.gif        xmlRequestFrame.height 
= "1";
InBlock.gif        xmlRequestFrame.frameBorder 
= "0";
InBlock.gif        xmlRequestFrame.id 
= callbackFrameID;
InBlock.gif        xmlRequestFrame.name 
= callbackFrameID;
InBlock.gif        xmlRequestFrame.style.position 
= "absolute";
InBlock.gif        xmlRequestFrame.style.top 
= "-100px"
InBlock.gif        xmlRequestFrame.style.left 
= "-100px";
ExpandedSubBlockStart.gifContractedSubBlock.gif        
try dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
if (callBackFrameUrl) dot.gif{
InBlock.gif                xmlRequestFrame.src 
= callBackFrameUrl;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
catch(e) dot.gif{}
InBlock.gif        document.body.appendChild(xmlRequestFrame);
ExpandedSubBlockEnd.gif    }

ExpandedSubBlockStart.gifContractedSubBlock.gif    
var interval = window.setInterval(function() dot.gif{
InBlock.gif        xmlRequestFrame 
= document.frames[callbackFrameID];
ExpandedSubBlockStart.gifContractedSubBlock.gif        
if (xmlRequestFrame && xmlRequestFrame.document) dot.gif{
InBlock.gif            window.clearInterval(interval);
InBlock.gif            xmlRequestFrame.document.write(
"");
InBlock.gif            xmlRequestFrame.document.close();
InBlock.gif            xmlRequestFrame.document.write('
<html><body><form method="post"><input type="hidden" name="__CALLBACKLOADSCRIPT" value="t"></form></body></html>');
InBlock.gif            xmlRequestFrame.document.close();
InBlock.gif            xmlRequestFrame.document.forms[
0].action = theForm.action;
InBlock.gif            
var count = __theFormPostCollection.length;
InBlock.gif            
var element;
ExpandedSubBlockStart.gifContractedSubBlock.gif            
for (var i = 0; i < count; i++dot.gif{
InBlock.gif                element 
= __theFormPostCollection[i];
ExpandedSubBlockStart.gifContractedSubBlock.gif                
if (element) dot.gif{
InBlock.gif                    
var fieldElement = xmlRequestFrame.document.createElement("INPUT");
InBlock.gif                    fieldElement.type 
= "hidden";
InBlock.gif                    fieldElement.name 
= element.name;
InBlock.gif                    fieldElement.value 
= element.value;
InBlock.gif                    xmlRequestFrame.document.forms[
0].appendChild(fieldElement);
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

InBlock.gif            
var callbackIdFieldElement = xmlRequestFrame.document.createElement("INPUT");
InBlock.gif            callbackIdFieldElement.type 
= "hidden";
InBlock.gif            callbackIdFieldElement.name 
= "__CALLBACKID";
InBlock.gif            callbackIdFieldElement.value 
= eventTarget;
InBlock.gif            xmlRequestFrame.document.forms[
0].appendChild(callbackIdFieldElement);
InBlock.gif            
var callbackParamFieldElement = xmlRequestFrame.document.createElement("INPUT");
InBlock.gif            callbackParamFieldElement.type 
= "hidden";
InBlock.gif            callbackParamFieldElement.name 
= "__CALLBACKPARAM";
InBlock.gif            callbackParamFieldElement.value 
= eventArgument;
InBlock.gif            xmlRequestFrame.document.forms[
0].appendChild(callbackParamFieldElement);
ExpandedSubBlockStart.gifContractedSubBlock.gif            
if (theForm["__EVENTVALIDATION"]) dot.gif{
InBlock.gif                
var callbackValidationFieldElement = xmlRequestFrame.document.createElement("INPUT");
InBlock.gif                callbackValidationFieldElement.type 
= "hidden";
InBlock.gif                callbackValidationFieldElement.name 
= "__EVENTVALIDATION";
InBlock.gif                callbackValidationFieldElement.value 
= theForm["__EVENTVALIDATION"].value;
InBlock.gif                xmlRequestFrame.document.forms[
0].appendChild(callbackValidationFieldElement);
ExpandedSubBlockEnd.gif            }

InBlock.gif            
var callbackIndexFieldElement = xmlRequestFrame.document.createElement("INPUT");
InBlock.gif            callbackIndexFieldElement.type 
= "hidden";
InBlock.gif            callbackIndexFieldElement.name 
= "__CALLBACKINDEX";
InBlock.gif            callbackIndexFieldElement.value 
= callbackIndex;
InBlock.gif            xmlRequestFrame.document.forms[
0].appendChild(callbackIndexFieldElement);
InBlock.gif            xmlRequestFrame.document.forms[
0].submit();
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }
10);
ExpandedBlockEnd.gif}

None.gif
None.gif
// 该函数在每次回调结束后会调用来检查当前的回调列表中的回调的执行情况,如果,执行完毕的,则从列表中删除回调对象,并删除临时建立的IFRAME
ExpandedBlockStart.gifContractedBlock.gif
function  WebForm_CallbackComplete()  dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif    
for (i = 0; i < __pendingCallbacks.length; i++dot.gif{
InBlock.gif        callbackObject 
= __pendingCallbacks[i];
ExpandedSubBlockStart.gifContractedSubBlock.gif        
if (callbackObject && callbackObject.xmlRequest && (callbackObject.xmlRequest.readyState == 4)) dot.gif{
InBlock.gif            WebForm_ExecuteCallback(callbackObject);
ExpandedSubBlockStart.gifContractedSubBlock.gif            
if (!__pendingCallbacks[i].async) dot.gif{
InBlock.gif                __synchronousCallBackIndex 
= -1;
ExpandedSubBlockEnd.gif            }

InBlock.gif            __pendingCallbacks[i] 
= null;
InBlock.gif            
var callbackFrameID = "__CALLBACKFRAME" + i;
InBlock.gif            
var xmlRequestFrame = document.getElementById(callbackFrameID);
ExpandedSubBlockStart.gifContractedSubBlock.gif            
if (xmlRequestFrame) dot.gif{
InBlock.gif                xmlRequestFrame.parentNode.removeChild(xmlRequestFrame);
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
// 该函数执行我们在回调激发端指定的处理返回数据的script函数,如我们上面范例代码中的ReceiveServerData函数
ExpandedBlockStart.gifContractedBlock.gif
function  WebForm_ExecuteCallback(callbackObject)  dot.gif {
InBlock.gif    
var response = callbackObject.xmlRequest.responseText;
ExpandedSubBlockStart.gifContractedSubBlock.gif    
if (response.charAt(0== "s"dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
if ((typeof(callbackObject.eventCallback) != "undefined"&& (callbackObject.eventCallback != null)) dot.gif{
InBlock.gif            callbackObject.eventCallback(response.substring(
1), callbackObject.context);
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedSubBlockStart.gifContractedSubBlock.gif    
else if (response.charAt(0== "e"dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
if ((typeof(callbackObject.errorCallback) != "undefined"&& (callbackObject.errorCallback != null)) dot.gif{
InBlock.gif            callbackObject.errorCallback(response.substring(
1), callbackObject.context);
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedSubBlockStart.gifContractedSubBlock.gif    
else dot.gif{
InBlock.gif        
var separatorIndex = response.indexOf("|");
ExpandedSubBlockStart.gifContractedSubBlock.gif        
if (separatorIndex != -1dot.gif{
InBlock.gif            
var validationFieldLength = parseInt(response.substring(0, separatorIndex));
ExpandedSubBlockStart.gifContractedSubBlock.gif            
if (!isNaN(validationFieldLength)) dot.gif{
InBlock.gif                
var validationField = response.substring(separatorIndex + 1, separatorIndex + validationFieldLength + 1);
ExpandedSubBlockStart.gifContractedSubBlock.gif                
if (validationField != ""dot.gif{
InBlock.gif                    
var validationFieldElement = theForm["__EVENTVALIDATION"];
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
if (!validationFieldElement) dot.gif{
InBlock.gif                        validationFieldElement 
= document.createElement("INPUT");
InBlock.gif                        validationFieldElement.type 
= "hidden";
InBlock.gif                        validationFieldElement.name 
= "__EVENTVALIDATION";
InBlock.gif                        theForm.appendChild(validationFieldElement);
ExpandedSubBlockEnd.gif                    }

InBlock.gif                    validationFieldElement.value 
= validationField;
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockStart.gifContractedSubBlock.gif                
if ((typeof(callbackObject.eventCallback) != "undefined"&& (callbackObject.eventCallback != null)) dot.gif{
InBlock.gif                    callbackObject.eventCallback(response.substring(separatorIndex 
+ validationFieldLength + 1), callbackObject.context);
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
// 获取对应的回调对象 
ExpandedBlockStart.gifContractedBlock.gif
function  WebForm_FillFirstAvailableSlot(array, element)  dot.gif {
InBlock.gif    
var i;
ExpandedSubBlockStart.gifContractedSubBlock.gif    
for (i = 0; i < array.length; i++dot.gif{
InBlock.gif        
if (!array[i]) break;
ExpandedSubBlockEnd.gif    }

InBlock.gif    array[i] 
= element;
InBlock.gif    
return i;
ExpandedBlockEnd.gif}

None.gif
None.gif
// 再下面是一些辅助函数和与callback关系不大的函数,我就不列出来了,有兴趣的朋友可以自己看看
None.gif//
dot.gif

从以上代码我们可以很明白的看到,系统判断您的浏览器是否支持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的执行效率应该说还是不错的

None.gif private   void  RenderCallback()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      
bool flag1 = !string.IsNullOrEmpty(this._requestValueCollection["__CALLBACKLOADSCRIPT"]);
InBlock.gif      
try
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
string text1 = null;
InBlock.gif            
if (flag1)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  text1 
= this._requestValueCollection["__CALLBACKINDEX"];
InBlock.gif                  
if (string.IsNullOrEmpty(text1))
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{
InBlock.gif                        
throw new HttpException(SR.GetString("Page_CallBackInvalid"));
ExpandedSubBlockEnd.gif                  }

InBlock.gif                  
for (int num1 = 0; num1 < text1.Length; num1++)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{
InBlock.gif                        
if (!char.IsDigit(text1, num1))
ExpandedSubBlockStart.gifContractedSubBlock.gif                        
dot.gif{
InBlock.gif                              
throw new HttpException(SR.GetString("Page_CallBackInvalid"));
ExpandedSubBlockEnd.gif                        }

ExpandedSubBlockEnd.gif                  }

InBlock.gif                  
this.Response.Write("<script>parent.__pendingCallbacks[");
InBlock.gif                  
this.Response.Write(text1);
InBlock.gif                  
this.Response.Write("].xmlRequest.responseText=\"");
ExpandedSubBlockEnd.gif
            }

InBlock.gif            
if (this._callbackControl != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  
string text2 = this._callbackControl.GetCallbackResult();
InBlock.gif                  
if (this.EnableEventValidation)
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{
InBlock.gif                        
string text3 = this.ClientScript.GetEventValidationFieldValue();
InBlock.gif                        
this.Response.Write(text3.Length.ToString(CultureInfo.InvariantCulture));
InBlock.gif                        
this.Response.Write('|');
InBlock.gif                        
this.Response.Write(text3);
ExpandedSubBlockEnd.gif                  }

InBlock.gif                  
else
ExpandedSubBlockStart.gifContractedSubBlock.gif                  
dot.gif{
InBlock.gif                        
this.Response.Write('s');
ExpandedSubBlockEnd.gif                  }

InBlock.gif                  
this.Response.Write(flag1 ? Util.QuoteJScriptString(text2) : text2);
ExpandedSubBlockEnd.gif            }

InBlock.gif            
if (flag1)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  
this.Response.Write("\";parent.__pendingCallbacks[");
InBlock.gif
                  this.Response.Write(text1);
InBlock.gif                  
this.Response.Write("].xmlRequest.readyState=4;parent.WebForm_CallbackComplete();</script>");
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif      }

InBlock.gif      
catch (Exception exception1)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
this.Response.Clear();
InBlock.gif            
this.Response.Write('e');
InBlock.gif            
if (this.Context.IsCustomErrorEnabled)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  
this.Response.Write(SR.GetString("Page_CallBackError"));
InBlock.gif                  
return;
ExpandedSubBlockEnd.gif            }

InBlock.gif            
this.Response.Write(flag1 ? Util.QuoteJScriptString(HttpUtility.HtmlEncode(exception1.Message)) : HttpUtility.HtmlEncode(exception1.Message));
ExpandedSubBlockEnd.gif      }

ExpandedBlockEnd.gif}

另外,才发现原来System.Web.UI.Utils这个类中有那么多有用的方便的函数如QuateJScriptString(),以前一直自己手写这样功能的函数呢~~真傻呀~~

Conclusion

至此,我们已经基本上清楚明白callback的前台幕后了。如果您对服务段的处理过程的细节还觉得不够,您也可以自行反编译Page对象,看看其实现代码的细节,还是很有意思的。

总体而言,我们发现,callback无论是兼容性(XMLHTTP或IFRAME我想大多数浏览器都支持吧),还是性能(没有返回不需要的数据),还是使用的便利性(因为ASP.NET帮我们绑定了页面上的当前的Web控件的数据,这就意味着我们可以在callback后的服务端,象postback时一样来写代码,也方便我们移植原来的postback的代码到callback方式的代码)都是非常优秀的。我们也完全可以扩展现有的控件,或者写我们自己的控件以支持这样的callback效果,并且,混合使用callback控件和原来的postback方式的控件也是非常可靠和容易的。这对我们升级原来的基于postback为主的代码,是非常有利的,如果用Atlas来做同样的代码升级和与postback方式的控件混合使用,我可以跟您说,会有很多问题。不信你自己可以试试~~

转载于:https://www.cnblogs.com/chinahkk/archive/2005/12/13/296481.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值