作者 : solq
blog : http://www.cnblogs.com/solq/p/3574640.html
整个框架只有一个核心文件,两个配置文件,够轻了吧
1.只需要添加 ws Controller 文件就行,启动自动注入 Controller
2.path 路径参数,请求参数自动注入
3.请求参数格式转换,可扩展转换类型
4.容器变量字段自动注入
5.容器初始化执行
反正是就仿spring 那套 个人觉得已秒杀一切了哈哈
源码下载 : http://files.cnblogs.com/solq/solq_nodejsapp.rar
使用方式 =============================================================
1.controller 自动注入请求参数示例:
‘/testparam’:{ auth : [],//权限 methods : [‘GET’], controller : function(param_p2,param_p1,body_xxx){ console.log(“testparam==”,arguments); } } 以param_为标识自动注入
如 url 请求
http://xxxxx:port/testparam?p1=111&p2=2222
拦截器会自动将 p1,p2注入 param_p1,param_p2
body_ 标识会注入 body_xxx={p1:111,p2:2222}
2.path 参数注入变量
‘/testpathParam/{p1}/{p2}’:{ auth : [],//权限 methods : [‘GET’], controller : function(path_int_p2,path_p1,req, res){ console.log(“testpathParam”,arguments); } }
如 url 请求
http://xxxxx:port/testpathParam/11111/22222
path_(var)标识 (var) 会自动替换路径上的 {var}
req,res 会自动注入的
3.容器字段自动注入 : auto_ 标识
module.exports = { f1 :‘this f1’, auto_xxx:null, auto_ip:null, postConstruct : function(){ console.log(‘postConstruct’,this.auto_xxx,this.auto_ip.id); }, preDestroy : function(){ console.log(‘preDestroy’); } }; 每个容器都有自己的ID 属性,如没有会以文件名为ID
auto_(id)
当完成所有扫描容器时,再遍历查找所有容器,根据 auto_ 标识拦载 注入,然后初始化容器 postConstruct
下面是实现代码
var _injection = function(filePath,container){ var id = container.id; if(id == null){ id =_path.basename(filePath).replace(’.js’,’’); container.id = id; } container.filePath = filePath ; AppContext[id]!=null && _error(" injection key is has : " ,id); AppContext[id] = container; }
var _auto_injection_field = function(){ for(var id in AppContext){ var container = AppContext[id];
for(var field in container){
if(field.indexOf('auto_')>-1){
var checkFn = container[field];
if(typeof checkFn == 'function'){
continue;
}
var injectionKey = field.replace('auto_','');
if(AppContext[injectionKey]==null){
_error('injection field not find id : ',field, id );
}else{
container[field] = AppContext[injectionKey];
debug("injection field : ",injectionKey,id )
}
}
}
}
}
var _postConstruct = function(){ for(var id in AppContext){ var container = AppContext[id]; container.postConstruct!=null && container.postConstruct(); } }
先晒晒代码 ==================================
config.js
1 /** 2 * @author solq 3 * @deprecated blog: cnblogs.com/solq 4 * */ 5 var appConfig={ 6 /db config/ 7 dbHost : ‘127.0.0.1’, 8 dbPort : 9977 , 9 dbUser : ‘’, 10 dbPasssWord : ‘’, 11 12 /debug config/ 13 LOGDEBUG : 1, 14 LOGINFO : 2, 15 LOGERROR : 4, 16 LOGLEVEL : (1 | 4), 17 18 /web config/ 19 webPort : 5555, 20 21 /auto scan config/ 22 scanResource : { 23 ‘./resource’ : { 24 //include : [], 25 //exclude : [’./ws/test/test1.js’] 26 } 27 }, 28 scanService : { 29 ‘./service’ : { 30 //include : [], 31 //exclude : [’./ws/test/test1.js’] 32 } 33 }, 34 scanController : { 35 ‘./ws’ : { 36 //include : [], 37 exclude : [’./ws/test/test1.js’] 38 } 39 } 40 }; 41 42 module.exports = appConfig;
debug.js
View Code 主入口
app.js
/**
- @author solq
- @deprecated blog: cnblogs.com/solq
- */ var http = require(“http”), fs = require(“fs”), _path = require(“path”), appConfig = require(’./config.js’), debug = require(’./core/debug.js’).debug, _error = require(’./core/debug.js’).error, requestController = require(’./core/RequestController.js’), AppContext={};
//处理功能方法 //cp google var walk = function(dir,suffix) { 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,suffix)) } else { if(suffix!=null){ if(suffix!=_path.extname(file)){
}
}
results.push(file)
}
})
return results
}
var scanProcess = function(scanConfig,suffix,callFn){ for(var dir in scanConfig){ var files = walk(dir,suffix), obj = scanConfig[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;
}
}
callFn(filePath);
}
}
} var _injection = function(filePath,container){ var id = container.id; if(id == null){ id =_path.basename(filePath).replace(’.js’,’’); container.id = id; } container.filePath = filePath ; AppContext[id]!=null && _error(" injection key is has : " ,id); AppContext[id] = container; }
var _auto_injection_field = function(){ for(var id in AppContext){ var container = AppContext[id];
for(var field in container){
if(field.indexOf('auto_')>-1){
var checkFn = container[field];
if(typeof checkFn == 'function'){
continue;
}
var injectionKey = field.replace('auto_','');
if(AppContext[injectionKey]==null){
_error('injection field not find id : ',field, id );
}else{
container[field] = AppContext[injectionKey];
debug("injection field : ",injectionKey,id )
}
}
}
}
}
var _postConstruct = function(){ for(var id in AppContext){ var container = AppContext[id]; container.postConstruct!=null && container.postConstruct(); } }
//IOC 控制流程 // scanResource>scanService>scanController // end scan auto injection field // run postConstruct
// scanResource debug( “injection service : =============================================”); scanProcess(appConfig.scanResource,null,function(filePath){ var resource=require(filePath); _injection(filePath,resource); debug( "injection resource : ", filePath); }); debug( “end injection service : =========================================”);
// scanService debug( “injection service : =============================================”); scanProcess(appConfig.scanService,’.js’,function(filePath){ var service=require(filePath); _injection(filePath,service); debug( "injection service : ", filePath); }); debug( “end injection service : =========================================”);
//scanController debug( “injection controller : =============================================”); scanProcess(appConfig.scanController,’.js’,function(filePath){ var controller=require(filePath); _injection(filePath,controller); requestController.add(controller); debug( "injection controller : ", filePath); }); debug( “end injection controller : =========================================”);
debug( “injection field : =============================================”); _auto_injection_field(); debug( “end injection field : =============================================”); //postConstruct _postConstruct();
http.createServer(function(request, response) {
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);
/**
- @author solq
- @deprecated blog: cnblogs.com/solq
- */ module.exports = { f1 :‘this f1’, auto_xxx:null, auto_ip:null, postConstruct : function(){ console.log(‘postConstruct’,this.auto_xxx,this.auto_ip.id); }, preDestroy : function(){ console.log(‘preDestroy’); } };
ws Controller js
/**
-
@author solq
-
@deprecated blog: cnblogs.com/solq
-
*/ module.exports = {
postConstruct : function(){ console.log(‘init ============================’); },
‘/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<0)
_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){
if(url=='id'){
continue;
}
if(url.indexOf('auto_')>-1){
continue;
}
var obj = handle[url] ;
if(typeof obj!='string'){
continue;
}
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;
我发现,controller 是通过 return 来返回结果的。
如果 controller 中涉及异步的查询,那么所查询到的数据就无法通往外界。
在改进的过程中,controller 应该还是会变成接收 function (req, res) {} 的形式,并通过 res 来返回数据。
没有 github 地址吗?
不会玩 github 等能拿出手了会共享的
不该参照 Spring 的,应该参照 Rails
我看了一下 restify的风格,没有 spring 的好 我喜欢自动注入,反转控制
自动注入就是hibernate的转型而矣,IOC就是@,没啥难度,用nodejs自己写一个更容易实现。
基本注入已完成,就差路径映射变量处理