var util = require('util'); var Emitter = require('events'); var Request = require('request'); var async = require('async'); /*接口*/ var _ = require('lodash'); var Utils = require('./Utils'); var Interfacer = function (config, app) { this.config = config; this.CONSTS = app.get("Register"); this.config.domain = this.config.domain || app.get("Register").domain; this.apis = {}; Emitter.call(this); }; util.inherits(Interfacer, Emitter); Interfacer.prototype.register = function (mos) { var me = this, name = mos.namespace; if (!name) { console.info(" Interfacer name can not empty"); } for (var key in mos.apis) { var name_key = name + "_" + key; if (me.isExisted(name_key)) { console.info("can not add repeat Interfacer key,please checkout"); } /*需要进行验证判断*/ me.apis[name_key] = mos.apis[key]; } }; Interfacer.prototype.isExisted = function (key) { return !!this.apis[key]; }; Interfacer.prototype.getInterfacerByNameSpace = function (namespace) { var me = this; if (!namespace) return {}; var apis = {}, len = namespace.length; for (var key in me.apis) { if (key.slice(0, len + 1) === namespace + "_") { var name = key.slice(len + 1); if (typeof me.apis[key] !== "function") { apis[name] = me.apis[key]; } } } return apis; } function __getArgs(str) { var arr = str.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].split(','); return (arr.length == 1 && arr[0] == "")?[]:arr; } function __requestApi(config, apiOpt, req, callback) { var me = this; if (typeof apiOpt == "function") { var args = __getArgs(apiOpt), o = {}; if (args.length > 2) { args.slice(2).map(function (namespace) { namespace = _.trim(namespace); o[namespace] = {}; var apis = me.getInterfacerByNameSpace(namespace); for (var action in apis) { o[namespace][action] = (function (api, config, consts) { var intermo = new Interfacer.create(api); return function () { var args = [].slice.call(arguments, 0), params = {}; intermo.apiOpt = api; intermo.config = { config: config, consts: consts }; if (args.length === 1 && _.isPlainObject(args[0])) { intermo.req.param(args[0]); } else { api.params = api.params || {}; var os = Object.keys(api.params); args.forEach(function (value, i) { params[os[i]] = value; }); intermo.req.param(params); } if (!this.queue) { this.queue = []; } this.queue.push(intermo); this.__proto__ = new queue(); return this; }; })(apis[action], config, me.CONSTS); } }); } var fns = []; for (var i in o) { fns.push(o[i]); } return apiOpt.apply(0, [req, function (err, result) { req._yoheaders = req._yoheaders; return callback(null, result); }].concat(fns)); } var options = __requestOption(req, apiOpt, config, me.CONSTS); __sendRequest(options, function (result) { return callback(null, result); }, function (result) { return callback(result, null); }, {len:1}, 0, []); //Request(options, function (error, response, body) { // var _err_ = new Error(); // if (error) { // console.info("Error [request"+options.url+"]:" + options.title); // console.error(error); // return callback(error, null); // } // try { // if (response && response.statusCode === 200) { // var obj = JSON.parse(body) // if (!(typeof obj == "object")) { // _err_.message = "Error[json parse@" + options.title + "--"+options.url+"]:" + body; // console.info(_err_.message); // console.error(_err_); // return callback(_err_, null); // } // } else { // _err_.message = "Error[response state @" + options.title + "--"+options.url+"]:" + response; // console.info(_err_.message); // console.error(_err_); // return callback(_err_, null); // } // } catch (err) { // console.info("Error[response to json @" + options.title + "--"+options.url+"]"); // console.info(response); // console.error(err); // return callback(err, null); // } // return callback(null, JSON.parse(body)); //}); }; function __requestOption(req, apiOpt, config, consts) { var method = (apiOpt.method || "POST").toLocaleUpperCase(), errs = []; var data = {}; if (apiOpt.params && (_.isArray(apiOpt.params) || _.isPlainObject(apiOpt.params))) { if (_.isArray(apiOpt.params)) { //兼容 yo之前的模式 后期废弃 apiOpt.params.forEach(function (param) { var name = param.name; if (req.param(name)) { if (param.type.toUpperCase() == "NUMBER") { data[name] = Number(req.param(name)); } else { data[name] = String(req.param(name)); } } }); } else if (_.isPlainObject(apiOpt.params)) { var fns = []; for (var name in apiOpt.params) { if (req.param("@" + name)) { continue; } var param = apiOpt.params[name]; if (param.default) { data[name] = param.type(param.default); } if (req.param(name)) { data[name] = param.type(req.param(name)); } if (param.coerce && typeof param.coerce == "function") { fns.push({ param: param,name:name}); } } fns.forEach(function (fn) { var d = fn.param.coerce.call(data, req); if (d) { data[fn.name] = param.type(d); } }); //验证 for (var name in apiOpt.params) { var param = apiOpt.params[name]; if (param.required && !data[name]) { errs.push("params "+name + "is required!!!"); } if (param.validator&&typeof param.validator=="function") { var isSuc = param.validator.call(data, data[name]); if (isSuc===false) { errs.push("params " +name + " validator error!!!"); } } } } } var options = { method: method }; options.errs = errs; options.title = apiOpt.title || ''; options.outobj = apiOpt.outobj || ''; if (options.method == "GET") { apiOpt.query = true; } if (/^https{0,1}:\/\//g.test(apiOpt.url)) { options.url = apiOpt.url; } else { options.url = (apiOpt.domain || config.domain) + apiOpt.url; } if (/\{\{.*\}\}/.test(options.url)) { var _qs = {}; if (apiOpt.query) { _qs = data; } var a = _.merge(consts, _qs); options.url=Utils.template(options.url, a); } if (apiOpt.form) { if (typeof apiOpt.form === "string") { options.form = Utils.template(apiOpt.form, data); } else if (_.isPlainObject(apiOpt.form)) { options.form = {}; for (var i in apiOpt.form) { options.form[i] = Utils.template(apiOpt.form[i], data) } } options.headers = { 'Content-Type' : 'application/x-www-form-urlencoded' } } else if (apiOpt.query) { //不做任何事 } else { options.body = JSON.stringify(data); options.headers = { 'Content-Type' : 'application/json' } } if (apiOpt.headers && _.isPlainObject(apiOpt.headers)) { options.headers = {}; for (var name in apiOpt.headers) { var param = apiOpt.headers[name]; if (req.param("@" + name)) { if (param.default) { options.headers[name] = param.type(param.default); } if (req.param("@" + name)) { options.headers[name] = param.type(req.param("@" + name)); } } } } if (req._yoheaders) { options.headers = _.merge(options.headers, req._yoheaders); } console.log("*************************************"); console.log("Http", options.url, data, options.headers); console.log("*************************************"); return options; } /* * {mos} 接口key 数组 比如[key1,key2] 后面可能要兼容字符串 比如 传key1 */ Interfacer.prototype.require = function (mos, req, res, cb) { var me = this, funcs = [], names = [], errName = []; mos.forEach(function (name) { if (me.apis.hasOwnProperty(name)) { names.push(name); if (me.config.mock) { funcs.push(me.apis[name].output); } else { funcs.push(function (callback) { __requestApi.call(me, me.config, me.apis[name], req, callback); }); } } else { console.error(name+"不存在"); errName.push(name); } }); if (funcs.length != mos.length) { var err = new Error(); err.message = "某个" + errName.join("->") + " 可能不存在!"; cb(err, funcs, names); return; } if (me.config.mock) { cb(null, funcs, names); return; } async.parallel(funcs, function (err, results) { if (err) { console.info("Async Error"); console.error(err); console.error(results); return cb(err, results, names); } return cb(null, results, names); }); }; Interfacer.create = function (apis) { return { req: { __params__: null, param: function (value) { if (typeof value == "object") { this.__params__ = value; } else { return this.__params__[value]; } } }, apiOpt: null, config: {}, error: function () { } } } function queue() { } queue.prototype = { done: function (success, fail) { var that = this; if (typeof success !== 'function') return; var len = that.queue.length, watchlen = { len: len }; var args = new Array(3); var wlen=Object.defineProperty({}, 'len', { value: len, writable: true, enumerable: false, configurable: true }); for (var i = 0; i < len; i++) { (function (queue,args) { var options = __requestOption(queue.req, queue.apiOpt, queue.config.config, queue.config.consts); __sendRequest(options, success, fail, wlen, i,args); })(this.queue[i],args); } that.queue = null; //var options = __requestOption(this.req, this.apiOpt); //this.__sendRequest(options, success, fail); return this; }, } function __sendRequest(options, success, fail, wlen, i, args) { var _err_ = new Error(), obj; if (options.errs instanceof Array&&options.errs.length) { _err_.message = options.errs.join(','); console.info("Error [options" + options.url + "]:" + options.title); console.error(_err_); wlen.len = 0; fail && fail(_err_); return; } Request(options, function (error, response, body) { if (!wlen.len) { return; } if (error) { console.info("Error [request"+options.url+"]:" + options.title); console.error(error); wlen.len = 0; fail && fail(error); return; } try { if (response && (response.statusCode === 200||response.statusCode === 302)) { if (!options.outobj) { obj = JSON.parse(body) if (!(typeof obj == "object")) { _err_.message = "Error[json parse@" + options.title + "--"+options.url+"]:" + body; console.info(_err_.message); console.error(_err_); wlen.len = 0; fail && fail(_err_); return; } } else { obj = options.outobj.toLocaleUpperCase().indexOf("BODY")>-1?body:response; } } else { _err_.message = "Error[response state @" + options.title + "--"+options.url+"]:" + response.statusCode; console.info(_err_.message); console.error(_err_); wlen.len = 0; fail && fail(_err_); return; } } catch (err) { console.info("Error[response to json @" + options.title + "--"+options.url+"]"); console.info(response); console.error(err); wlen.len = 0; fail && fail(err); return; } args[i]=obj; wlen.len--; if (!wlen.len) { success && success.apply(0, args) } return; }); } module.exports = Interfacer;