登录
注册
node.js 学习社区
个人NODEJS REST 轻量级框架 源码已共享

泪花

2014-12-09 00:35

作者 : 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(paramp2,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;

原文引自:http://cnodejs.org/topic/53100f7bfd3dc6f7120c70ee

回复 · 0

发表回复

你可以在回复中 @ 其他人