3. ASP.NET AJAX部分呈现剖析
3.1 先从客户端讲起
看一下上面的示例代码在客户端的HTML代码, 这里只列出核心部分,其他全部隐去。
<script type="text/javascript">
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('ScriptManager1', document.getElementById('form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tUpdatePanel1'], [], [], 90);
//]]>
</script>
<div id="UpdatePanel1">
7/25/2008 4:54:36 PM
<br />
<input type="submit" name="Button1" value="Button" id="Button1" />
</div>
看一下上面的两句JavaScript代码,第一句代码中的_initialize 方法是客户端PageRequestManager对象上的静态方法,它会创建一个 PageRequestManager 类的全局实例,并将其初始化。在这个初始化函数中,ageRequestManager对象注册了当前表单对象的submit事件,以及window对象的load和unload事件。
而第二句代码则是通过PageRequestManager的getInstance方法来检索其唯一实例, 得到该实例后调用_updateControls方法来注册UpdatePanel以及其Trigger控件。
我们可以从MicrosoftAjaxWebForm.js文件中得到_updateControls方法的声明:
function Sys$WebForms$PageRequestManager$_updateControls(updatePanelIDs, asyncPostBackControlIDs, postBackControlIDs, asyncPostBackTimeout) {}
由其中第一个参数代表了当前页面上所有的UpdatePanel控件的ID集合,如果该UpdatePanel的ChildrenAsTrigger为True的话,应在ID前添加字符't',否则添加字符'f';而第二个参数是所有引发异步回送的控件ID;第三个参数是所有引发同步回送的控件ID;第四个参数设定了异步回送的Timeout时间,单位为秒。于PageRequestManager对象注册了当前表单的submit时间,所以每当页面有提交动作的时候,PageRequestManager对象就会介入,看一下PageRequestManager对象页面提交处理函数_onFormSubmit(evt)。
如果需要执行一次异步回送的话,会中止原有的普通浏览器会回发,代之使用XMLHttpRequest进行AJAX回发。在封装这个请求的时候,当前页面的所有字段以及视图状态都会被打包在请求中,另外还设置了这次Request的HTTP头:request.get_headers()['X-MicrosoftAjax'] = 'Delta=true';
在服务器端将会根据这个HTTP头标记来判定是否为一次AJAX异步回发。
_onFormSubmit(evt)函数代码:
function Sys$WebForms$PageRequestManager$_onFormSubmit(evt) {
var continueSubmit = true;
var isCrossPost = this._isCrossPost;
this._isCrossPost = false;
if (this._onsubmit) {
continueSubmit = this._onsubmit();
}
if (continueSubmit) {
for (var i = 0; i < this._onSubmitStatements.length; i++) {
if (!this._onSubmitStatements[i]()) {
continueSubmit = false;
break;
}
}
}
if (!continueSubmit) {
if (evt) {
evt.preventDefault();
}
return;
}
var form = this._form;
if (isCrossPost) {
return;
}
if (this._activeDefaultButton && !this._activeDefaultButtonClicked) {
this._onFormElementActive(this._activeDefaultButton, 0, 0);
}
if (!this._postBackSettings.async) {
return;
}
var formBody = new Sys.StringBuilder();
formBody.append(encodeURIComponent(this._scriptManagerID) + '=' + encodeURIComponent(this._postBackSettings.panelID) + '&');
var count = form.elements.length;
for (var i = 0; i < count; i++) {
var element = form.elements[i];
var name = element.name;
if (typeof(name) === "undefined" || (name === null) || (name.length === 0)) {
continue;
}
var tagName = element.tagName;
if (tagName === 'INPUT') {
var type = element.type;
if ((type === 'text') ||
(type === 'password') ||
(type === 'hidden') ||
(((type === 'checkbox') || (type === 'radio')) && element.checked)) {
formBody.append(encodeURIComponent(name));
formBody.append('=');
formBody.append(encodeURIComponent(element.value));
formBody.append('&');
}
}
else if (tagName === 'SELECT') {
var optionCount = element.options.length;
for (var j = 0; j < optionCount; j++) {
var option = element.options[j];
if (option.selected) {
formBody.append(encodeURIComponent(name));
formBody.append('=');
formBody.append(encodeURIComponent(option.value));
formBody.append('&');
}
}
}
else if (tagName === 'TEXTAREA') {
formBody.append(encodeURIComponent(name));
formBody.append('=');
formBody.append(encodeURIComponent(element.value));
formBody.append('&');
}
}
if (this._additionalInput) {
formBody.append(this._additionalInput);
this._additionalInput = null;
}
var request = new Sys.Net.WebRequest();
var action = form.action;
if (Sys.Browser.agent === Sys.Browser.InternetExplorer) {
var queryIndex = action.indexOf('?');
if (queryIndex !== -1) {
var path = action.substr(0, queryIndex);
if (path.indexOf("%") === -1) {
action = encodeURI(path) + action.substr(queryIndex);
}
}
else if (action.indexOf("%") === -1) {
action = encodeURI(action);
}
}
request.set_url(action);
request.get_headers()['X-MicrosoftAjax'] = 'Delta=true';
request.get_headers()['Cache-Control'] = 'no-cache';
request.set_timeout(this._asyncPostBackTimeout);
request.add_completed(Function.createDelegate(this, this._onFormSubmitCompleted));
request.set_body(formBody.toString());
var handler = this._get_eventHandlerList().getHandler("initializeRequest");
if (handler) {
var eventArgs = new Sys.WebForms.InitializeRequestEventArgs(request, this._postBackSettings.sourceElement);
handler(this, eventArgs);
continueSubmit = !eventArgs.get_cancel();
}
if (!continueSubmit) {
if (evt) {
evt.preventDefault();
}
return;
}
this._scrollPosition = this._getScrollPosition();
this.abortPostBack();
handler = this._get_eventHandlerList().getHandler("beginRequest");
if (handler) {
var eventArgs = new Sys.WebForms.BeginRequestEventArgs(request, this._postBackSettings.sourceElement);
handler(this, eventArgs);
}
if (this._originalDoCallback) {
this._cancelPendingCallbacks();
}
this._request = request;
request.invoke();
if (evt) {
evt.preventDefault();
}
}
我们可以发现AJAX回发所提交的数据量其实跟普通回发过程中提交的数据量是一样的,并且还附加了一些额外信息。


