前面2篇文章我介绍了AJAXPro的一些实际应用.但是其基本的原理还不是很清楚,感觉做技术的,
最好还是"知其然知其所以然",这样才可以做到"有的放矢".
现在我们从头开始,了解AJAXPro的处理方式.
首先,我们在Web.config中添加了一段httpHandler注册.
<add path="*.ashx" verb="*" type="AjaxPro.AjaxHandlerFactory,AjaxPro.2"/>
这样,所有以ashx结尾的文件都由AjaxPro.AjaxHandlerFactory这个httpHandler处理.
打开使用AJAXPro注册的页面(就是使用AjaxPro.Utility.RegisterTypeForAjax(typeof(页面类))的页面).
查看其代码:发现他添加了4段js代码:
<script type="text/javascript" src="/ajaxpro/prototype.ashx"></script> <script type="text/javascript" src="/ajaxpro/core.ashx"></script> <script type="text/javascript" src="/ajaxpro/converter.ashx"></script> <script type="text/javascript" src="/ajaxpro/SD2007.Test.Test1,SD2007.ashx"></script>
prototype.ashx就是prototype.js.一个著名的js开源AJAX框架.(地球人都知道吧!)
core.ashx是什么呢?
他其实就是一个js的类,这个类的作用是创建XMLHttpRequest对象进行异步操作.还有,XMLHttpRequest是依赖于浏览器的.
如果浏览器不能创建,则创建一个IFrameXmlHttp类来进行异步操作,也就是创建一个iframe.
converter.ashx是一个转换对象,用来把.Net数据类型转换到js中使用.前面一篇我的函数返回的是一个Hashtable 对象,但
他则转换成2维的数组.这个对象可以转换很多的.net数据类型.
SD2007.Test.Test1,SD2007.ashx就是我们页面注册AJAXPro对象产生的文件,他的代码不长,如下:
if
(
typeof
SD2007
==
"
undefined
"
) SD2007
=
{};
if
(
typeof
SD2007.Test
==
"
undefined
"
) SD2007.Test
=
{};
SD2007.Test.Test1_class
=
function
() {};
Object.extend(SD2007.Test.Test1_class.prototype, Object.extend(
new
AjaxPro.AjaxClass(), {
GetCity:
function
(sName) {
return
this
.invoke(
"
GetCity
"
, {
"
sName
"
:sName},
this
.GetCity.getArguments().slice(
1
));
},
url: '
/
ajaxpro
/
SD2007.Test.Test1,SD2007.ashx'
}));
SD2007.Test.Test1
=
new
SD2007.Test.Test1_class();
他利用prototype建立对象,并"继承"对象AjaxPro.AjaxClass,扩展一个GetCity方法,这个方法invoke调用ASP.Net里
注册为AJAXPro.Method的GetCity方法.也就是异步操作的核心.他其实也是利用其他的js框架来实现异步数据的操作,不
过"异步的调用一个方法"而已.我们现在看看其invoke方法的实现(红色的注释是我自己添加上去的):
invoke: function (method, args, callback, context) {
this .__start = new Date().getTime();
// if(this.xmlHttp == null) {
this .xmlHttp = new XMLHttpRequest(); //建立异步对象
// }
this .isRunning = true ;
this .method = method;
this .args = args;
this .callback = callback;
this .context = context;
//是否使用异步操作
var async = typeof (callback) == " function " && callback != AjaxPro.noOperation;
if (async) {
if (MS.Browser.isIE) {
this .xmlHttp.onreadystatechange = this .doStateChange.bind( this );
} else {
this .xmlHttp.onload = this .doStateChange.bind( this );
this .xmlHttp.onerror = this .mozerror.bind( this );
}
this .onLoading( true );
}
var json = AjaxPro.toJSON(args) + "" ; //转换成JSON数据
//下面做什么的不知道,好像是用了加密数据的
if (AjaxPro.cryptProvider != null && typeof AjaxPro.cryptProvider.encrypt == " function " ) {
json = AjaxPro.cryptProvider.encrypt(json);
}
this .xmlHttp.open( " POST " , this .url, async); //post数据
this .xmlHttp.setRequestHeader( " Content-Type " , " text/plain; charset=utf-8 " );
this .xmlHttp.setRequestHeader( " X- " + AjaxPro.ID + " -Method " , method);
if (AjaxPro.token != null && AjaxPro.token.length > 0 ) {
this .xmlHttp.setRequestHeader( " X- " + AjaxPro.ID + " -Token " , AjaxPro.token);
}
/* if(!MS.Browser.isIE) {
this.xmlHttp.setRequestHeader("Connection", "close");
} */
//超时处理
this .timeoutTimer = setTimeout( this .timeout.bind( this ), AjaxPro.timeoutPeriod);
//发送数据
try { this .xmlHttp.send(json); } catch (e){} // IE offline exception
if ( ! async) {
return this .createResponse({error: null ,value: null });
}
return true ;
}
};
发现就是js调用XMLHttpRequest对象,然后发生数据进行异步操作而已.不过加了其他的一些他的代码.不过其中的
doStateChange函数比较的重要,他将会在服务端返回后执行.
this .onStateChanged( this .xmlHttp.readyState, this );
if ( this .xmlHttp.readyState != 4 || ! this .isRunning) {
return ;
}
this .duration = new Date().getTime() - this .__start;
if ( this .timeoutTimer != null ) {
clearTimeout( this .timeoutTimer);
}
var res = this .getEmptyRes();
if ( this .xmlHttp.status == 200 && this .xmlHttp.statusText == " OK " ) {
res = this .createResponse(res);
} else {
res = this .createResponse(res, true );
res.error = {Message: this .xmlHttp.statusText,Type: " ConnectFailure " ,Status: this .xmlHttp.status};
}
this .endRequest(res);
},
如果 status 是 200,也就是 OK,那么清除掉超时处理函数,最后使用 callback 调用 createResponse 函数
还记得如果不是异步的话,createResponse 将会直接调用而不是通过 doStateChange 吧。
下面我们继续看看createResponse函数
if ( ! noContent) {
if ( typeof ( this .xmlHttp.responseText) == " unknown " ) {
r.error = {Message: " XmlHttpRequest error reading property responseText. " , Type: " XmlHttpRequestException " };
return r;
}
var responseText = "" + this .xmlHttp.responseText;
//解密数据
if (AjaxPro.cryptProvider != null && typeof AjaxPro.cryptProvider.decrypt == " function " ) {
responseText = AjaxPro.cryptProvider.decrypt(responseText);
}
if ( this .xmlHttp.getResponseHeader( " Content-Type " ) == " text/xml " ) {
r.value = this .xmlHttp.responseXML;
} else {
if (responseText != null && responseText.trim().length > 0 ) {
r.json = responseText;
var v = null ;
eval( " v = " + responseText + " ; " );
if (v != null ) {
if ( typeof v.value != " undefined " ) r.value = v.value;
else if ( typeof v.error != " undefined " ) r.error = v.error;
}
}
}
}
/* if(this.xmlHttp.getResponseHeader("X-" + AjaxPro.ID + "-Cache") == "server") {
r.isCached = true;
} */
//返回响应的数据
return r;
}
如果前面的 json 也就是 Request 是加过密的,这里就需要对 responseText 进行解密。完了之后得到 r.value,r 将会被返回并提供给 callback 函数。r 被传入它的 res 参数。整个 Ajax ClientScript 的流程就差不多是完成了。
AJAXPro大概原理的探究已经差不多了,看完了他的实现,我才发现js真的是一个很"强悍"的语言.学好了,在web开
发上将会如虎添翼.
下篇介绍:(四)AJAXPro之旅---AJAXPro类库的探索...
(特补上IFrameXmlHttp的分析(加了注释了):)
onreadystatechange: null , headers: [], method: " POST " , url: null , async: true , iframe: null ,
status: 0 , readyState: 0 , responseText: null ,
abort: function () {
},
readystatechanged: function () {
var doc = this .iframe.contentDocument || this .iframe.document;
if (doc != null && doc.readyState == " complete " && doc.body != null && doc.body.res != null ) {
this .status = 200 ;
this .statusText = " OK " ;
this .readyState = 4 ;
this .responseText = doc.body.res; // 设置响应文本为IFrame的documnet的内容
this .onreadystatechange();
return ;
}
setTimeout( this .readystatechanged.bind( this ), 10 );
},
open: function (method, url, async) {
if (async == false ) {
alert( " Synchronous call using IFrameXMLHttp is not supported. " );
return ;
}
if ( this .iframe == null ) {
var iframeID = " hans " ;
if (document.createElement && document.documentElement &&
(window.opera || navigator.userAgent.indexOf('MSIE 5.0 ') == - 1 ))
{
// 建立iframe
var ifr = document.createElement('iframe');
ifr.setAttribute('id', iframeID);
ifr.style.visibility = 'hidden'; // 隐藏iframe
ifr.style.position = 'absolute';
ifr.style.width = ifr.style.height = ifr.borderWidth = '0px';
this .iframe = document.getElementsByTagName('body')[ 0 ].appendChild(ifr); // 添加到body
}
else if (document.body && document.body.insertAdjacentHTML)
{
// 也是建立iframe,考虑到浏览器兼容性
document.body.insertAdjacentHTML('beforeEnd', ' < iframe name = " ' + iframeID + ' " id = " ' + iframeID + ' " style = " border:1px solid black;display:none " ></ iframe > ');
}
// 设置iframe变量为刚才建立的iframe DOM对象
if (window.frames && window.frames[iframeID]) {
this .iframe = window.frames[iframeID];
}
this .iframe.name = iframeID;
this .iframe.document.open();
this .iframe.document.write( " < " + " html>< " + " body></ " + " body></ " + " html> " );
this .iframe.document.close();
}
this .method = method;
this .url = url;
this .async = async;
},
// 建立响应头
setRequestHeader: function (name, value) {
for ( var i = 0 ; i < this .headers.length; i ++ ) {
if ( this .headers[i].name == name) {
this .headers[i].value = value;
return ;
}
}
this .headers.push({ " name " :name, " value " :value});
},
getResponseHeader: function (name, value) {
return null ;
},
// 建立input ,如果数据有回车则建立textarea
addInput: function (doc, form, name, value) {
var ele;
var tag = " input " ;
if (value.indexOf( " /n " ) >= 0 ) {
tag = " textarea " ;
}
if (doc.all) {
ele = doc.createElement( " < " + tag + " name=/ "" + name + " / " /> " );
} else {
ele = doc.createElement(tag);
ele.setAttribute( " name " , name);
}
ele.setAttribute( " value " , value);
form.appendChild(ele);
ele = null ;
},
send: function (data) {
if ( this .iframe == null ) {
return ;
}
var doc = this .iframe.contentDocument || this .iframe.document;
var form = doc.createElement( " form " );
// 建立一个form.设置其参数
doc.body.appendChild(form);
form.setAttribute( " action " , this .url);
form.setAttribute( " method " , this .method);
form.setAttribute( " enctype " , " application/x-www-form-urlencoded " );
for ( var i = 0 ; i < this .headers.length; i ++ ) {
switch ( this .headers[i].name.toLowerCase()) {
case " content-length " :
case " accept-encoding " :
case " content-type " :
break ;
default :
this .addInput(doc, form, this .headers[i].name, this .headers[i].value);
}
}
// 建立input ,如果数据有回车则建立textarea
this .addInput(doc, form, " data " , data);
form.submit(); // 提交数据
setTimeout( this .readystatechanged.bind( this ), 0 );
}
};