@zibu/common-mina
Version:
微信小程序通用逻辑功能模块。包含AjaxService、clone、dataFormat
318 lines (289 loc) • 12.5 kB
JavaScript
import * as Enum from './Enum';
import HttpRequest from './HttpRequest';
const _host = typeof __HOST__ === 'string' ? __HOST__ : '';
/**
* 定义AjaxService类
*/
class AjaxService {
/**
* AjaxService类构造函数
* @param {String} url RESTful格式的请求地址(默认值:__HOST__ + '{/api}{/module}{/action}{/id}')
* @param {String} contentType 发送形式,默认form表单(json、form、formData)
* @param {String} host 请求服务器域名
* @param {Boolean} isReturnWholeResponse 是否返回完整的response,默认只返回body(默认:false)
*/
constructor(url, contentType = 'form', host = _host, isReturnWholeResponse = false) {
this.requestInstance = null; // HttpRequest实例
this.cancel = () => {}; // 想取消ajax请求时,调用此方法,只有在h5页面可以取消
this.originalUrl = url || host + '{/api}{/version}{/module}{/action}{/id}'; // +运算符的优先级要比||运算符优先级高
this.sections = []; // RESTful格式地址中动态部分
this.isReturnWholeResponse = isReturnWholeResponse;
this.init(contentType);
}
static ContentType = Enum.ContentType;
/**
* 初始化AjaxService
* @param {String} contentType 请求Header中的Content-Type
*/
init(contentType) {
const httpRequest = new HttpRequest(contentType);
this.requestInstance = httpRequest.request;
this.cancel = this.requestInstance.cancelFun;
// 设置Content-Type
this.setHeader('Content-Type', httpRequest.contentType);
// 注入request钩子函数
this.injectRequestHook(config => {
const parameter = config.data;
if (typeof parameter !== 'object' || !parameter) {
return config;
}
config.data = this.processRequestParam(parameter, config.header['Content-Type']);
return config;
});
// 注入response钩子函数
this.injectResponseHook(
response => {
return this.isReturnWholeResponse ? response : response.data;
},
error => {
if (HttpRequest.isCancel(error)) {
// 取消ajax请求
return {
type: 'cancel',
body: error
};
}
if (error.request) {
// ajax请求错误
return {
type: 'response',
body: error.request
};
}
if (error.response) {
// ajax响应错误
return {
type: 'response',
body: error.response
};
}
return {
type: 'other',
body: error
};
}
);
// 分解原始RESTful格式地址
let reg = /\{\/([^}]+)\}/g;
// 因为str.match方法返回的结果只有匹配的内容,没有每一个匹配中正则表达式小括号内的内容
// 所以使用reg.exec方法。
while (reg.lastIndex < this.originalUrl.length) {
let section = reg.exec(this.originalUrl);
if (!section) {
break;
}
this.sections.push(section);
}
// this.initHTTPMethod();
}
/**
* 设置指定请求类型附加的header,如果未指定类型,则默认全部请求都附加指定header
* @param {String} key header的key
* @param {String} value header的value
* @param {String} requestType 请求类型
*/
setHeader(key, value, requestType) {
if (key && value) {
requestType = requestType || 'common';
let header = this.requestInstance.defaults.headers[requestType];
header[key] = value;
}
}
/**
* 注入请求钩子函数
* @param {Function} hookFunction 请求钩子函数
*/
injectRequestHook(hookFunction) {
if (typeof hookFunction === 'function') {
this.requestInstance.interceptors.request.use(config => {
return hookFunction(config);
});
}
}
/**
* 处理请求参数
* @param {Object} parameter 请求参数
* @param {Enum<ContentType>} headersContentType headers中临时设置的Content-Type
* @returns {Object} 返回处理后的参数
*/
processRequestParam(parameter, headersContentType) {
const isFormDataType = parameter.constructor === FormData;
const entries = isFormDataType ? parameter.entries() : Object.entries(parameter);
const newParam = Array.isArray(parameter) ? [] : {};
const specialParams = {};
for (const [key, value] of entries) {
// 文件类型因为不是值类型会被序列化没了,所以先记录下来序列化完后,再还原回来
if (value && value.constructor === File) {
specialParams[key] = value;
} else {
newParam[key] = value;
}
}
// 利用JSON.stringify去掉undefined和function类型的值
parameter = JSON.parse(JSON.stringify(newParam));
// 还原特殊参数
Object.keys(specialParams).forEach(key => {
parameter[key] = specialParams[key];
});
/**
* 如果发送请求是FORM表单,Axios不会对FORM表单的请求参数进行转换,直接转换成FORM表单格式即可。
* 不是FORM表单的话,再判断原有参数类型是不是FormData,如果是,还需要将JSON转换成FormData
*/
if ((headersContentType || this.contentType) === ContentType.FORM) {
const params = [];
Object.keys(parameter).forEach(key => {
const value = parameter[key];
params.push(`${key}=${value}`);
});
parameter = params.join('&');
} else if (isFormDataType) {
const formDataParam = new FormData();
Object.keys(parameter).forEach(key => formDataParam.append(key, parameter[key]));
parameter = formDataParam;
}
return parameter;
}
/**
* 注入响应钩子函数
* @param {Function} resolveHook 成功钩子函数
* @param {Function} rejectHook 失败钩子函数
*/
injectResponseHook(resolveHook, rejectHook) {
this.requestInstance.interceptors.response.use(
response => {
if (typeof resolveHook === 'function') {
let returnValue = resolveHook(response);
// 如果未返回值,则表示Ajax的钩子函数不需要返回内容
if (typeof returnValue !== 'undefined') {
return returnValue;
}
} else {
return response;
}
},
error => {
if (typeof rejectHook === 'function') {
let returnValue = rejectHook(error);
// 如果未返回值,则表示Ajax的钩子函数不需要返回内容
if (typeof returnValue !== 'undefined') {
return returnValue;
}
} else {
return error;
}
}
);
}
/**
* 将方法写到外层的原因:原先的执行方案是在构造函数中,调用init方法,init方法里初始化完requestInstance之后,执行initHTTPMethod,给this对象附加http请求方法,实际上并不是在原型链上定义方法
* 产生的问题:继承类无法通过覆写的方式重写方法,只能通过在继承类的构造函数中覆盖this对象里附加的方法
* 解决方案:改成使用原型链的方式定义http方法
* 原先的代码
* initHTTPMethod() {
* ['post', 'delete', 'put', 'patch', 'get'].forEach(key => {
* if (typeof this.requestInstance[key] === 'function') {
* this[key] = (urlObj, param, config) => this.requestInstance[key](this.urlMatch(urlObj), param, config);
* }
* });
* }
*/
/**
* 发送post请求
* @param {Object} urlObj 请求地址对象
* @param {Object} param 请求参数
* @param {Object} config [请求配置项](https://github.com/axios/axios#instance-methods | https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html)
* @returns {Promise} 返回Promise对象
*/
post(urlObj, param, config) {
return this.execHttpRequest('post', urlObj, param, config);
}
/**
* 发送delete请求
* @param {Object} urlObj 请求地址对象
* @param {Object} param 请求参数
* @param {Object} config [请求配置项](https://github.com/axios/axios#instance-methods | https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html)
* @returns {Promise} 返回Promise对象
*/
delete(urlObj, param, config) {
return this.execHttpRequest('delete', urlObj, param, config);
}
/**
* 发送put请求
* @param {Object} urlObj 请求地址对象
* @param {Object} param 请求参数
* @param {Object} config [请求配置项](https://github.com/axios/axios#instance-methods | https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html)
* @returns {Promise} 返回Promise对象
*/
put(urlObj, param, config) {
return this.execHttpRequest('put', urlObj, param, config);
}
/**
* 发送patch请求
* @param {Object} urlObj 请求地址对象
* @param {Object} param 请求参数
* @param {Object} config [请求配置项](https://github.com/axios/axios#instance-methods | https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html)
* @returns {Promise} 返回Promise对象
*/
patch(urlObj, param, config) {
return this.execHttpRequest('patch', urlObj, param, config);
}
/**
* 发送get请求
* @param {Object} urlObj 请求地址对象
* @param {Object} param 请求参数
* @param {Object} config [请求配置项](https://github.com/axios/axios#instance-methods | https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html)
* @returns {Promise} 返回Promise对象
*/
get(urlObj, param, config) {
return this.execHttpRequest('get', urlObj, param, config);
}
/**
* 执行HTTP请求
* @param {String} methodName 请求方法名
* @param {Object} urlObj 请求地址对象
* @param {Object} param 请求参数
* @param {Object} config [请求配置项](https://github.com/axios/axios#instance-methods | https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html)
* @returns {Promise} 返回Promise对象
*/
execHttpRequest(methodName, urlObj, param, config) {
if (this.requestInstance && typeof this.requestInstance[methodName] === 'function') {
return this.requestInstance[methodName](this.urlMatch(urlObj), param, config);
}
throw new Error('AjaxService:请求的方法不存在');
}
/**
* 将请求地址对象匹配到原始请求地址上
* @param {Object} urlObj 请求地址对象
* @returns {String} 返回匹配后的地址
*/
urlMatch(urlObj) {
let url = this.originalUrl;
// 如果请求的方法里提供了host,则替换掉实例化时传递的服务器地址
if (urlObj.hasOwnProperty('host')) {
url = url.replace(/http[s]?:\/\/[^{]+/, urlObj.host);
}
this.sections.forEach(section => {
let key = section[1];
let value = urlObj.hasOwnProperty(key) ? urlObj[key] : this[key];
if (value) {
value = '/' + value;
} else {
value = '';
}
let reg = new RegExp(section[0]);
url = url.replace(reg, value);
});
return url;
}
}
export default AjaxService;