Node.js后端框架设计构想(1)


后端的核心文件mass.js包含批量创建与删除文件夹,MD5加密,类型识别与模块加载等功能。现在网站名与网站的路径也还是混淆在里面,以后会独立到一个配置文件中。只要运行node mass.js这命令就立即从模板文件中构建一个样板网站出来。下面就是它建站的最主要代码:


  1. //--------开始创建网站---------  
  2.  //你想建立的网站的名字(请修正这里)  
  3.  mass.appname = "jslouvre";  
  4.  //在哪个目录下建立网站(请修正这里)  
  5.  mass.approot = process.cwd();  
  6.  //用于修正路径的方法,可以传N个参数  
  7.  mass.adjustPath = function(){  
  8.      [].unshift.call(arguments,mass.approot, mass.appname);  
  9.      return require("path").join.apply(null,arguments)  
  10.  }  
  11.  var dir = mass.adjustPath("")  
  12.  //  mass.rmdirSync(dir);//......  
  13.  mass.require("http,fs,path,scaffold,intercepters",function(http,fs,path,scaffold,intercepters){  
  14.      mass.log("<code style="color:blue;">=========================</code>",true)  
  15.      if(path.existsSync(dir)){  
  16.          mass.log("<code style="color:red">此网站已存在</code>",true);  
  17.      }else{  
  18.          fs.mkdir(dir,0755)  
  19.          mass.log("<code style="color:green">开始利用内部模板建立您的网站……</code>",true);  
  20.      }  
  21.      global.mapper = scaffold(dir);//取得路由系统  
  22.      http.createServer(function(req, res) {  
  23.          var arr = intercepters.concat();  
  24.          //有关HTTP状态的解释 http://www.cnblogs.com/rubylouvre/archive/2011/05/18/2049989.html  
  25.          req.on("err500",function(err){  
  26.              res.writeHead(500, {  
  27.                  "Content-Type": "text/html"  
  28.              });  
  29.              var html = fs.readFileSync(mass.adjustPath("public/500.html"))  
  30.              var arr = []  
  31.              for(var i in err){  
  32.                  arr.push("<li>"+i+"  :   "+err[i]+" </li>")  
  33.              }  
  34.              res.write((html+"").replace("{{url}}",arr.join("")));  
  35.              res.end();  
  36.          });  
  37.          req.on("next_intercepter",function(){  
  38.              try{  
  39.                  var next = arr.shift();  
  40.                  next && next.apply(null,arguments)  
  41.              }catch(err){  
  42.                  req.emit("err500",err);  
  43.              }  
  44.          });  
  45.          req.emit("next_intercepter",req, res);  
  46.      }).listen(8888);  
  47.     console.log("start server in 8888 port")  
  48.  }); 

只要运行mass.js,它会根据appname与approot判定目标路径是否存在此网站,没有就创建相应文件夹 fs.mkdir(dir,0755)。但更多的文件夹与文件是由scaffold.js完成的。scaffold里面个文件夹列表,用于让程序从templates把相应的文件夹拷贝到网站的路径下,并建立505.html, 404.html, favicon.ico, routes.js等文件。其中最重头的是routes,它是用来定义路由规则。


  1. //routes.js  
  2. //最重要的部分,根据它生成controller, action, model, views  
  3.    
  4. mass.define("routes",function(){  
  5.     return function(map){  
  6.         //方法路由  
  7.         //        map.get('/','site#index');  
  8.         //        map.get('/get_comments/:post_id','site#get_comments');  
  9.         //        map.post('/add_comment','site#add_comment');  
  10.         //        //资源路由  
  11.         //        map.resources('posts');  
  12.         //        map.resources('users');  
  13.         //        map.get('/view/:post_name','site#view_post');  
  14.         //        map.get('/rss','site#rss');  
  15.    
  16.         // map.resources('posts', {path: 'articles', as: 'stories'});  
  17.         //嵌套路由  
  18.         //        map.resources('posts', function (post) {  
  19.         //            post.resources('users');  
  20.         //        });  
  21.         //命名空间路由  
  22.         map.namespace("tests",function(tests){  
  23.             tests.resources('comments');  
  24.         })  
  25.     //        map.resources('users', {  
  26.     //            only: ['index', 'show']  
  27.     //        });  
  28.     //  
  29.     //        map.resources('users', {  
  30.     //            except: ['create', 'destroy']  
  31.     //        });  
  32.     //        map.resources('users', function (user) {  
  33.     //            user.get('avatar', 'users#avatar');  
  34.     //        });  
  35.     //        map.root("home#index")  
  36.     }  
  37. }); 

