UNPKG

advanced

Version:

A simple MVC framework based on Express

310 lines (276 loc) 10 kB
var _ = require('lodash'), Promise = require('bluebird'), fs = Promise.promisifyAll(require('fs-extra')), request = require('request'), config = require('../config/advanced'); fs.existsAsync = function(path) { return new Promise(function(resolve) { fs.exists(path, resolve); }); }; var Utils = module.exports = { /** * get/set config * @param key * @param value * @returns {*} */ c: function(key, value) { // 获取所有数据 if (!arguments.length) { return _.mapValues(config, function(value, key) { return Utils.c(key); }); } if (_.isString(key)) { if (!~key.indexOf('.')) { // 一维object if (arguments.length === 1) { // 一维object获取数据 if (_.isString(config[key])) { return config[key].replace(/{(.*?)}/g, function (nouse, name) { return Utils.c(name); }); } else if (_.isPlainObject(config[key])) { return _.mapValues(config[key], function(_value, _key) { return Utils.c(key + '.' + _key); }); } else { return config[key]; } } else { // 一维object设置数据 if (_.isPlainObject(value) && _.isPlainObject(config[key])) { // 都为object则merge config[key] = _.extend(config[key], value); } else { config[key] = value; } } } else { // 二维object key = key.split('.'); if (arguments.length === 1) { // 二维object获取数据 if (config[key[0]] && config[key[0]][key[1]]) { var temp = config[key[0]][key[1]]; if (_.isString(temp)) { return temp.replace(/{(.*?)}/g, function (nouse, name) { return Utils.c(name); }); } else { return temp; } } else { return undefined; } } else { // 二维object设置数据 if (!_.isPlainObject(config[key[0]])) { config[key[0]] = {}; } config[key[0]][key[1]] = value; } } } else if (_.isPlainObject(key)) { // 批量赋值 return _.extend(config, key); } }, /** * 继承 * @param Parent * @param prototype * @returns {Function} */ inherit: function(Parent, prototype) { var Child = function() { Parent.apply(this, arguments); }; Child.prototype = Object.create(Parent.prototype); _.each(prototype, function(proto, name) { if (!_.isFunction(proto)) { return Child.prototype[name] = proto; } Child.prototype[name] = (function() { var _super = function() { return Parent.prototype[name].apply(this, arguments); }, _superApply = function(args) { return Parent.prototype[name].apply(this, args); }; return function() { var __super = this._super, __superApply = this.__superApply; this._super = _super; this._superApply = _superApply; var returnValue = proto.apply(this, arguments); this._super = __super; this._superApply = __superApply; return returnValue; }; })(); }); Child.__super = Parent.prototype; Child.prototype.constructor = Child; _.extend(Child, Parent); return Child; }, fs: fs, /** * 用‘controller@method’形式的字符串,返回一个方法 * @param path * @returns {Function} */ getFnByString: function(path) { return function(req, res, next) { Utils.runControllerByString(path, req, res, next); }; }, /** * 执行一个controller的相应action * @param path * @param action * @param param * @param req * @param res * @param next * @returns {boolean} */ runController: function(path, action, param, req, res, next) { path = Utils.c('controllerPath') + '/' + path; if (fs.existsSync(path + '.js')) { var Ctrl = require(path), ctrl = new Ctrl(req, res, next); if (typeof ctrl[action] === 'function') { if (param != undefined) { req.params[0] = param; } ctrl[action](); return true; } } return false; }, /** * 通过一个字符串的形式,调用action * @param str {String} 类似'controller@action' * @param req * @param res * @param next */ runControllerByString: function(str, req, res, next) { var results = []; if (typeof str === 'string') { results = str.split('@'); } results[0] || (results[0] = Utils.c('defaultController')); results[1] || (results[1] = Utils.c('defaultAction')); if (!Utils.runController(results[0], results[1], null, req, res, next)) { throw new Error('Run controller error: Controller => ' + results[0] + ', Action => ' + results[1]); } }, /** * 是否是开发模式 * @returns {boolean} */ isDev: function() { return Utils.c('env') === 'development'; }, request: function(options, req, res) { var Logger = require('./logger'), logger = Logger('request'), errorLogger = Logger('error'); req || (req = {}); options = requestOptions(options, req); // only set form when options.body is undefined if (!options.body && !options.form) { options.form = req.body; } var t1 = new Date().getTime(); return new Promise(function(resolve, reject) { request(options, function(error, response, body) { if (error) { reject(error); } else if (response.statusCode >= 200 && response.statusCode < 300 || response.statusCode === 304) { var cookie; if (res && (cookie = response.caseless.get('Set-Cookie'))) { res.append('Set-Cookie', cookie); } resolve(body); } else { if (res) res.status(response.statusCode); reject(body); } }); }).then(function(body) { logger.log({request: options.uri, type: 'request', options: options, response: body, time: (new Date().getTime() - t1) + 'ms'}); return body; }, function(err) { var info = {request: options.uri, type: 'request', options: options, error: err, time: (new Date().getTime() - t1) + 'ms'}; logger.log(info); errorLogger.error(info); throw err; }); }, proxy: function(req, res, next, options) { var Logger = require('./logger'), logger = Logger('request'), errorLogger = Logger('error'); options = requestOptions(options, req); if (req.is('application/x-www-form-urlencoded')) { options.form = req.body; } else if (req.is('application/json')) { options.body = req.body; } else if (req.is('multipart/form-data')) { options.formData = req.body; } else { // warning } var t1 = new Date().getTime(); var _req = request(options); // 去掉transfer-encoding头,在nginx代理中,会再加一次该头,导致浏览器解析错误 // _req.pipefilter = function(response, dest) { // dest.removeHeader('Transfer-Encoding'); // }; _req.on('error', function(err) { errorLogger.log({request: options.uri, type: 'proxy', options: options, error: err, time: (new Date().getTime() - t1) + 'ms'}); next(err); }).pipe(res); var responseBody = ''; _req.on('data', function(response) { if (response) { responseBody += response.toString('utf-8'); } }); _req.on('end', function() { logger.log({request: options.uri, type: 'proxy', options: options, response: responseBody, time: (new Date().getTime() - t1) + 'ms'}); }); return _req; } }; function requestOptions(options, req) { options || (options = {}); if (_.isString(options)) { options = {uri: options}; } options.headers = _.extend(_.omit(req.headers, 'connection', 'content-length', 'host'), { 'X-Forwarded-By': 'Advanced' }, options.headers); options = _.extend({ uri: req.path, method: req.method, json: true, qs: req.query, timeout: Utils.c('timeout'), gzip: true }, options); if (options.uri.indexOf('://') < 0) { if (!options.baseUrl) { options.baseUrl = Utils.c('apis.defaults'); } } else { options.baseUrl = ''; } return options; }