返回列表

前端MVC框架[02] 发送AJAX请求及建立连接池

默认分类 2012-10-11 07:51:28

<

ol start='100' class='dp-xml'>

  • /  
  •   异步请求管理器  
  •  /  
  • var Requester = {  
  •     /  
  •       全局事件处理接口 注:不支持onsuccess  
  •        
  •       @Map {'ontimeout':function(){},'onfailure':function(){}}  
  •      /  
  •     handler:{},  
  •     /   
  •       获取XMLHttpRequest对象   
  •          
  •       @return {XMLHttpRequest} XMLHttpRequest对象   
  •       @description 使用缓存模式避免每次都检测浏览器类型  
  •      */   
  •     createXHRObject: function () {  
  •         var me = this,  
  •             i,  
  •             list,  
  •             len,  
  •             xhr = null,  
  •             methods = [  
  •                 function () {return new XMLHttpRequest();},   
  •                 function () {return new ActiveXObject('Msxml2.XMLHTTP');},  
  •                 function () {return new ActiveXObject('Microsoft.XMLHTTP');}  
  •             ];  
  •           
  •         for (i = 0, len = methods.length; i < len; i++) {  
  •             try {  
  •                 xhr = methodsi;  
  •                 this.createXHRObject = methods[i];  
  •                 break;  
  •             } catch (e) {  
  •                 continue;  
  •             }  
  •         }  
  •         if (!xhr) {  
  •             throw new Error(100000,'Requester.createXHRObject() fail. Your browser not support XHR.');  
  •         }  
  •           
  •         xhr.eventHandlers = {};  
  •         xhr.fire = me.creatFireHandler();  
  •           
  •         return xhr;  
  •     },  
  •                                       
  •     /   
  •      * 生成新的触发事件方法   
  •      *    
  •      * @param {String} type 事件类型   
  •      */   
  •     creatFireHandler: function(){  
  •         return function (type) {   
  •             type = 'on' + type;   
  •             var xhr = this,  
  •                 handler = xhr.eventHandlers[type],   
  •                 globelHandler = window.Requester.handler[type];   
  •                
  •             // 不对事件类型进行验证   
  •             if (handler) {   
  •                 if (xhr.tick) {   
  •                   clearTimeout(tick);   
  •                 }   
  •                   
  •                 if (type != 'onsuccess') {   
  •                     handler(xhr);   
  •                 } else {   
  •                     //处理获取xhr.responseText导致出错的情况,比如请求图片地址.   
  •                     try {   
  •                         xhr.responseText;   
  •                     } catch(error) {   
  •                         return handler(xhr);   
  •                     }   
  •                     var text = xhr.responseText.replace(/^\s+/ig, "");   
  •                     if(text.indexOf('{') === 0){   
  •                         //{success:true,message:   
  •                         //插入表单验证错误提示   
  •                         var JSONParser;   
  •                         try {   
  •                             JSONParser = new Function("return " + text + ";");  
  •                             data = JSONParser();    
  •                         }   
  •                         //如果json解析出错则尝试移除多于逗号再试   
  •                         catch (e){   
  •                             JSONParser = new Function("return " + window.Requester.removeJSONExtComma(text) + ";");   
  •                             data = JSONParser();    
  •                         }   
  •                         if ( String(data.success).replace(/\s/ig,'').toLowerCase() !== 'true' ) {   
  •                             // 当后端验证失败时  
  •                             if (Requester.backendError) {  
  •                                 Requester.backendError(data);  
  •                             }  
  •                         }   
  •                            
  •                         handler(data);   
  •                     }else{   
  •                         handler(text);   
  •                     }   
  •                 }   
  •             }   
  •             // 检查是否配置了全局事件  
  •             else if (globelHandler) {   
  •                 //onsuccess不支持全局事件   
  •                 if (type == 'onsuccess') {   
  •                     return;   
  •                 }   
  •                 globelHandler(xhr);   
  •             }  
  •         };  
  •     },  
  •     /  
  •       检测是否有空闲的XHR或创建新对象  
  •        
  •       @after Requester  
  •       @comment 使用Facade外观模式修改Requester.request方法  
  •       以增加路径权限判断  
  •      /  
  •     getValidXHR: function () {  
  •         var me = this;  
  •         return me.createXHRObject();  
  •     },  
  •     /  
  •      * request发送请求  
  •      *  
  •      * @url {String} 请求的URL  
  •      * @options {Map} POST的参数,回调函数,MD5加密等  
  •      */   
  •     request: function (url, opt_options) {  
  •         var xhr = this.getValidXHR();  
  •         //有可用连接  
  •         if (xhr) {  
  •             var me = this,  
  •                 options     = opt_options || {},   
  •                 data        = options.data || "",   
  •                 async       = !(options.async === false),   
  •                 username    = options.username || "",   
  •                 password    = options.password || "",   
  •                 method      = (options.method || "GET").toUpperCase(),   
  •                 headers     = options.headers || {},   
  •                 // 基本的逻辑来自lili同学提供的patch   
  •                 timeout     = options.timeout || 0,   
  •                 usemd5      = options.usemd5 || false,  
  •                 tick, key, str,  
  •                 stateChangeHandler = me.createStateChangeHandler();   
  •            
  •               
  •             // 将options参数中的事件参数复制到eventHandlers对象中   
  •             // 这里复制所有options的成员,eventHandlers有冗余   
  •             // 但是不会产生任何影响,并且代码紧凑                
  •             for (key in options) {   
  •                 xhr.eventHandlers[key] = options[key];   
  •             }   
  •                
  •             headers['X-Requested-With'] = 'XMLHttpRequest';   
  •                
  •                
  •             try {   
  •                 //提交到服务器端的参数是Map则转换为string  
  •                 if(Object.prototype.toString.call(data)==='[object Object]'){   
  •                     str = []   
  •                     for(key in data){  
  •                         if (key){  
  •                             str.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))   
  •                         }  
  •                     }  
  •                     data = str.join('&');  
  •                 }  
  •                   
  •                 //使用GET方式提交  
  •                 if (method == 'GET') {   
  •                     if (data) {   
  •                         url += (url.indexOf('?') >= 0 ? ( data.substr(0,1) == '&' ? '' : '&') : '?') + data;   
  •                         data = null;   
  •                     }  
  •                 }  
  •                 else if (usemd5) {  
  •                     data = me.encodeMD5(data);  
  •                 }  
  •                   
  •                 if (username) {   
  •                     xhr.open(method, url, async, username, password);   
  •                 } else {   
  •                     xhr.open(method, url, async);   
  •                 }   
  •                   
  •                 if (async) {   
  •                     xhr.onreadystatechange = stateChangeHandler;   
  •                 }   
  •                    
  •                 // 在open之后再进行http请求头设定   
  •                 // FIXME 是否需要添加; charset=UTF-8呢   
  •                 if (method == 'POST') {   
  •                     xhr.setRequestHeader("Content-Type",   
  •                         (headers['Content-Type'] || "application/x-www-form-urlencoded"));   
  •                 }   
  •                    
  •                 for (key in headers) {   
  •                     if (headers.hasOwnProperty(key)) {   
  •                         xhr.setRequestHeader(key, headers[key]);   
  •                     }   
  •                 }   
  •                    
  •                 xhr.fire('beforerequest');   
  •                   
  •                 if (timeout) {   
  •                   xhr.tick = setTimeout(function(){   
  •                     xhr.onreadystatechange = window.Requester.fn;   
  •                     xhr.abort();   
  •                     xhr.fire("timeout");   
  •                   }, timeout);   
  •                 }   
  •                 xhr.send(data);   
  •                    
  •                 if (!xhr.eventHandlers['async']) {   
  •                     stateChangeHandler();   
  •                 }   
  •             } catch (ex) {   
  •                 xhr.fire('failure');   
  •             }   
  •         }  
  •         //暂时没有可用连接,将请求放进队列  
  •         else {  
  •             this.que.push({'url':url,'options':options});  
  •         }  
  •     },  
  •     /   
  •       readyState发生变更时调用   
  •          
  •       @ignore   
  •      /   
  •     createStateChangeHandler: function() {   
  •         return function() {  
  •             var xhr = this;  
  •             if (xhr.readyState == 4) {   
  •                 try {   
  •                     var stat = xhr.status;   
  •                 } catch (ex) {   
  •                     // 在请求时,如果网络中断,Firefox会无法取得status   
  •                     xhr.fire('failure');   
  •                     return;   
  •                 }   
  •                    
  •                 xhr.fire(stat);   
  •                    
  •                 // http://www.never-online.net/blog/article.asp?id=261   
  •                     // case 12002: // Server timeout         
  •                     // case 12029: // dropped connections   
  •                     // case 12030: // dropped connections   
  •                     // case 12031: // dropped connections   
  •                     // case 12152: // closed by server   
  •                     // case 13030: // status and statusText are unavailable   
  •                    
  •                 // IE error sometimes returns 1223 when it    
  •                 // should be 204, so treat it as success   
  •                 if ((stat >= 200 && stat < 300)   
  •                     || stat == 304   
  •                     || stat == 1223) {   
  •                     xhr.fire('success');   
  •                 } else {   
  •                     xhr.fire('failure');   
  •                 }   
  •                    
  •                 /   
  •                   NOTE: Testing discovered that for some bizarre reason, on Mozilla, the   
  •                   JavaScript <code>XmlHttpRequest.onreadystatechange</code> handler   
  •                   function maybe still be called after it is deleted. The theory is that the   
  •                   callback is cached somewhere. Setting it to null or an empty function does   
  •                   seem to work properly, though.   
  •                      
  •                   On IE, there are two problems: Setting onreadystatechange to null (as   
  •                   opposed to an empty function) sometimes throws an exception. With   
  •                   particular (rare) versions of jscript.dll, setting onreadystatechange from   
  •                   within onreadystatechange causes a crash. Setting it from within a timeout   
  •                   fixes this bug (see issue 1610).   
  •                      
  •                   End result: always set onreadystatechange to an empty function (never to   
  •                   null). Never set onreadystatechange from within onreadystatechange (always   
  •                   in a setTimeout()).   
  •                  */   
  •                 window.setTimeout(function() {   
  •                     // 避免内存泄露.   
  •                     // 由new Function改成不含此作用域链的 window.Requester.fn 函数,   
  •                     // 以避免作用域链带来的隐性循环引用导致的IE下内存泄露. By rocy 2011-01-05 .   
  •                     xhr.onreadystatechange = window.Requester.fn;   
  •                     if (xhr.eventHandlers['async']) {   
  •                         xhr = null;   
  •                     }   
  •                 }, 0);   
  •                   
  •                 window.Requester.checkQue();  
  •             }   
  •         }  
  •     },  
  •     /  
  •      * encodeMD5加密提交的数据  
  •      *  
  •      * @data {String} 需要加密的paramString  
  •      * @return {String} 加密后的paramString  
  •      */   
  •     encodeMD5: function (data) {  
  •         var paramstr = Base64.encode(data).replace(/\+/g,'*');  
  •         var md5 = String(MD5.encode(paramstr)).toUpperCase();  
  •         paramstr = paramstr.split('');  
  •         paramstr.reverse();  
  •           
  •         return 'result=' + md5 + paramstr.join('');  
  •     }  
  • };  
  •   
  • window.Requester = Requester;  
  • window.Requester.fn = function(){};  
  •   
  • /**  
  •  * 移除JSON字符串中多余的逗号如{'a':[',],}',],}  
  •  *  
  •  * @param {string} JSON字符串  
  •  * @return {string} 处理后的JSON字符串  
  •  */  
  • Requester.removeJSONExtComma = function(str) {  
  •     var i,  
  •         j,  
  •         len,  
  •         list,  
  •         c,  
  •         notValue = null,  
  •         preQuot = null,  
  •         lineNum;  
  •   
  •     list = String(str).split('');  
  •     for (i = 0, len = list.length; i < len; i++) {  
  •         c = list[i];  
  •         //单引或双引  
  •         if (/^[\'\"]$/.test(c)) {  
  •             if (notValue === null && preQuot === null) {  
  •                 notValue = false;  
  •                 preQuot = i;  
  •                 continue;  
  •             }  
  •             //值  
  •             if (!notValue) {  
  •                 //前面反斜杠个数  
  •                 lineNum = 0;  
  •                 for (j = i - 1; j > -1; j--) {  
  •                     if (list[j] === '\\') {lineNum++;}  
  •                     else { j = -1; }  
  •                 }  
  •                 //个数为偶数且和开始引号相同  
  •                 //结束引号  
  •                 if (lineNum % 2 === 0) {  
  •                     if (list[preQuot] === c) {  
  •                         notValue = true;  
  •                         preQuot = -1;  
  •                     }  
  •                 }  
  •             }  
  •             //非值  
  •             else {  
  •                 //开始引号  
  •                 if (preQuot == -1) {  
  •                     preQuot = i;  
  •                     notValue = false;  
  •                 }  
  •                 //结束引号  </