[转帖]Mootools源码分析-33 -- Request

原帖地址:http://space.flash8.net/space/?18713/viewspace-406767.html

原作者:我佛山人

 

var Request = new Class({
    
//继承Chain,Events和Options的接口实现
    Implements: [Chain, Events, Options],

    options: {
        
/*onRequest: $empty, //开始事件
        onSuccess: $empty,  //成功事件
        onFailure: $empty,  //失败事件
        onException: $empty,
*/ //异常事件
        url: '',    //Ajax请求的地址
        data: '',    //Ajax提交的地址
        headers: {    //Ajax请求的HTTP头数据
            //来源信息
            'X-Requested-With''XMLHttpRequest',
            
//接受的内容类型
            'Accept''text/javascrīpt, text/html, application/xml, text/xml, */*'
        },
        
//是否异步请求
        async: true,
        
//提交方式
        method: 'post',
        
//多次请求冲突时的处理方式
        link: 'ignore',
        
//判断状态成功的方法
        isSuccess: null,
        
//判断状态成功的方法
        emulation: true,
        
//是否使用url编码
        urlEncoded: true,
        
//POST请求时使用的编码
        encoding: 'utf-8',
        
//动态执行字符脚本
        evalscrīpts: false,
        
//动态执行返回字符脚本
        evalResponse: false
    },

    
//构造函数
    initialize: function(options)    {
        
//使用浏览器特性创建一个XmlHttpRequest对象
        this.xhr = new Browser.Request();
        
this.setOptions(options);
        
//判断状态成功的方法,如果参数中不提供,使用默认方法
        this.options.isSuccess = this.options.isSuccess || this.isSuccess;
        
this.headers = new Hash(this.options.headers);
    },

    onStateChange: 
function()    {
        
//如果状态码不等于4或者没有正执行的请求,直接返回
        if (this.xhr.readyState != 4 || !this.running)    return;
        
//设置标记
        this.running = false;
        
//初始化状态码
        this.status = 0;
        
//设置状态码
        $try(function()    {
            
this.status = this.xhr.status;
        }.bind(
this));
        
//如果状态码显示请求成功
        if (this.options.isSuccess.call(thisthis.status))    {
            
//提取响应数据
            this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
            
//请求成功
            this.success(this.response.text, this.response.xml);
        }    
else    {
            
//响应数据全为null
            this.response = {text: null, xml: null};
            
//请求失败
            this.failure();
        }
        
//置空状态改变事件监听
        this.xhr.onreadystatechange = $empty;
    },

    
//判断请求状态是否成功
    isSuccess: function()    {
        
//状态码大于等于200小于300时为成功
        return ((this.status >= 200&& (this.status < 300));
    },

    
//处理返回字符中包含的脚本
    processscrīpts: function(text)    {
        
//如果返回字符为纯脚本,直接解释执行
        if (this.options.evalResponse || (/(ecma|java)scrīpt/).test(this.getHeader('Content-type')))    return $exec(text);
        
//混合字符时过滤出脚本,根据配置参数是否执行包含脚本
        return text.stripscrīpts(this.options.evalscrīpts);
    },

    
//请求成功
    success: function(text, xml)    {
        
//触发事件
        this.onSuccess(this.processscrīpts(text), xml);
    },

    
//触发请求成功事件
    onSuccess: function()    {
        
//同时触发请求完成和请求成功事件
        this.fireEvent('onComplete', arguments).fireEvent('onSuccess', arguments).callChain();
    },

    
//请求失败
    failure: function()    {
        
//触发事件
    this.onFailure();
    },

    
//触发请求失败事件
    onFailure: function()    {
        
//同时触发请求完成和请求失败事件
        this.fireEvent('onComplete').fireEvent('onFailure'this.xhr);
    },

    
//设置请求的HTTP头部数据
    setHeader: function(name, value)    {
        
this.headers.set(name, value);
        
return this;
    },

    
//获取响应的HTTP头部数据
    getHeader: function(name)    {
        
return $try(function()    {
            
return this.xhr.getResponseHeader(name);
        }.bind(
this));
    },

    
//检查约束,类似Fx的同名方法
    check: function()    {
        
//如果请求线程没有在运行中,返回真允许运行当前线程的请求
        if (!this.running)    return true;
        
//如果当前有正运行的请求线程
        switch (this.options.link)    {
        
//配置了link参数为cancel时,直接取消正执行的线程,启用新线程
        case 'cancel'this.cancel();    return true;
        
//等待当前线程执行完成再执行新线程
        case 'chain'this.chain(this.send.bind(this, arguments));    return false;
        }
        
return false;
    },

    
//发送请求
    send: function(options)    {
        
//检查约束
        if (!this.check(options))    return this;
        
//设置标记
        this.running = true;

        
//根据参数类型处理
        var type = $type(options);
        
if (type == 'string' || type == 'element') options = {data: options};

        
//实现更灵活的使用,可以将配置参数延后在send时
        var old = this.options;
        options 
= $extend({data: old.data, url: old.url, method: old.method}, options);
        
var data = options.data, url = options.url, method = options.method;

        
//不同类型的处理
        switch ($type(data))    {
        
//如果是HTML Element,读取它的值,并根据name值构成name=value的名值对
        case 'element': data = $(data).toQueryString(); break;
        
case 'object'case 'hash': data = Hash.toQueryString(data);
        }

        
//put和delete提交方式时的处理,因为Ajax不支持put和delete,所以通过添加一个_method给后台处理
        if (this.options.emulation && ['put''delete'].contains(method))    {
            
var _method = '_method=' + method;
            data 
= (data) ? _method + '&' + data : _method;
            method 
= 'post';
        }

        
//POST方式指定编码时的处理,需要设置HTTP头部信息
        if (this.options.urlEncoded && method == 'post')    {
            
var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
            
this.headers.set('Content-type''application/x-www-form-urlencoded' + encoding);
        }

        
//GET方式时的处理,直接将提交的数据加在请求地址后面
        if (data && method == 'get')    {
            
//判断请求地址中有否包含问号
            url = url + (url.contains('?'? '&' : '?'+ data;
            data 
= null;
        }

        
//打开请求端口,注意以下代码中xhr对象的方法的执行顺序
        this.xhr.open(method.toUpperCase(), url, this.options.async);

        
//监听请求的响应状态更改事件
        this.xhr.onreadystatechange = this.onStateChange.bind(this);

        
//设置所有HTTP头部信息
        this.headers.each(function(value, key)    {
            
if (!$try(function()    {
                
this.xhr.setRequestHeader(key, value);
                
return true;
                
//出错时触发异常事件
            }.bind(this))) this.fireEvent('onException', [key, value]);
        }, 
this);

        
//触发onRequest事件
        this.fireEvent('onRequest');
        
//发送数据
        this.xhr.send(data);
        
//如果是同步执行,不需要事件监听,直接进到状态处理方法
        if (!this.options.async)    this.onStateChange();
        
return this;
    },

    
//取消请求
    cancel: function()    {
        
//如果没有正运行的线程,不必操作,直接返回
        if (!this.running)    return this;
        
//设置标记
        this.running = false;
        
//中止请求
        this.xhr.abort();
        
//置状态改变监听为空
        this.xhr.onreadystatechange = $empty;
        
//创建新请求线程
        this.xhr = new Browser.Request();
        
//触发onCancel事件
        this.fireEvent('onCancel');
        
return this;
    }
});

//就地执行的匿名函数
(function()    {
    
//HTTP的方法处理
    var methods = {};
    
//处理用于Request继承实现的方法
    ['get''post''GET''POST''PUT''DELETE'].each(function(method)    {
        methods[method] 
= function()    {
            
//优先处理参数中字符类型的为请求地址,另一个有定义的参数为提交数据
            var params = Array.link(arguments, {url: String.type, data: $defined});
            
//最终仍需要通过调用send方法执行
            return this.send($extend(params, {method: method.toLowerCase()}));
        };
    });
    
/*
    扩展实现,现在可以使用Request.get,Request.put之类的方法了,并且方法名不区分大小写,如:
    Request.get('http://wfsr.net/action.aspx', 'id=1');
    
*/
    Request.implement(methods);
})();

//单件模式,根据Request为Element实现的扩展
Element.Properties.send = {
    
//setter
    set: function(options)    {
        
//先从临时对象取实例
        var send = this.retrieve('send');
        
//如果存在,取消执行
        if (send) send.cancel();
        
//删除缓存的Request实例,缓存配置参数
        return this.eliminate('send').store('send:options', $extend({
            data: 
this, link: 'cancel', method: this.get('method'|| 'post', url: this.get('action')
        }, options));
    },

    
//getter
    get: function(options)    {
        
//如果提供了配置参数并且未存在实例
        if (options || !this.retrieve('send'))    {
            
//如果没有缓存配置参数,调用setter缓存
            if (options || !this.retrieve('send:options'))    this.set('send', options);
            
//根据缓存参数,缓存Request实例
            this.store('send'new Request(this.retrieve('send:options')));
        }
        
//返回缓存中的实例
        return this.retrieve('send');
    }
};

//Element的send方法
Element.implement({
    
//Element可以直接执行send方法实现Ajax请求了
    send: function(url)    {
        
//读取缓存中的Request实例
        var sender = this.get('send');
        
//发送请求,Request的配置参数需要单独设置
        sender.send({data: this, url: url || sender.options.url});
        
return this;
    }
});

 

posted @ 2009-11-25 13:28  webgis松鼠  阅读(366)  评论(0编辑  收藏  举报