UNPKG

bigape

Version:

an bigpipe inpired node structure based on express

371 lines (326 loc) 8.1 kB
/** * @desc: Service 基本数据服务 * @authors: Yex * @date: 2016-08-03 21:08:11 */ var wrapRequest = require('./wrapRequest'); var config = require('./config'); var monitor = config.plugins('monitor'); //require('@qnpm/q-monitor'); var debug = require('debug'); //require('@qnpm/q-logger'); var logger = debug('bigape'); var _ = require('lodash'); module.exports = { /** * 请求地址 */ url: '', /** * 请求参数 */ params: '', /** * 监控名称 */ monitor: '', /** * 接口请求失败后的重试次数, 如果不为0, 后端接口返回异常会重新请求接口 */ retryTimes: 0, /** * 接口请求方式,默认是GET * @type {String} */ method: 'GET', /** * 超时时间 * @type {Number} */ timeout: 30000, // 接口代理地址 proxySet: null, // 为所有的service统一设置proxy代理 $setProxy: function(proxy) { if (!proxy) return; this.proxySet = proxy; }, // service 原型统一开关 $setGlobal: function(key, value) { if (!key || typeof value === 'undefined') { return; } this[key] = value; }, /** * 接口请求前校验,如果失败则停止请求 * @param {Object} req request * @param {Object} res response * @return {Boolean} if it checked */ queryValid: function() { return true; }, /** * 接口请求前校验,如果失败则停止请求 * @param {Object} req request * @param {Object} res response * @return {Boolean} if it checked */ beforeQueryValid: function() { return true; }, /** * 初始化 */ init: function() { return this; }, /** * 获取参数 * @param req * @param res * @returns {string} */ getParams: function(req) { if (this.params) { return this.params; } if (!_.isEmpty(req.query)) { return req.query; } if (!_.isEmpty(req.body)) { return req.body; } return {}; }, /** * 获取请求url * @param req * @param res * @returns {string} */ getURL: function() { return this.url; }, /** * 获取监控名称 * @param req * @param res * @returns {string} */ getMonitor: function() { return this.qmonitor; }, load: function(req, res) { var self = this; var beforeLoadValid = this.queryValid(req, res); // 有异步校验 if (this.isPromise(beforeLoadValid)) { logger('进行请求前校验'); return beforeLoadValid .then(function() { return self._load(req, res); }) .catch(function(error) { logger('请求接口前校验失败或数据异常', error); throw error; }); } if (beforeLoadValid) { return this._load(req, res); } throw beforeLoadValid; }, // 接口代理 proxy: function(req, res, options) { options = _.pick(options || {}, [ 'url', 'params', 'method', 'timeout', 'validData', 'qmonitor', 'retryTimes' ]); _.extend(this, options); return this.json(req, res); }, json: function(req, res) { var self = this; this.load(req, res) .then(function(data) { data = self.parse(data); res.json(data); }) .catch(function(err) { res.json(self._getErrorJson(err)); }); }, /** * 从后端加载数据 * @param req * @param res * @returns {*} */ _load: function(req, res) { var self = this; return wrapRequest(this.getURL(req, res), { data: this.getParams(req, res), qmonitor: this.getMonitor(req, res), method: this.method, postType: 'json', proxy: this.proxySet, //'http://127.0.0.1:8888', timeout: this.timeout || 30000, headers: this.getHeaders(req, res), jsonValid: function(data) { return self.validData(data, req, res); } }).then( function(json) { return self._preParse(json); }, function(json) { return self.reload(req, res, json); } ); }, /** * request headers * @param {Object} req request * @param {Object} res response * @return {Object} headers set */ getHeaders: function(req) { var cookies = req.cookies, headers = req.headers; var sourceObj; try { sourceObj = JSON.parse(cookies['_plat_source']); } catch (e) { sourceObj = { value: 'jiulvxing' }; } return { uid: cookies['_uc_uid'], channel: sourceObj.value || 'jiulvxing', ip: (headers['x-real-ip'] || headers['ip'] || '').replace('::ffff:', ''), frontHeadExt: cookies['_frontHeadExt'], openDomain: req.hostname, plat: 'touch', dist: cookies._plat_dist || 'h5' //分发渠道 }; }, // 数据格式预处理 _preParse: function(json) { var bstatus = json.bstatus; if (bstatus) { return { status: bstatus.code, message: bstatus.des || 'success', des: bstatus.info || '', data: json.data }; } return { status: json.status || json.code || (json.ret ? 0 : -1) || 0, message: json.message || json.des || json.msg || 'success', data: json.data }; }, reload: function(req, res, json) { var cache = res.locals, retryTimes = this.retryTimes, retriedTimes = cache.retriedTimes || 0; var qmonitorKey = this.getMonitor(req, res); // logger('重试次数: ', retryTimes, '已重试次数: ', retriedTimes); if (qmonitorKey) { monitor.addCount(qmonitorKey + '-api-error'); } if (retryTimes > 0 && retriedTimes < retryTimes) { cache.retriedTimes = retriedTimes + 1; // console.log('重新请求接口'); if (qmonitorKey) { monitor.addCount(qmonitorKey + '-api-retry'); } return this.load(req, res); } else { throw this._getErrorJson(json); } }, /** * 直接返回json数据 * @param {Object} req request * @param {Object} res response * @param {Function} next next */ process: function(req, res) { var self = this; this.load(req, res) .then(function(data) { data = self.parse(data); res.json(data); }) .catch(function(err) { res.json(self._getErrorJson(err)); }); }, /** * 数据处理,process 时候才会调用,否则不对数据做处理 * @param {Object} data 原始数据 * @return {Object} 处理之后的数据 */ parse: function(data) { return data; }, /** * 获错误的返回数据 * @param {Object} error error Object * @return {Object} error json */ _getErrorJson: function(error) { if (error.bstatus) { error = { code: error.bstatus.code, message: error.bstatus.des }; } var json = { status: error.code || error.status || 500, message: error.message || '未获取到数据,请稍后重试', data: error.data || null }; error.des ? (json.des = error.des) : null; return json; }, create: function(props) { var obj = Object.create(this); props && _.extend(obj, props); return obj; }, /** * 自定义 request 响应数据校验 * @param {Object} data 返回数据 * @param {Object} req request * @param {Object} res response * @return {Boolean} flag */ validData: function(data) { // success ret var ret = (typeof data.status !== 'undefined' && data.status == 0) || (typeof data.code !== 'undefined' && /^\d+$/.test(data.code) && data.code == 0) || (typeof data.ret !== 'undefined' && data.ret === true) || (typeof data.bstatus !== 'undefined' && data.bstatus.code == 0); // Object.keys(data).length !== 0; return ret; }, /** * isPromise - 判断是否为`Promise`对象 * * @param {type} obj description * @return {type} description */ isPromise: function(obj) { return typeof obj.then === 'function'; } };