Asp.net Ajax ASP.NET 局部更新PostBack的客户端调用过程

<script type="text/javascript">
Sys.WebForms.PageRequestManager._initialize('ScriptManager1', document.getElementById('form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tUpdatePanel1'], [], [], 90);
</script>

看一下MicrosoftAjaxWebForms.js里Sys.WebForms.PageRequestManager._initialize的定义,

Sys.WebForms.PageRequestManager._initialize = function Sys$WebForms$PageRequestManager$_initialize(scriptManagerID, formElement) {
if (Sys.WebForms.PageRequestManager.getInstance()) {
throw Error.invalidOperation(Sys.WebForms.Res.PRM_CannotRegisterTwice);
}
Sys.WebForms.PageRequestManager._instance = new Sys.WebForms.PageRequestManager();
Sys.WebForms.PageRequestManager.getInstance()._initializeInternal(scriptManagerID, formElement);
}

生成了一个Sys.WebForms.PageRequestManager对象实例,然后调用了该实例的_initializeInternal方法(实际方法名为 Sys$WebForms$PageRequestManager$_initializeInternal),在该方法中,

function Sys$WebForms$PageRequestManager$_initializeInternal(scriptManagerID, formElement) {
this._scriptManagerID = scriptManagerID;

this._form = formElement;

this._form._initialAction = this._form.action;

this._onsubmit = this._form.onsubmit;
this._form.onsubmit = null;
this._onFormSubmitHandler = Function.createDelegate(this, this._onFormSubmit);
this._onFormElementClickHandler = Function.createDelegate(this, this._onFormElementClick);
this._onWindowUnloadHandler = Function.createDelegate(this, this._onWindowUnload);
Sys.UI.DomEvent.addHandler(this._form, 'submit', this._onFormSubmitHandler);
Sys.UI.DomEvent.addHandler(this._form, 'click', this._onFormElementClickHandler);
Sys.UI.DomEvent.addHandler(window, 'unload', this._onWindowUnloadHandler);

this._originalDoPostBack = window.__doPostBack;
if (this._originalDoPostBack) {
window.__doPostBack = Function.createDelegate(this, this._doPostBack);
}

this._pageLoadedHandler = Function.createDelegate(this, this._pageLoadedInitialLoad);
Sys.UI.DomEvent.addHandler(window, 'load', this._pageLoadedHandler);
}

保存了单个ScriptManager的ID,对几个事件添加了处理函数,其中比较重要的是

Sys.UI.DomEvent.addHandler(this._form, 'submit', this._onFormSubmitHandler);
Sys.UI.DomEvent.addHandler(this._form, 'click', this._onFormElementClickHandler);

而上面的

Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tUpdatePanel1'], [], [], 90);

则保存了一系列的UpdatePanel的ID,可能造成PostBack的控件ID,以及异步PostBack过期时间(这里设置了90秒),参考MicrosoftAjaxWebForms.js里

的Sys$WebForms$PageRequestManager$_updateControls()函数。

在点击按钮后,上面的click事件会触发,导致Sys$WebForms$PageRequestManager$_onFormElementClick()的执行。在其中会设置_postBackSettings,如果你仔细研究一下_getPostBackSettings(),你会发现,它会将按钮的ID与它所在的UpdatePanel的ID通过“|”连接在一起。然后设置一些其他的输入参数,将来会在提交时传回服务器。

因为我们的按钮是个<input type="submit">,导致_onFormSubmit(), 即Sys$WebForms$PageRequestManager$_onFormSubmit()的执行,这是PostBack的关键函数。

