前端MVC框架[02] 发送AJAX请求及建立连接池
默认分类 2012-10-11 07:51:28
- /
- 异步请求管理器
- /
- 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;
- }
- //结束引号 </
>> 留言评论