前端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; } //结束引号 </
>> 留言评论