function Sys$WebForms$PageRequestManager$_onFormSubmit(evt) {

这部分是检查客户端脚本的验证或验证控件是否合法

var continueSubmit = true;

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 (form.action !== form._initialAction) {
return;
}

if (!this._postBackSettings.async) {
return;
}

这部分生成传回服务器的数据

var formBody = new Sys.StringBuilder();

注意这里,第一个名字是_scriptManagerID,而值是_postBackSettings是UpdatePanel的ID“|”按钮的ID,

formBody.append(this._scriptManagerID + '=' + this._postBackSettings.panelID + '&');

所以我们在第一篇的PostBack数据里看到 "ScriptManager1=UpdatePanel1|btnUpdate&...." !

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(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(name);
formBody.append('=');
formBody.append(encodeURIComponent(option.value));
formBody.append('&');
}
}
}
else if (tagName === 'TEXTAREA') {
formBody.append(name);
formBody.append('=');
formBody.append(encodeURIComponent(element.value));
formBody.append('&');
}
}

if (this._additionalInput) {
formBody.append(this._additionalInput);
this._additionalInput = null;
}

生成一个Sys.Net.WebRequest对象,

var request = new Sys.Net.WebRequest();
request.set_url(form.action);

注意这里,这个是告诉服务器端,当前的PostBack是个局部的,不是整页更新,参考服务器端的Microsoft.Web.UI.PageRequestManager的 IsAsyncPostBackRequest() 方法的实现,

request.get_headers()['Pragma'] = 'Delta=true';
request.get_headers()['Cache-Control'] = 'no-cache';
request.set_timeout(this._asyncPostBackTimeout);

设置PostBack完成后的回调函数,

request.add_completed(Function.createDelegate(this, this._onFormSubmitCompleted));
request.set_body(formBody.toString());

下面是几个客户端的事件的触发,initializeRequest/beginRequest,你可以在自己的脚本里通过Sys.WebForms.PageRequestManager的实例对象加入自己的脚本监听PostBack状态或者取消PostBack,参考,

Customizing Partial-Page Rendering with Client Scripting
http://ajax.asp.net/docs/tutorials/usingMsAjaxLibrary/default.aspx

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);
}

this._request = request;

向服务器的PostBack在这里开始,

request.invoke();

if (evt) {
evt.preventDefault();
}
}

Sys.Net.WebRequest是在MicrosoftAjax.js里定义的,

Sys.Net.WebRequest = function Sys$Net$WebRequest() {
if (arguments.length !== 0) throw Error.parameterCount();
this._url = ""
this._headers = { };
this._body = null;
this._userContext = null;
this._httpVerb = null;
this._executor = null;
this._invokeCalled = false;
this._timeout = 0;
}

Sys.Net.WebRequest.prototype = {
add_completed: Sys$Net$WebRequest$add_completed,
remove_completed: Sys$Net$WebRequest$remove_completed,

completed: Sys$Net$WebRequest$completed,

_get_eventHandlerList: Sys$Net$WebRequest$_get_eventHandlerList,

get_url: Sys$Net$WebRequest$get_url,
set_url: Sys$Net$WebRequest$set_url,

get_headers: Sys$Net$WebRequest$get_headers,

get_httpVerb: Sys$Net$WebRequest$get_httpVerb,
set_httpVerb: Sys$Net$WebRequest$set_httpVerb,

get_body: Sys$Net$WebRequest$get_body,
set_body: Sys$Net$WebRequest$set_body,

get_userContext: Sys$Net$WebRequest$get_userContext,
set_userContext: Sys$Net$WebRequest$set_userContext,

get_executor: Sys$Net$WebRequest$get_executor,
set_executor: Sys$Net$WebRequest$set_executor,

get_timeout: Sys$Net$WebRequest$get_timeout,
set_timeout: Sys$Net$WebRequest$set_timeout,

getResolvedUrl: Sys$Net$WebRequest$getResolvedUrl,

invoke: Sys$Net$WebRequest$invoke
}

其中的invoke函数,调用的是Sys.Net.WebRequestManager.executeRequest()

function Sys$Net$WebRequest$invoke() {
if (arguments.length !== 0) throw Error.parameterCount();
if (this._invokeCalled) {
throw Error.invalidOperation(Sys.Res.invokeCalledTwice);
}

Sys.Net.WebRequestManager.executeRequest(this);
this._invokeCalled = true;
}

Sys.Net.WebRequestManager是一个Sys.Net._WebRequestManager实例对象,

