惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
大猫的无限游戏
大猫的无限游戏
T
Tailwind CSS Blog
S
SegmentFault 最新的问题
V
V2EX
阮一峰的网络日志
阮一峰的网络日志
C
Cisco Blogs
博客园 - 叶小钗
P
Privacy International News Feed
Jina AI
Jina AI
Apple Machine Learning Research
Apple Machine Learning Research
T
Threatpost
IT之家
IT之家
博客园 - 聂微东
Know Your Adversary
Know Your Adversary
Help Net Security
Help Net Security
罗磊的独立博客
I
Intezer
S
Schneier on Security
博客园_首页
C
CERT Recently Published Vulnerability Notes
雷峰网
雷峰网
Cisco Talos Blog
Cisco Talos Blog
宝玉的分享
宝玉的分享
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Webroot Blog
Webroot Blog
TaoSecurity Blog
TaoSecurity Blog
MyScale Blog
MyScale Blog
P
Privacy & Cybersecurity Law Blog
T
The Exploit Database - CXSecurity.com
PCI Perspectives
PCI Perspectives
Security Latest
Security Latest
H
Heimdal Security Blog
S
Secure Thoughts
Hacker News: Ask HN
Hacker News: Ask HN
Y
Y Combinator Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Microsoft Security Blog
Microsoft Security Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
SecWiki News
SecWiki News
The GitHub Blog
The GitHub Blog
A
Arctic Wolf
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
T
Threat Research - Cisco Blogs
Engineering at Meta
Engineering at Meta
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

博客园 - 雨中流泪

轻量的PHP开发框架--- litePhp Rails 命令大全 ObjectHTML Framework 0.0.9.1 for PHP 发布.欢迎试用 在学习中沉沦... 在Dom4j中使用xpath - 雨中流泪 - 博客园 Spring.Net + NHibernate 入门例子 - 雨中流泪 使用Spring 2.5 和 Hibernate 3.2 开发MVC Web程序(基于annotation特性) PHP函数收藏---不断更新中! - 雨中流泪 - 博客园 程序员的编辑器--EmEditor 自己写的一个JS组件,JCombo 0.9 - 雨中流泪 - 博客园 Enterprise Library 3.1的研究之路---Data Access Application Block(1) 发个"违法"的东西.VisualAssist X 1559破解版 自己写的Log日志记录类,支持文件和数据库,自动建立Log表格 (二)AJAXPro之旅---构造实际的AJAX应用. (一)AJAXPro之旅---神奇的小魔盒 Windows Live Writer 我的AJAX类的使用(一个简单的异步组件) Grove,.Net下最方便的O/R Map库 ASP.Net分页组件1.0开发下载了...
(三)AJAXPro之旅---原理的探究 - 雨中流泪 - 博客园
雨中流泪 · 2007-09-12 · via 博客园 - 雨中流泪

前面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);
        //发送数据
        
trythis.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);
    }
};