[转帖]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(this, this.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;
}
});
//继承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(this, this.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;
}
});