Sys.Net._WebRequestManager = function Sys$Net$_WebRequestManager() {
this._this = this;
this._defaultTimeout = 0;
this._defaultExecutorType = "Sys.Net.XMLHttpExecutor"
}

Sys.Net._WebRequestManager.prototype = {
add_invokingRequest: Sys$Net$_WebRequestManager$add_invokingRequest,
remove_invokingRequest: Sys$Net$_WebRequestManager$remove_invokingRequest,

add_completedRequest: Sys$Net$_WebRequestManager$add_completedRequest,
remove_completedRequest: Sys$Net$_WebRequestManager$remove_completedRequest,

_get_eventHandlerList: Sys$Net$_WebRequestManager$_get_eventHandlerList,

get_defaultTimeout: Sys$Net$_WebRequestManager$get_defaultTimeout,
set_defaultTimeout: Sys$Net$_WebRequestManager$set_defaultTimeout,

get_defaultExecutorType: Sys$Net$_WebRequestManager$get_defaultExecutorType,
set_defaultExecutorType: Sys$Net$_WebRequestManager$set_defaultExecutorType,

executeRequest: Sys$Net$_WebRequestManager$executeRequest
}

上面的Sys.Net.WebRequestManager.executeRequest()执行的是这个函数,

function Sys$Net$_WebRequestManager$executeRequest(webRequest) {
/// <param name="webRequest" type="Sys.Net.WebRequest"></param>
var e = Function._validateParams(arguments, [
{name: "webRequest", type: Sys.Net.WebRequest}
]);
if (e) throw e;

var executor = webRequest.get_executor();
if (!executor) {
var failed = false;
try {

在这里生成了一个Sys.Net.XMLHttpExecutor对象

var executorType = eval(this._defaultExecutorType);
executor = new executorType();
} catch (e) {
failed = true;
}

if (failed || !Sys.Net.WebRequestExecutor.isInstanceOfType(executor) || !executor) {
throw Error.argument("defaultExecutorType", String.format(Sys.Res.invalidExecutorType, this._defaultExecutorType));
}

webRequest.set_executor(executor);
}

if (executor.get_aborted()) {
return;
}

这是另外一个的客户端事件,invokingRequest,

var evArgs = new Sys.Net.NetworkRequestEventArgs(webRequest);
var handler = this._get_eventHandlerList().getHandler("invokingRequest");
if (handler) {
handler(this, evArgs);
}

if (!evArgs.get_cancel()) {

终于开始执行,调用Sys.Net.XMLHttpExecutor.executeRequest,

executor.executeRequest();
}
}

Sys.Net.XMLHttpExecutor的定义,

Sys.Net.XMLHttpExecutor = function Sys$Net$XMLHttpExecutor() {
if (arguments.length !== 0) throw Error.parameterCount();

Sys.Net.XMLHttpExecutor.initializeBase(this);

var _this = this;
this._xmlHttpRequest = null;
this._webRequest = null;
this._responseAvailable = false;
this._timedOut = false;
this._timer = null;
this._aborted = false;
this._started = false;

this._onReadyStateChange = function () {
if (_this._xmlHttpRequest.readyState === 4 ) {

_this._clearTimer();
_this._responseAvailable = true;
_this._webRequest.completed(Sys.EventArgs.Empty);
if (_this._xmlHttpRequest != null) {
_this._xmlHttpRequest.onreadystatechange = Function.emptyMethod;
_this._xmlHttpRequest = null;
}
}
}

this._clearTimer = function this$_clearTimer() {
if (_this._timer != null) {
window.clearTimeout(_this._timer);
_this._timer = null;
}
}

this._onTimeout = function this$_onTimeout() {
if (!_this._responseAvailable) {
_this._clearTimer();
_this._timedOut = true;
_this._xmlHttpRequest.onreadystatechange = Function.emptyMethod;
_this._xmlHttpRequest.abort();
_this._webRequest.completed(Sys.EventArgs.Empty);
_this._xmlHttpRequest = null;
}
}

}

