(三)AJAXPro之旅---原理的探究

前面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函数比较的重要,他将会在服务端返回后执行.

doStateChange:  function () {
        
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函数

createResponse:  function (r, noContent) {
        
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的分析(加了注释了):)

AjaxPro.IFrameXmlHttp.prototype  =  {
    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 );
    }
};

 


 

你可能感兴趣的:(Ajax)