nodejs REST 轻量级框架
作者 : solq
blog : http://www.cnblogs.com/solq/p/3574640.html
整个框架只有一个核心文件,两个配置文件,够轻了吧
1.只需要添加 ws Controller 文件就行,启动自动注入 Controller
2.path 路径参数,请求参数自动注入
3.请求参数格式转换,可扩展转换类型
先晒晒代码
config.js
/** * @author solq * @deprecated blog: cnblogs.com/solq * */ var appConfig={ /**db config**/ dbHost : ‘127.0.0.1‘, dbPort : 9977 , dbUser : ‘‘, dbPasssWord : ‘‘, /**debug config**/ LOGDEBUG : 1, LOGINFO : 2, LOGERROR : 4, LOGLEVEL : (1 | 4), /**web config**/ webPort : 5555, scanController : { ‘./ws‘ : { //include : [], exclude : [‘./ws/test/test1.js‘] } } }; module.exports = appConfig;
debug.js
/** * @author solq * @deprecated blog: cnblogs.com/solq * */ var appConfig = require(‘../config.js‘); module.exports = { debug : function(){ (appConfig.LOGDEBUG & appConfig.LOGLEVEL) && console.log.apply(console, arguments); }, error : function(){ (appConfig.LOGERROR & appConfig.LOGLEVEL) && console.error.apply(console, arguments); }, info : function(){ (appConfig.LOGINFO & appConfig.LOGLEVEL) && console.info.apply(console, arguments); } }
主入口
app.js
/** * @author solq * @deprecated blog: cnblogs.com/solq * */ var http = require("http"), fs = require("fs"), appConfig = require(‘./config.js‘), debug = require(‘./core/debug.js‘).debug, requestController = require(‘./core/RequestController.js‘), AppContext={}; //scan Controller //cp google var walk = function(dir) { var results = [] var list = fs.readdirSync(dir) list.forEach(function(file) { file = dir + ‘/‘ + file var stat = fs.statSync(file) if (stat && stat.isDirectory()) results = results.concat(walk(file)) else results.push(file) }) return results } debug( "register controller : ============================================="); for(var dir in appConfig.scanController){ var files = walk(dir), obj = appConfig.scanController[dir]; for(var i in files){ var filePath=files[i]; if(obj.include!=null){ if(obj.include.indexOf(filePath)<0){ continue; } } if(obj.exclude!=null){ if(obj.exclude.indexOf(filePath)>-1){ continue; } } var controller=require(filePath); controller.filePath = filePath ; requestController.add(controller); debug( "register controller : ", filePath); } } debug( "end register controller : ========================================="); http.createServer(function(request, response,AppContext) { var result=null; try{ result=requestController.filter(request, response,AppContext); }catch(e){ response.writeHead(500, {"Content-Type": "text/plain;charset=utf-8"}); response.write(e); response.end(); return; } response.writeHead(200, {"Content-Type": "text/plain;charset=utf-8"}); if(result!=null){ if(typeof result == ‘string‘){ response.write(result); }else if(typeof result == ‘object‘ || Array.isArray(result) ){ try{ response.write(JSON.stringify(result)); }catch(e){ response.write(e); } }else{ response.write(result); } } response.end(); }).listen(appConfig.webPort);
ws Controller js
/** * @author solq * @deprecated blog: cnblogs.com/solq * */ module.exports = { ‘/testpath‘:{ auth : [],//权限 methods : [‘GET‘,‘POST‘], controller : function(request, response){ console.log("testpath",arguments); } }, ‘/testpath/{p1}/{p2}‘:{ auth : [],//权限 methods : [‘GET‘], controller : function(path_p2,path_p1){ console.log("testpath==",arguments); } }, ‘/testparam‘:{ auth : [],//权限 methods : [‘GET‘], controller : function(param_p2,param_p1,body_xxx){ console.log("testparam==",arguments); } }, ‘/testpathParam/{p1}/{p2}‘:{ auth : [],//权限 methods : [‘GET‘], controller : function(path_int_p2,path_p1,param_p1,param_p2){ console.log("testpathParam",arguments); } }, ‘/index‘:{ auth : [],//权限 methods : [‘GET‘], controller : function(){ console.log("hello controller"); return {‘code‘:200,data:{‘xx‘:‘测试中文‘}}; } } };
core js
/** * @author solq * @deprecated blog: cnblogs.com/solq * */ var queryUrl = require("url"), queryString = require( "querystring" ) debug = require(‘../core/debug.js‘).debug, _error = require(‘../core/debug.js‘).error, dateConverter = require(‘../core/dateConverter.js‘); var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; var requestController={ _urlData : {}, _pathData : {}, _paramData : {}, _pathParamData : {}, /** 保存方法参数元数据结构 { sawName : 原始的参数名 name : 参数名 index : 参数下标 type : 参数类型 如 string array int date object pathIndex : 对应路径参数下标 mapType : 映身处理的类型 如 path param body other required : 是否注入 默认都为 true } */ getParamMetadata : function(params,url){ //path 处理 if(url.lastIndexOf(‘/‘) == url.length){ url = url.substring(0, url.length - 1).trim(); } var checkPath = url.replace(/{([^}]+)}/gm,function(a,b,c){ return ‘{}‘; }); var checkGroup = checkPath.split("/").filter(function(e){return e}), sawGroup = url.split("/").filter(function(e){return e}); var result = { pathParamData : {}, paramData : {}, otherData : {}, bodyData : null, pathParamLength : 0, paramLength : 0, checkParamLength : 0, otherLength : 0 , sawPath : url , checkPath : checkPath, sawGroup : sawGroup, checkGroup : checkGroup }; if(checkPath != url){ var checkPathGroup={}; for(var t in checkGroup){ var __v=checkGroup[t]; if(‘{‘==__v[0]){ continue; } checkPathGroup[t]= __v; } result.checkPathGroup = checkPathGroup; } var injectionkey = [‘path_‘,‘param_‘,‘body_‘,‘def_‘,‘auto_‘,‘int_‘,‘date_‘,‘array_‘,‘object_‘]; var otherKey =[‘req‘,‘res‘,‘request‘, ‘response‘,‘auto_‘]; for(var i in params){ var param = params[i], name = param; metadata = { sawName : param, index : i, type : ‘string‘, required : true }; for(var j in injectionkey){ name=name.replace(injectionkey[j],‘‘); } metadata.name = name; if(result[name]!=null) _error(‘ParamMetadata register is Has: ‘,param, url); //参数类型处理 if(param.indexOf(‘date_‘)>-1){ metadata.type = ‘date‘; }else if(param.indexOf(‘object_‘)>-1){ metadata.type = ‘object‘; }else if(param.indexOf(‘array_‘)>-1){ metadata.type = ‘array‘; }else if(param.indexOf(‘int_‘)>-1){ metadata.type = ‘int‘; } //还有其它等等可扩展 //映射类型处理 if(otherKey.indexOf(param)>-1){ metadata.mapType = ‘other‘; result.otherLength++; if(result.otherData[name]!=null) _error(‘ParamMetadata register is Has: ‘,param, url); result.otherData[name] = metadata; continue; } if(param.indexOf(‘path_‘)>-1){ metadata.mapType = ‘path‘; result.pathParamLength++; if(result.pathParamData[name]!=null) _error(‘ParamMetadata register is Has: ‘,param, url); var checkKey=‘{‘+name+‘}‘; var gi=sawGroup.indexOf(checkKey); if(gi<-1) _error(‘ParamMetadata register path key not find: ‘,param, url , checkKey, sawGroup); metadata.pathIndex = gi; result.pathParamData[name] = metadata; continue; } if(param.indexOf(‘param_‘)>-1){ metadata.mapType = ‘param‘; result.paramLength++; if(param.indexOf(‘def_‘)<0){ result.checkParamLength++; metadata.required=false; } if(result.paramData[name]!=null) _error(‘ParamMetadata register is Has: ‘,param, url); result.paramData[name] = metadata; continue; } if(param.indexOf(‘body_‘)>-1){ metadata.mapType = ‘body‘; if(result.bodyData!=null) _error(‘ParamMetadata register is Has: ‘,param, url); result.bodyData = metadata; continue; } } return result; }, //public function add : function(handle){ /*** 1 url 映射处理 2 参数处理 3 注入外部上下文 */ for(var url in handle){ var obj = handle[url] ; if(obj.methods==null || obj.methods.length==0){ obj.methods = [‘GET‘]; } for(var i in obj.methods){ var method = obj.methods[i]; var controller = obj.controller; var params = this.getParamNames(controller); var paramsMetadata = this.getParamMetadata(params,url); var key = this.getKey(method,url); obj.paramsMetadata = paramsMetadata; //分类保存,只要是方便映射,更快处理请求 if(paramsMetadata.pathParamLength==0 && paramsMetadata.paramLength==0){ //没有任何路径,请求参数 this.injectionUrlMap(key,obj); }else if(paramsMetadata.pathParamLength!=0 && paramsMetadata.paramLength==0){ //只有路径 参数 key = this.getKey(method,paramsMetadata.checkPath); var groupNum = paramsMetadata.sawGroup.length; this.injectionPathMap(groupNum,key,obj); }else if(paramsMetadata.pathParamLength==0 && paramsMetadata.paramLength!=0){ //只有参数 this.injectionParamMap(key,obj); }else if(paramsMetadata.pathParamLength!=0 && paramsMetadata.paramLength!=0){ //都有 key = this.getKey(method,paramsMetadata.checkPath); var groupNum = paramsMetadata.sawGroup.length; this.injectionPathAndParamMap(groupNum,key,obj); } //debug("params=====",params,"url=====",url,"paramsMetadata=====",paramsMetadata); } } }, findPathFilter : function(data,groupPath){ var _ar = data[groupPath.length]; if(_ar==null) return null; var _filter =null; //debug("findPathFilter======", data,groupPath); //debug("findPathFilter======", _ar); for(var i in _ar){ var _f = _ar[i]; var paramsMetadata = _f.paramsMetadata, flag = true, checkPathGroup = paramsMetadata.checkPathGroup; for(var j in checkPathGroup){ var checkValue = checkPathGroup[j]; if( checkValue != groupPath[j] ){ flag = false; break; } } //debug("findPathFilter======", checkPathGroup,groupPath,flag); if(flag){ _filter = _f; break; } } return _filter; }, filter : function(request, response,AppContext){ var _url = request.url; if( _url == ‘/favicon.ico‘){ return; } var method = request.method , urlObj = queryUrl.parse(_url), path = urlObj.pathname.trim(), queryObj = queryString.parse( urlObj.query ); if(path.lastIndexOf(‘/‘) == path.length){ path = path.substring(0, path.length - 1); } var paramLength = this.getMapSize(queryObj); var key=this.getKey(method,path), groupPath = path.split("/").filter(function(e){return e}) ; //TODO //auth check // urlData>pathData>paramData>pathParamData var _filter=null, isCheckParamLength = false; if(paramLength==0){ _filter=this._urlData[key]; if(_filter==null ){ _filter = this.findPathFilter(this._pathData,groupPath); } }else{ isCheckParamLength = true; _filter=this._paramData[key]; if(_filter==null ){ _filter = this.findPathFilter(this._pathData,groupPath); } if(_filter==null ){ _filter = this.findPathFilter(this._pathParamData,groupPath); } } if(_filter==null ){ _error(‘not find controller : ‘ ,key); return; } var paramsMetadata = _filter.paramsMetadata, _paramLength = paramsMetadata.paramLength, _checkParamLength = paramsMetadata.checkParamLength, controller = _filter.controller; if( isCheckParamLength && _paramLength!=paramLength){ //if(_checkParamLength!=paramLength){ //暂时不支持默认参数 //TODO throw _error(‘paramLength length is wrong : ‘ ,key,‘ name : ‘,paramsMetadata.name,‘ length : ‘,_paramLength,paramLength); return; //} } //param length is right //injection path //injection param //injection paramAndPath //injection other var callParams=[], resultMap = {} ; for(var name in paramsMetadata.pathParamData){ var metadata = paramsMetadata.pathParamData[name]; this.injectionParamProcess(request, response,AppContext,metadata,queryObj,groupPath,resultMap); } //paramData for(var name in paramsMetadata.paramData){ var metadata = paramsMetadata.paramData[name]; this.injectionParamProcess(request, response,AppContext,metadata,queryObj,null,resultMap); } //bodyData if(paramsMetadata.bodyData!=null){ this.injectionParamProcess(request, response,AppContext,paramsMetadata.bodyData,queryObj,null,resultMap); } //otherData for(var name in paramsMetadata.otherData){ var metadata = paramsMetadata.otherData[name]; this.injectionParamProcess(request, response,AppContext,metadata,queryObj,null,resultMap); } //sort for(var k in resultMap){ callParams.push(k); } for(var i in callParams){ callParams[i]=resultMap[i]; } //debug(‘callParams ==============‘,callParams); return controller.apply(controller,callParams); }, //private function injectionParamProcess : function(request, response,AppContext,metadata,queryObj,groupPath,resultMap){ var index = metadata.index, name = metadata.name, type = metadata.type, mapType = metadata.mapType, required = metadata.required, value=null; switch(mapType){ case ‘param‘ : value = queryObj[name]; if(value==null && required){ //TODO throw _error(‘injectionParam parram is null : ‘,name); } break; case ‘path‘ : var pathIndex = metadata.pathIndex; value = groupPath[pathIndex]; if(value==null){ //TODO throw _error(‘injectionParam path is null : ‘,name); } break; case ‘body‘ : value = queryObj; break; case ‘other‘ : //debug(value," other value +++++++++++"); var otherKey ={‘req‘:request,‘res‘:response,‘request‘:request, ‘response‘:response}; value = otherKey[name]; break; } if(value!=null ){ switch(mapType){ case ‘path‘ : case ‘param‘ : switch(type){ case ‘int‘ : var _v=parseInt(value); if(isNaN(_v) ){ //TODO throw _error(‘injectionParam parram num is null : ‘,name,value); } value=_v; break; case ‘array‘ : if(!Array.isArray(value)){ value=[value]; } break; case ‘date‘ : value=dateConverter.convert(value); break; } break; } } resultMap[index] = value; }, injectionPathAndParamMap : function(groupNum,key,controller){ if(this._pathParamData[groupNum]==null){ this._pathParamData[groupNum] = {}; } this._pathParamData[groupNum][key]!=null && _error("重复注册 REST injectionPathAndParamMap 处理器 : " ,key); debug("injectionPathAndParamMap : ",key); this._pathParamData[groupNum][key]=controller; }, injectionPathMap : function(groupNum,key,controller){ if(this._pathData[groupNum]==null){ this._pathData[groupNum] = {}; } this._pathData[groupNum][key]!=null && _error("重复注册 REST injectionPathMap 处理器 : " ,key); debug("injectionPathMap : ",key); this._pathData[groupNum][key]=controller; }, injectionUrlMap : function(key,controller){ this._urlData[key]!=null && _error("重复注册 REST injectionUrlMap 处理器 : " ,key); debug("injectionUrlMap : ",key); this._urlData[key]=controller; }, injectionParamMap : function(key,controller){ this._paramData[key]!=null && _error("重复注册 REST injectionParamMap 处理器 : " ,key); debug("injectionParamMap : ",key); this._paramData[key]=controller; }, getMapSize : function(map){ var num=0; for(var i in map) num++; return num; }, //cp google getParamNames : function(func) { var fnStr = func.toString().replace(STRIP_COMMENTS, ‘‘) var result = fnStr.slice(fnStr.indexOf(‘(‘)+1, fnStr.indexOf(‘)‘)).match(/([^\s,]+)/g) if(result === null) result = [] return result }, getKey : function(method,url){ if(url.lastIndexOf(‘/‘) == url.length){ url = key.substring(0, url.length - 1); } return method.toLowerCase().trim() + "_"+ url.toLowerCase().trim(); }, }; module.exports = requestController;
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。