Sys.Net.XMLHttpExecutor.prototype = {

get_timedOut: Sys$Net$XMLHttpExecutor$get_timedOut,

get_started: Sys$Net$XMLHttpExecutor$get_started,

get_responseAvailable: Sys$Net$XMLHttpExecutor$get_responseAvailable,

get_aborted: Sys$Net$XMLHttpExecutor$get_aborted,

executeRequest: Sys$Net$XMLHttpExecutor$executeRequest,

getResponseHeader: Sys$Net$XMLHttpExecutor$getResponseHeader,

getAllResponseHeaders: Sys$Net$XMLHttpExecutor$getAllResponseHeaders,

get_responseData: Sys$Net$XMLHttpExecutor$get_responseData,

get_statusCode: Sys$Net$XMLHttpExecutor$get_statusCode,

get_statusText: Sys$Net$XMLHttpExecutor$get_statusText,

get_xml: Sys$Net$XMLHttpExecutor$get_xml,

abort: Sys$Net$XMLHttpExecutor$abort
}

终于到了最原始的地方,

function Sys$Net$XMLHttpExecutor$executeRequest() {
if (arguments.length !== 0) throw Error.parameterCount();
this._webRequest = this.get_webRequest();

if (this._started) {
throw Error.invalidOperation(String.format(Sys.Res.cannotCallOnceStarted, 'executeRequest'));
}
if (this._webRequest === null) {
throw Error.invalidOperation(Sys.Res.nullWebRequest);
}

var body = this._webRequest.get_body();
var headers = this._webRequest.get_headers();

this._xmlHttpRequest = new XMLHttpRequest();

this._xmlHttpRequest.onreadystatechange = this._onReadyStateChange;
var verb = this._webRequest.get_httpVerb();
this._xmlHttpRequest.open(verb, this._webRequest.getResolvedUrl(), true );

if (headers) {
for (var header in headers) {
var val = headers[header];
if (typeof(val) !== "function")
this._xmlHttpRequest.setRequestHeader(header, val);
}
}

if (verb.toLowerCase() === "post") {
if ((headers === null) || !headers['Content-Type']) {
this._xmlHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}

if (!body) {
body = ""
}
}

var timeout = this._webRequest.get_timeout();
if (timeout > 0) {
this._timer = window.setTimeout(Function.createDelegate(this, this._onTimeout), timeout);
}
this._xmlHttpRequest.send(body);
this._started = true;
}

其中的XMLHttpRequest是这么定义的,

if (!window.XMLHttpRequest) {
window.XMLHttpRequest = function window$XMLHttpRequest() {
var progIDs = [ 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP' ];
for (var i = 0; i < progIDs.length; i++) {
try {
var xmlHttp = new ActiveXObject(progIDs[i]);
return xmlHttp;
}
catch (ex) {
}
}
return null;
}
}

IE7定义了原生的XMLHttpRequest对象,不使用ActiveX对象,参考

http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/overview/aboutxmlhttp.asp

XMLHttpRequest的onreadystatechange()调用WebRequest的completed(),即调用 MicrosoftAjaxWebForms.js 里Sys.WebForms.PageRequestManager 的_onFormSubmitCompleted(), 即Sys$WebForms$PageRequestManager$_onFormSubmitCompleted()

这个函数分析返回的数据,里面有个很大的switch 语句,只更新其中某些控件,同时更新__VIEWSTATE等隐藏控件,加载脚本等。这个函数太大,就不抄袭在这里了 。

总结如下,

用户看到的是Sys.WebForms.PageRequestManager对象,PostBack时通过生成的Sys.Net.WebRequest对象,然后调用Sys.Net.WebRequestManager对象(属于Sys.Net._WebRequestManager类)的executeRequest()方法,把Sys.Net.WebRequest对象当作参数传进去,该函数生成一个Sys.Net.XMLHttpExecutor对象,调用它的executeRequest()方法,该方法最终生成一个XMLHttpRequest对象,调用它的send()方法!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值