/**
* Created with IntelliJ IDEA.
* User: seeker910
* Date: 13-11-1
* Time: 上午3:45
* To change this template use File | Settings | File Templates.
*/
/**
* @memberof Rsd.data
* */
Rsd.define('Rsd.data.Ajax', {
extend:'Rsd.common.Object',
xtype:'ajax',
method: 'POST',
url: null,
/*
* 'text/plain'
* 'application/json; charset=utf-8':以json字符串形式发送,格式如:userid=admin&pwd=654321 需要服务端JSON.parse 还原JSON
* 'application/x-www-form-urlencoded;charset=UTF-8':以formData 格式发送,将表单内的数据转换为键值对,如:name=jack&age=18
* 'multipart/form-data':既可以上传文件等二进制数据,也可以上传表单键值对,只是最后会转化为一条信息,使用boundary=xxxxxxx分割
* */
contentType: 'application/x-www-form-urlencoded;charset=UTF-8',
/**
* 预期服务器返回的数据类型:text,json
*/
dataType: 'json',
async: true,
/**
* 超时时间 毫秒
*/
timeout:10000,
username: '',
password: '',
/**
* 是否是跨域请求
* */
crossDomain:true,
/**
* @description Ajax请求客户端标识,作用域:每个客户端, 添加到header中
* */
appId:'',
/**
* @description Ajax请求身份标识,作用域:每次请求, 添加到header中
* * */
token:'',
/**
* @description 授权码
*/
authCode:'',
/**
* @description 返回数据json格式:web:首字母小写
*/
jsonFormatter:null,
/**
* @constructs Rsd.data.Ajax
* @classdesc Ajax请求类
*
* @property {string} extend Rsd.common.Object
* @property {string} xtype ajax
* @property {EventList} events 事件列表
* @property {string} method GET,POST,PUT,DELETE
* @property {string} url
* @property {string} contentType application/x-www-form-urlencoded; charset=UTF-8
* @property {string} dataType text,json
* @property {boolean} async false
* @property {string} username
* @property {string} password
* @property {string} appId
* @property {string} token
* @property {string} authCode
*
* @param {object} config 自定义配置项
* */
constructor: function Ajax (config) {
config = config || {};
this.apply(config);
},
/**
* @description 事件注册
* @public
* @function
* @memberof Rsd.data.Ajax
* */
on: function on(name, fn) {
if (!name || name == '') {
throw new Error('argument \'name\' is null.');
}
if (!fn || typeof (fn) != 'function') {
throw new Error('argument \'fn\' is not a function object.');
}
//var me = this;
this.events.add(this, name, fn);
},
/**
*
*/
statechange:function statechange(xhr, evt)
{
/*
xhr.readyState =(0,1,2,3,4)
0:请求未初始化,还没有调用 open()。
1:请求已经建立,但是还没有发送,还没有调用 send()。
2:请求已发送,正在处理中(通常现在可以从响应中获取内容头)。
3:请求在处理中;通常响应中已有部分数据可用了,没有全部完成。
4:响应已完成;您可以获取并使用服务器的响应了。
*/
Rsd.debug('statechange: ['+this.url+']' + ' | readyState:' + xhr.readyState + ' | status:' + xhr.status);
},
/**
* @public
* @function
* @memberof Rsd.data.Ajax
* */
complete:function complete(xhr, evt)
{
Rsd.debug('request complete:['+this.url+']');
},
/**
* @public
* @function
* @memberof Rsd.data.Ajax
* @param {XmlHttpRequest} xhr
* @param {string} error
* */
error:function error(xhr, evt){
Rsd.debug('request error:['+this.url+'] (' + (xhr.textStatus||xhr.status ||'') + ')');
if(xhr.status == 404)
{
Rsd.showModalDialog(this.url,'服务器资源不存在(404)',400,500);
return false;
}
if(xhr.status == 405)
{
Rsd.alert("Http请求不允许使用方法-"+this.method+"(405)","请检查该Ajax请求Method属性的设置\r\n" + this.url + "\r\n");
return false;
}
console.error('Ajax请求失败('+(xhr.textStatus||xhr.status ||'unknown code')+'):',this.url,evt);
},
/**
* @description 请求成功(status == 200) 处理方法,仅在 request方法未指定callback参数时有效
* @public
* @function
* @memberof Rsd.data.Ajax
* */
success:function success(xhr) {
Rsd.debug('request success:[' + this.url + ']');
},
/**
* @public
* @function
* @memberof Rsd.data.Ajax
* @description http请求,返回json结构数据
* @param {object} data 作为Ajax对象请求时的参数中data属性(即远程方法的参数)
* @param {string|function} callback 回调函数,callback方法和对象success方法只有一个有效,优先使用callback。callback需求每次请求时指定,success对象属于,不需要每次请求指定
* */
request:function request(data,callback) {
var me = this;
var _c = this.getAjaxConfig();
if(Rsd.isEmpty(_c.url))
{
console.error(_c);
throw new Error('Ajax object config url value is null.');
}
_c.data = data;
_c.xhrFields={
withCredentials:_c.crossDomain==false
};
//beforeSend 在xhr.open 之后 ,xhr.send 之前 执行
_c.beforeSend = function(xhr)
{
//框架可扩展部分
//xhr.withCredentials = true; //指示跨域时是否包含 cookies
//setRequestHeader(name,value)添加http 头,必须在 xhr.open 之后 , 在跨域请求时 {name}需要在后端设置允许跨域设置
if(_c.contentType && _c.contentType != "multipart/form-data")
{
xhr.setRequestHeader("Content-type", _c.contentType);//不设置默认值,以免影响文件上传
}
xhr.setRequestHeader("accept-language",window.navigator.language);
if(_c.token)
{
xhr.setRequestHeader('token', _c.token);
}
if(_c.appId)
{
xhr.setRequestHeader('appId', _c.appId);
}
if(_c.authCode)
{
xhr.setRequestHeader('authorization', _c.authCode);
}
if(_c.jsonFormatter)
{
xhr.setRequestHeader('x-json-formatter', _c.jsonFormatter);
}
if(_c.accept)
{
xhr.setRequestHeader('accept', _c.accept);
}
//设置其他header信息
}
if(callback)
{
//callback 替换了 ajax对象中的success
_c.success = function(response){
var _data = response;
if(Rsd.isFunction(callback))
{
return callback.call(me,_data);
}
if(Rsd.isString(callback))
{
return me.funApplyByIOC(callback,[_data]);
}
console.warn('callback is not functon',callback);
};
}
if(Rsd.app && Rsd.app.appType=='wxapp' && wx && wx.request)
{
return wx.request(_c);
}
var type = _c.method.toUpperCase();
// 发送GET请求
if(type == 'GET'){
this.get();
return;
}
// 发送 POST请求
if(type == 'POST' || type == 'PUT' || type == 'PATCH' || type == 'DELETE'){
this.post();
return;
}
throw new Error("Unkown Method: " + _c.method);
},
/**
*
*/
get:function get()
{
var _c = this.getAjaxConfig() ||{};
var xhr = this.getXhr();
// 用于清除缓存
var random = Math.random();
if(Rsd.isEmpty(_c.data)){
xhr.open('GET', _c.url + (_c.url.endWith('.js') ?('?t='+ random):''), _c.async == undefined ? true : _c.async);
}
else
{
var _data = "";
_data = this.toFormDataString(_c.data);
xhr.open('GET', _c.url + (_c.url.indexOf('?')<0?'?':'&') + _data, _c.async == undefined ? true : _c.async);
}
// 在 open 之后 ,处理 beforeSend
_c.beforeSend.call(xhr.ajax,xhr);
//设置超时时间
xhr.timeout = (_c.timeout == null || _c.timeout == undefined) ? 10000 : _c.timeout;
//注册异步处理事件
if(_c.async == undefined||_c.async) {
//处理返回数据
xhr.onreadystatechange = function(evt){
/*
xhr.readyState =(0,1,2,3,4)
0:请求未初始化,还没有调用 open()。
1:请求已经建立,但是还没有发送,还没有调用 send()。
2:请求已发送,正在处理中(通常现在可以从响应中获取内容头)。
3:请求在处理中;通常响应中已有部分数据可用了,没有全部完成。
4:响应已完成;您可以获取并使用服务器的响应了。
*/
if(xhr.readyState == 4){
//处理同步返回结果
_c.response.call(xhr.ajax,xhr);
}
//跟踪状态
if (Rsd.isFunction(_c.statechange)) {
_c.statechange.call(xhr.ajax,xhr, evt);
}
}
}
try
{
xhr.send();
}
catch(err)
{
_c.error.call(xhr.ajax,xhr,err);
}
//处理返回
if(_c.async == undefined||_c.async)
{
//异步 ,不做处理,通过onreadystatechange事件处理
}
else
{
//处理同步返回结果
_c.response.call(xhr.ajax,xhr);
}
},
/**
*
* @param {*} data
* @param {*} callback
*/
post:function post()
{
var _c = this.getAjaxConfig() ||{};
var xhr = this.getXhr();
var type = _c.method;
xhr.open(type, _c.url, _c.async == undefined ? true : _c.async);
//在 open 之后 ,处理 beforeSend
_c.beforeSend(xhr);
var _data = _c.data;
//以json字符串形式发送,格式如:userid=admin&pwd=654321, 需要服务端JSON.parse 还原JSON
if(_c.contentType == "application/json")
{
_data = this.toJsonString(_c.data);
}
//以formData 格式发送,将表单内的数据转换为键值对,如:name=jack&age=18
if(_c.contentType == "application/x-www-form-urlencoded")
{
if(!Rsd.isType(_c.data, FormData))
{
//form 提交
_data = this.toFormDataString(_c.data);
}
}
//既可以上传文件等二进制数据,也可以上传表单键值对,最后会转化为一条信息
if(_c.contentType == "multipart/form-data")
{
if(!Rsd.isType(_c.data, FormData))
{
_data = this.toMultipartFormData(_c.data);//json 对象转化为FormData
}
}
//设置超时时间
xhr.timeout = (_c.timeout == null || _c.timeout == undefined) ? 10000 : _c.timeout;
//注册异步处理事件
if(_c.async == undefined||_c.async) {
//console.log( xhr.timeout);
//处理返回数据
xhr.onreadystatechange = function(evt){
/*
xhr.readyState =(0,1,2,3,4)
0:请求未初始化,还没有调用 open()。
1:请求已经建立,但是还没有发送,还没有调用 send()。
2:请求已发送,正在处理中(通常现在可以从响应中获取内容头)。
3:请求在处理中;通常响应中已有部分数据可用了,没有全部完成。
4:响应已完成;您可以获取并使用服务器的响应了。
*/
if(xhr.readyState == 4){
//处理同步返回结果
_c.response.call(xhr.ajax,xhr);
}
//跟踪状态
if (Rsd.isFunction(_c.statechange)) {
_c.statechange.call(xhr.ajax,xhr,evt);
}
}
}
try
{
xhr.send(_data);
}
catch(err)
{
_c.error.call(xhr.ajax,xhr, err);
}
//处理返回
if(_c.async == undefined||_c.async)
{
//异步 ,不做处理,通过onreadystatechange事件处理
}
else
{
//处理同步返回结果
_c.response.call(xhr.ajax,xhr);
}
},
/**
* 处理 返回结果
* @param {*} xhr
* @returns
*/
response:function response(xhr)
{
var _c = this.getAjaxConfig() ||{};
//返回结果
var str = xhr.responseText;
/*
* xhr.status
* 200——成功
* 201——提示知道新文件的URL
* 300——请求的资源可在多处得到
* 301——删除请求数据
* 404——没有发现文件、查询或URl
* 500——服务器产生内部错误
* */
if(xhr.status == 200)
{
var data=str;
if(_c.dataType == 'json')
{
try
{
data = Rsd.toJson(str);
}
catch (e)
{
}
}
if (Rsd.isFunction(_c.success))//指请求成功,未发生异常:404,timeout
{
_c.success.call(xhr.ajax,data);
}
return;
}
if(xhr.status >= 500) //服务端异常
{
if (Rsd.isFunction(_c.error)) {
_c.error.call(xhr.ajax,xhr, e);
}
return;
}
console.error(xhr.status,str);
},
/**
*
* @returns
*/
getXhr:function getXhr()
{
var _c = this.getAjaxConfig() ||{type:'GET', url:'', data:null, success:null, failed:null};
_c.method = _c.method||_c.type|| this.method ||'GET';
// 创建ajax对象
var xhr = null;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP')
}
//可注册事件
// loadstart:在接收到响应数据的第一个字节时触发
// progress:在接收响应期间持续不断地触发
// error:在请求发生错误时触发
// abort:在因为调用abort()方法而终止连接时触发
// load:在接收到完整的响应数据时触发
// loadend:在通信完成或者触发error、abort或load事件后触发
// timeout:超时发生时触发
// onreadystatechange
xhr.ajax = this;
//注册跟踪事件 对接外部事件注册
xhr.addEventListener('loadstart', _c.handleEvent);
xhr.addEventListener('load', _c.handleEvent);
xhr.addEventListener('loadend', _c.handleEvent);
xhr.addEventListener('error', _c.handleEvent);
xhr.addEventListener('abort', _c.handleEvent);
xhr.addEventListener('timeout', _c.handleEvent);
//下载进度
xhr.addEventListener('progress', _c.handleEvent);
//注册跟踪上传事件 对接外部事件注册
if(xhr.upload)
{
xhr.upload.ajax = this;
//上传开始
xhr.upload.addEventListener('loadstart', _c.handleUploadEvent);
//上传成功
xhr.upload.addEventListener('load', _c.handleUploadEvent);
//上传已经停止
xhr.upload.addEventListener('loadend', _c.handleUploadEvent);
//上传失败
xhr.upload.addEventListener('error', _c.handleUploadEvent);
//上传终止
xhr.upload.addEventListener('abort', _c.handleUploadEvent);
//由于预设时间到期,上传终止
xhr.upload.addEventListener('timeout', _c.handleUploadEvent);
//上传进度
xhr.upload.addEventListener("progress" , _c.handleUploadEvent , false);
}
// xhr.onabort
// xhr.onload
//
// xhr.onloadstart
// xhr.onprogress
//在请求中注册
// xhr.onreadystatechange
//完成
xhr.onloadend = function(evt)
{
if (Rsd.isFunction(_c.complete))
{
_c.complete(xhr,xhr.ajax.url,evt);
}else
{
console.error('complete',xhr.ajax.url,evt);
}
};
//超时
xhr.ontimeout = function (evt) {
//框架可扩展部分 timeout
if (Rsd.isFunction(_c.timeout))
{
_c.timeout(xhr,evt);
}else
{
console.error('timeout',xhr.ajax.url,evt);
}
};
//错误
xhr.onerror = function (evt) {
if (Rsd.isFunction(_c.error)) {
_c.error(xhr, evt);
}
else
{
console.error('error',xhr.ajax.url,evt);
}
};
return xhr;
},
/**
* @public
* @function
* @description 获取当前ajax的配置项
* @memberof Rsd.data.Ajax
* */
getAjaxConfig:function getAjaxConfig() {
if(this.__cache)
{
return this.__cache;
}
//当前对象的克隆版
var _c = {};
var _keys = [
'appId',
'token',
'authCode',
'jsonFormatter',
'method',
'url',
'accept',
'contentType',
'dataType',
'data',
'async',
'username',
'password',
'statechange',
'complete',
'error',
'success',
'timeout',
'response',
'handleEvent',
'handleUploadEvent'
];
for(var i in _keys)
{
if(_keys.hasOwnProperty(i))
{
if(i == "data")
{
_c[_keys[i]] = this[_keys[i]];
continue;
}
_c[_keys[i]] = Rsd.clone(this[_keys[i]]);
}
}
if(!Rsd.isString(_c.url))
{
console.error('args url is not string value.');
console.trace(_c);
return _c;
}
/*
if(this.key && _c.url && !_c.url.toLowerCase().startWith(Rsd.getRedjsHost().toLowerCase()))
{
_c.url = _c.url + (_c.url.indexOf('?')> 0? '&':'?') + '___key=' + this.key;
}
*/
var _url = _c.url;
if(_url.startWith('http://') || _url.startWith('https://')||_url.startWith('file://'))
{}
else
{
//只处理相对路径
_url = (Rsd.app&&Rsd.app.jsAgentHost)?(Rsd.app.jsAgentHost+'?' + Rsd.utf8ToBase64(_url)):_url;
_c.url = _url;
}
//console.log(_c);
//缓存
this.__cache = _c;
return this.__cache;
},
/**
* 重置config ,删除缓存
*/
resetAjaxConfig:function resetAjaxConfig()
{
delete this["__cache"];
},
/**
* @decription 对接所有请求(不包括上传事件)事件,使用时通过on(name,fn)注册方式 各事件响应
* @param {*} evt
*/
handleEvent:function handleEvent(evt)
{
var me = this.ajax;
me.events.fire(me,evt.type,[evt]);
if(Rsd.isDebug)
{
console.debug('['+evt.type+']' + evt.currentTarget.responseURL,evt);
}
},
/**
* @decription 对接所有上传事件,使用时通过on(name,fn)注册方式 各事件响应 ,name为'upload'+事件名
* @param {*} evt
*/
handleUploadEvent:function handleUploadEvent(evt)
{
var me = this.ajax;
me.events.fire(me,'upload' + evt.type,[evt]);
if(Rsd.isDebug)
{
console.debug('上传['+evt.type+']'+ evt.currentTarget.responseURL,evt.type,evt);
}
},
/**
* 以json字符串形式发送,格式如:userid=admin&pwd=654321, 需要服务端JSON.parse 还原JSON
* @param {*} data
* @returns
*/
toJsonString:function toJsonString(data)
{
return Rsd.toString(data);
},
/**
* 组织 form 属性 EncType(content-type)为 application/x-www-form-urlencoded 提交数据
* 以formData 格式发送,将表单内的数据转换为键值对,如:name=jack&age=18
* @param {*} obj
* @param {*} pre
*/
toFormDataString:function toFormDataString(obj,pre) {
var _pre = pre || '';
if (obj == undefined || obj==null) {
return '';
}
if (typeof obj == "string") {
if(_pre)
{
return _pre +"=" + obj.replace(/([\"\\])/g, "\\$1").replace(/(\n)/g, "\\n").replace(/(\r)/g, "\\r").replace(/(\t)/g, "\\t") + "";
}
else
{
return "" + obj.replace(/([\"\\])/g, "\\$1").replace(/(\n)/g, "\\n").replace(/(\r)/g, "\\r").replace(/(\t)/g, "\\t") + "";
}
}
if (obj instanceof Array) {
var r = [];
for (var i = 0; i < obj.length; i++) {
if (typeof (obj[i]) == 'function') {
continue;
}
if (typeof obj[i] == 'object') {
var _t = this.toFormDataString(obj[i], _pre + '[' + i + ']');
Array.prototype.push.apply(r, _t);
}
else {
r.push(this.toFormDataString(obj[i], _pre + '[' + i + ']'))
}
if (!_pre) {
r = r.join('&');
}
}
//非对象类型的数组 没有处理为:[1,24,5] 格式
return r;
}
if (typeof obj == "object") {
var r = [];
for (var k in obj) {
if (typeof (obj[k]) == 'function') {
continue;
}
if ( obj[k]==null || typeof obj[k] == "string") {
if(_pre)
{
r.push(_pre + '[' + k + ']' + "=" + this.toFormDataString(obj[k]));
}else
{
r.push(k + "=" + this.toFormDataString(obj[k]));
}
continue;
}
if(typeof obj[k] == 'object' )
{
if(_pre)
{
var _t = this.toFormDataString(obj[k],_pre + '[' + k + ']');
Array.prototype.push.apply(r , _t);
}else
{
var _t = this.toFormDataString(obj[k], k);
Array.prototype.push.apply(r , _t);
}
}
else
{
if(_pre)
{
r.push(_pre + '[' + k + ']' + "=" + this.toFormDataString(obj[k]));
}else
{
r.push(k + "=" + this.toFormDataString(obj[k]));
}
}
}
if(!_pre)
{
r = r.join('&');
}
//console.log(r);
return r;
}
return obj.toString().replace(/\"\:/g, '":""');
},
/**
* 将json 数据转化为FormData
* multipart/form-data
* 既可以上传文件等二进制数据,也可以上传表单键值对,最后会转化为一条信息
*/
toMultipartFormData:function toMultipartFormData(data)
{
var _data = data||{};
var formData = new FormData();
//debugger;
for(var i in _data)
{
var _d = _data[i];
if(Rsd.isArray(_d))
{
for(var j in _d)
{
formData.append(i + '['+j+']' , _d[j]);console.log(i+ '['+j+']',_d[j]);
}
continue;
}
if(Rsd.isType(_d,File))
{
formData.append(i,_d,_d.name);
continue;
}
formData.append(i,_d);
}
return formData;
}
},function (type) {
if (!Rsd.data.Ajax.prototype.hasOwnProperty("events")) {
var _eventsGetter = function () {
var _me = this;
if (_me.__events == null) {
_me.__events = Rsd.create('Rsd.common.EventList', {});
}
return _me.__events;
};
this.defineProperty(type,"events", _eventsGetter, null,true);
}
});