UNPKG

epi

Version:

A Api Service Framework Base On Express.js

512 lines (378 loc) 11.9 kB
/** * @desc 封装express创建服务的方法 * @author sam * @version 0.8.3 * */ var https = require('https'); var cluster = require('cluster'); var os = require('os'); var util = require('util'); var METHODS = require('./methods').METHODS; var EventEmitter = require('events').EventEmitter; var express = require('express'); /** * @desc 创建一个http服务程序,包括https和http服务 * @constructor HttpServer * @this {HttpServer} * @param {Object} options * @example * * *'use strict'; * *var HttpServer = require('epi').HttpServer; * *var util = require('util'); * * var config = { * host : null, * key : 'string', //https监听时需要这个key * cert : 'string', //https监听的时候需要证书 * version : '1.0.0' //api默认的版本信息 * port : { * http : 8000, //如果想要监听http端口,直接写 * https : 4433 //如果不想监听https,直接置为null * } * } * * //创建一个httpServer===================================== * var httpServer = new HttpServer(config); * * * //设置================================================== * httpServer.setting(function (app) { * app.set('trust proxy', true); * app.set('x-powered-by', false); * }); * //导入中间件============================================= * var body = require('body-parser'); * var cookie = require('cookie-parser'); * var compression = require("compression"); * * //应用中间件 * httpServer.use(function (app) { * app.use(compression()); * app.use(cookie()); * app.use(body.json()); * app.use(body.urlencoded({ * extended: true * })); * }); * * * //导入路由============================================== * var testRouter = require('./router/test'); * * //注册路由 * testRouter.map(httpServer); * //错误处理============================================= * httpServer.error(function (err, req, res, next) { * console.error(err.stack); * res.status(500).json({ * error: err * }); * }); * */ var HttpServer = function (options) { /* * * { * host : null, * key : 'string', * cert : 'string', * version : { * defaults : '1.0.0', * headers : { * version : 'accept-version', * channel : 'client-channel' * } * }, //api默认的版本信息 * port : { * http : 8000, * https : 4433 * } * } * */ options = options || {}; EventEmitter.call(this); this.__host = options.host || null; this.__version = options.version; this.__key = options.key; this.__cert = options.cert; this.__port = { http: options.port.http, https: options.port.https }; this.__version = { defaults : (options.version && options.version.defaults) ? options.version.defaults : '1.0.0', headers : (options.version && options.version.headers) ? options.version.headers : 'accept-version' }; this.__channel = { defaults : (options.channel && options.channel.defaults) ? options.channel.defaults : null, headers : (options.channel && options.channel.headers) ? options.channel.headers : 'client-channel' }; this.__cache = { headers : { request : (options.cache && options.cache.request) ? options.cache.request : 'use-api-cache', response : (options.cache && options.cache.request) ? options.cache.request : 'use-api-cache' } }; this.__server = null; /** * 如果调用cluster方法启动程序,所有子进程都在这里 * */ this.workers = null; /** * express 创建的app,如果想调用express的相关方法,可以使用这个app * */ this.app = null; /** * 导出express.Router 以支持sub app * */ this.Router = express.Router; this.__init(); }; util.inherits(HttpServer, EventEmitter); /** * @desc 初始化系统 * @access private * */ HttpServer.prototype.__init = function () { this.app = express(); var self = this; if (this.__port.https) { if (!this.__key || !this.__cert) { throw new Error('listen https need key and cert'); } this.__server = https.createServer({ key: this.__key, cert: this.__cert }, this.app); } /** * 用于解析version和device的中间件 * 添加一个解析version和device设备类型的中间件 */ this.app.use(function (req, res, next) { req.startTime = Date.now(); req.version = req.get(self.__version.headers.version); req.channel = req.get(self.__version.headers.channel); req.useCache = req.get(self.__cache.headers.request) ? true : false; //check api cache exits function //if you want to use api cache by custom, rewrite this function res.checkCache = function(fn){ fn(null, null); }; next(); }); return this.app; }; /** * 挂接方法 * */ METHODS.forEach(function(method){ HttpServer.prototype[method] = function(path, options, handler, ttl){ var self = this; if (!util.isObject(options)) { throw new Error('options is object like {v : "1.0.0", c : "pc"}'); } options = options || {}; options.v = options.v || self.__version; /*** * @desc defined handler * @param {Object} options example {v : '1.0.0', c : 'pc'} * @param {function} handler function(req, res, next){} * @param {Number} ttl the cache time In seconds * */ self.app[method](path, function(req, res, next){ if (options.v && req.version !== options.v) { return next(); } if (options.c && req.channel !== options.c) { return next(); } if(util.isNumber(ttl) && req.useCache && util.isFunction(res.checkCache)){ // check if there are cache , // if cached then send the cached value to request client res.checkCache(function(err, result){ if(!err && result){ //set a header to show the data come from apiCache res.set(self.__cache.headers.response, 'true'); return res.send(result); }else{ res.cacheTime = ttl; //cache time handler(req, res, next); } }); }else if(!util.isFunction(res.checkCache)){ //tip user that , the res.checkCache function is rewrite with error next(new Error('res.checkCache is must be a function, please check again')); }else{ handler(req, res, next); } }); }; }); /** * @desc 设置程序属性 * @param {Function} fn function(app){} * @example * httpServer.setting(function (app) { * app.set('trust proxy', true); * app.set('x-powered-by', false); * }); * */ HttpServer.prototype.setting = function (fn) { fn(this.app); }; /** * @desc 应用中间件 * @param fn {Function} function(app){} * @example * httpServer.use(function(app){ * app.use(compression()); * app.use(timeout('10s')); * app.use(cookie()); * app.use(body.json()); * }); * */ HttpServer.prototype.use = function (fn) { fn(this.app); }; /** * @desc 注册路由 * @param fn {Function} function(app){} * */ HttpServer.prototype.route = function (fn) { fn(this.app); }; /** * @desc 添加404处理 * @param fn {Function} function(req, res, next){} * */ HttpServer.prototype.uriNotFoundError = function(fn){ this.app.all('*', fn); }; /** * @desc 添加错误处理 * @param fn {Function} function(err, req, res, next){} * */ HttpServer.prototype.internalServerError = function (fn) { this.app.use(fn); }; /** * @desc 监听端口 * @param callback {Function} 监听后返回的消息 * @example * httpServer.listen(8000, function(err, message){ * console.log(message); * }) * */ HttpServer.prototype.listen = function (callback) { if (!this.__port.http && !this.__port.https) { throw new Error('port.http or port.https is not set'); } var listenMessage = ''; var flag = 0; var count = 0; var self = this; if (this.__port.http) { count++; } if (this.__port.https) { count++; } if (this.__port.http) { this.app.listen(this.__port.http, self.__host, function (err) { flag++; if (err) { listenMessage += err.message; } var host = self.__host || '127.0.0.1'; listenMessage += 'server listen on : http://' + host + ':' + self.__port.http + ', pid : ' + process.pid; if (flag >= count) { self.emit('listen', self.__port.http); callback && callback(listenMessage); } }); } if (this.__port.https) { this.__server.listen(this.__port.https, self.__host, function (err) { flag++; if (err) { listenMessage += err.message; } var host = self.__host || '127.0.0.1'; listenMessage += '\nserver listen on : https://' + host + ':' + self.__port.https + ', pid : ' + process.pid; if (flag >= count) { self.emit('listen', self.__port.https); callback && callback(listenMessage); } }); } }; /** * @desc 以集群的方式启动程序 * @param number {Number} 要启动的监听进程数量,null || 0 启动和 cpu个数 - 1 的进程数量 * @param callback {Function} 监听后返回的消息 * @example * //子进程启动事件 * httpServer.on('childStart', function(worker){ * //记录子进程启动 * logger.info(Date.now(), worker.pid); * }); * * * //子进程退出事件 * httpServer.on('childStop', function(worker, code, signal){ * //记录子进程非正常退出 * if(!signal && code !== 0){ * logger.fatal(Date.now(), worker.pid, code, signal); * } * }); * * //子进程退出事件 * httpServer.on('childRestart', function(worker){ * //记录子进程重启 * logger.fatal(Date.now(), worker.pid); * }); * * * //使用集群方式监听======================================= * httpServer.cluster(0, function (message) { * console.log(message); * }); * */ HttpServer.prototype.cluster = function (number, callback) { var self = this; var cpuCount = number || os.cpus().length - 1; self.workers = {}; if (cluster.isMaster) { for (var i = 0; i < cpuCount; i++) { var workerProcess = cluster.fork(); self.workers[workerProcess.process.pid] = workerProcess; self.emit('childStart', workerProcess); } cluster.on('exit', function (worker, code, signal) { self.emit('childStop', worker, code, signal); delete self.workers[worker.process.pid]; setTimeout(function(){ var workProcess = cluster.fork(); self.workers[workProcess.process.pid] = cluster.fork(); self.emit('childRestart', workProcess); }, 1000); }); } else { self.listen(callback); } }; exports.HttpServer = HttpServer;