上面就是routes.js的所有内容。允许建立五种路由:根路由,资源路由,方法路由(get,delete,put,post),命名空间路由,嵌套路由。其实它们统统都会归化为资源路由,每个URL都对应一个控制器与其下的action。它会调用router.js,让里面的Router实例mapper调用router.js里面的内容,然后返回mapper。


  1. //scaffold.js  
  2.         var routes_url = mass.adjustPath('config/routes.js'),  
  3.         action_url = "app/controllers/",  
  4.         view_url = "app/views/",  
  5.         mapper = new Router  
  6.    
  7.         mass.require("routes("+routes_url+")",function(fn){//读取routes.js配置文件  
  8.             fn(mapper)  
  9.         });  
  10.  //这里省掉,一会儿解说  
  11.    
  12.         return mapper; 

Router实例mapper在routes运行完毕后,那么它的几个属性就会添加了N多成员与元素,我们再利用它来进一步构建我们的控制器,视图与模型。


  1. //如 this.controllers = {};现在变为  
  2. { comments:  
  3.    { actions: [ 'index', 'create', 'new', 'edit', 'destroy', 'update', 'show' ],  
  4.    
  5.      views: [ 'index', 'new', 'edit', 'show' ],  
  6.      namespace: 'tests' } }  
  7.    
  8. //   this.GET = [];现在变为  
  9. [ { controller: 'comments',  
  10.     action: 'index',  
  11.     method: 'GET',  
  12.     namespace: '/tests/',  
  13.     url: '/tests/comments.:format?',  
  14.     helper: 'tests_comments',  
  15.     matcher: /^\/tests\/comments$/i },  
  16.   { controller: 'comments',  
  17.     action: 'new',  
  18.     method: 'GET',  
  19.     namespace: '/tests/',  
  20.     url: '/tests/comments/new.:format?',  
  21.     helper: 'new_tests_comments',  
  22.     matcher: /^\/tests\/comments\/new$/i },  
  23.   { controller: 'comments',  
  24.     action: 'edit',  
  25.     method: 'GET',  
  26.     namespace: '/tests/',  
  27.     url: '/tests/comments/:id/edit.:format?',  
  28.     helper: 'edit_tests_comment',  
  29.     matcher: /^\/tests\/comments\/\d+\/edit$/i },  
  30.   { controller: 'comments',  
  31.     action: 'show',  
  32.     method: 'GET',  
  33.     namespace: '/tests/',  
  34.     url: '/tests/comments/:id.:format?',  
  35.     helper: 'tests_comment',  
  36.     matcher: /^\/tests\/comments\/\d+$/i } ] 

mapper有四个数组属性,GET,POST,DELETE,PUT,我称之为匹配栈,这些数组的元素都是一个个对象,对象都有一个matcher的正则属性,就是用来匹配请求过来的URL的pathname属性,当然首先我们先取得其method,让相应的匹配栈去处理它。


现在手脚架scaffold.js还很简鄙,以后它会结合热部署功能,当用户修改routes.js或其他配置文件时,它将会自动生成更多的视图与控制器等等。


然后我们就启动服务器了,由于req是EventEmitter的实例,因此我们可以随意在上面绑定自定义事件,这里有两个事件next_intercepter与err500。err500就不用说了,next_intercepter是用来启动拦截器群集。这里我们只需要启动第一个。它在回调中会自动启动下一个。这些拦截器是由intercepters.js 统一加载的。


  1. //intercepters.js  
  2. mass.intercepter = function(fn){//拦截器的外壳  
  3.     return function(req, res, err){  
  4.         if(err ){  
  5.             req.emit("next_intercepter", req, res, err);  
  6.         }else if(fn(req,res) === true){  
  7.             req.emit("next_intercepter", req, res)  
  8.         }  
  9.     }  
  10. }  
  11. var deps = ["mime","postData","query","methodOverride","json","favicon","matcher","handle404"];//"more",  
  12. mass.define("intercepters", deps.map(function(str){  
  13.     return "intercepters/"+str  
  14. }).join(","), function(){  
  15.     console.log("取得一系列栏截器");  
  16.     return [].slice.call(arguments,0)  
  17. }); 

每个拦截器都会对原始数据进行处理,并决定是继续启用下一个拦截器。比如mime拦截器:


  1. mass.define("intercepters/mime",function(){  
  2.     console.log("本模块用于取得MIME,并作为request.mime而存在");  
  3.     return mass.intercepter(function(req, res){  
  4.         console.log("进入MIME回调");  
  5.         var str = req.headers['content-type'] || '';  
  6.         req.mime = str.split(';')[0];  
  7.         return true;  
  8.     })  
  9. }) 




标签:
Warning: Invalid argument supplied for foreach() in C:\zl\webjia\view.php on line 50

友情链接
轻松育儿世界奇观
苏ICP备16066217号-2