UNPKG

@zibu/common-mina

Version:

微信小程序通用逻辑功能模块。包含AjaxService、clone、dataFormat

318 lines (289 loc) 12.5 kB
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;