2、WebForm_DoCallback(...)
Ok,明白了以上代码的含义,下面我们来看看,前面的这条“<%= ClientScript.GetCallbackEventReference(this, "arg", "ReceiveServerData", "context")%>;”在运行时会被解析成什么样子呢?我们只要在页面运行时察看页面源码就可以看到,实际上服务器帮我们生成了下面这段 script代码:
<script language="javascript" type="text/javascript">
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相关的部分贴出来解释一下,见代码中的注释:
//用于存放所有未完成的callback对象的数组__pendingCallbacks
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既然是一个任意格式的字符串参数,我们肯定要在服务段自己解析它的。
