Node.js后端框架设计构想

后端的核心文件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("=========================",true)
  15.      if(path.existsSync(dir)){
  16.          mass.log("此网站已存在",true);
  17.      }else{
  18.          fs.mkdir(dir,0755)
  19.          mass.log("开始利用内部模板建立您的网站……",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("
  33. "+i+"  :   "+err[i]+" 
  34. ")
  35.              }
  36.              res.write((html+"").replace("{{url}}",arr.join("")));
  37.              res.end();
  38.          });
  39.          req.on("next_intercepter",function(){
  40.              try{
  41.                  var next = arr.shift();
  42.                  next && next.apply(null,arguments)
  43.              }catch(err){
  44.                  req.emit("err500",err);
  45.              }
  46.          });
  47.          req.emit("next_intercepter",req, res);
  48.      }).listen(8888);
  49.     console.log("start server in 8888 port")
  50.  });

只要运行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. })

#p#

postData拦截器

 
 
 
  1. mass.define("intercepters/postData","querystring",function(qs){
  2.     console.log("本模块用于取得POST请求过来的数据,并作为request.body而存在");
  3.     return mass.intercepter(function(req,res){
  4.         console.log("进入postData回调");
  5.         reqreq.body = req.body || {};
  6.         if ( req._body ||  /GET|HEAD/.test(req.method) || 'application/x-www-form-urlencoded' !== req.mime ){
  7.             return true;
  8.         }
  9.         var buf = '';
  10.         req.setEncoding('utf8');
  11.         function buildBuffer(chunk){
  12.             buf += chunk
  13.         }
  14.         req.on('data', buildBuffer);
  15.         req.once('end',function(){
  16.             try {
  17.                 if(buf != ""){
  18.                     req.body = qs.parse(buf);
  19.                     req._body = true;
  20.                 }
  21.                 req.emit("next_intercepter",req,res)
  22.             } catch (err){
  23.                 req.emit("next_intercepter",req,res,err)
  24.             }finally{
  25.                 req.removeListener("data",buildBuffer)
  26.             }
  27.         })
  28.     });
  29. });

query拦截器

 
 
 
  1. mass.define("intercepters/query","querystring,url",function(qs,URL){
  2.     console.log("本模块用于取得URL的参数并转为一个对象,作为request.query而存在");
  3.     return mass.intercepter(function(req, res){
  4.         req.query = ~req.url.indexOf('?')
  5.         ? qs.parse(URL.parse(req.url).query)
  6.         : {};
  7.         return true;
  8.     })
  9. })

methodOverride拦截器

 
 
 
  1. mass.define("intercepters/methodOverride",function(){
  2.     console.log("本模块用于校正method属性");
  3.     var methods = {
  4.         "PUT":"PUT",
  5.         "DELETE":"DELETE"
  6.     },
  7.     method = mass.configs.method || "_method";
  8.     return mass.intercepter(function(req, res){
  9.         reqreq.originalMethod = req.method;
  10.         var defaultMethod = req.method === "HEAD" ? "GET" : req.method;
  11.         var _method = req.body ? req.body[method] : req.headers['x-http-method-override']
  12.         _method = (_method || "").toUpperCase();
  13.         req.method = methods[_method] || defaultMethod;
  14.         if(req.body){
  15.             delete req.body[method];
  16.         }
  17.         return true;
  18.     })
  19. })

json拦截器

 
 
 
  1. mass.define("intercepters/json",function(){
  2.     console.log("本模块处理前端发过来的JSON数据");
  3.     return mass.intercepter(function(req, res, err){
  4.         reqreq.body = req.body || {};
  5.         if (req._body  || 'GET' == req.method || !~req.mime.indexOf("json")){
  6.             console.log("进入json回调")
  7.             return true;
  8.         }else{
  9.             var buf = '';
  10.             req.setEncoding('utf8');
  11.             function buildBuffer(chunk){
  12.                 buf += chunk;
  13.             }
  14.             req.on('data', buildBuffer);
  15.             req.once('end', function(){
  16.                 try {
  17.                     req.body = JSON.parse(buf);
  18.                     req._body = true;
  19.                     req.emit("next_intercepter",req,res);
  20.                 } catch (err){
  21.                     err.status = 400;
  22.                     req.emit("next_intercepter",req,res,err);
  23.                 }finally{
  24.                     req.removeListener("data",buildBuffer);
  25.                 }
  26.             });
  27.         }
  28.     })
  29. })

而在这么多拦截器中,最重要的是matcher拦截器,它进入框架MVC系统的入口。把原始请求的pathname取出来,然后通过正则匹配它,只要一个符合就停下来,然后加载对应的控制器文件,调用相应的action处理请求!

 
 
 
  1. mass.define("intercepters/matcher","url",function(URL){
  2.     console.log("用于匹配请求过来的回调")
  3.     return mass.intercepter(function(req,res){
  4.         console.log("进入matcher回调");
  5.         var pathname = URL.parse(req.url).pathname, is404 = true,method = req.method, arr = mapper[method];
  6.         for(var i =0, obj; obj = arr[i++];){
  7.             if(obj.matcher.test(pathname)){
  8.                 is404 = false
  9.                 var url = mass.adjustPath("app/controllers/",obj.namespace, obj.controller+"_controller.js")
  10.                 mass.require(obj.controller+"_controller("+url +")",function(object){
  11.                     object[obj.action](req,res);//进入控制器的action!!!
  12.                     console.log(obj.action)
  13.                 },function(){
  14.                     var err = new Error;
  15.                     err.statusCode = 404
  16.                     req.emit("next_intercepter",req,res,err);
  17.                 })
  18.                 break;
  19.             }
  20.         }
  21.         if(is404){
  22.             var err = new Error;
  23.             err.statusCode = 404
  24.             req.emit("next_intercepter",req,res,err);
  25.         }
  26.     })
  27. })

***殿后的是handle404拦截器:

 
 
 
  1. mass.define("intercepters/handle404","fs,path",function(fs){
  2.     console.log("本模块用于处理404错误");
  3.     return function(req, res, err){
  4.         console.log("进入handle404回调");
  5.         var accept = req.headers.accept || '';
  6.         if (~accept.indexOf('html')) {
  7.             res.writeHead(404, {
  8.                 "Content-Type": "text/html"
  9.             });
  10.             var html = fs.readFileSync(mass.adjustPath("public/404.html"))
  11.             res.write((html+"").replace("{{url}}",req.url));
  12.             res.end();
  13.         } else if (~accept.indexOf('json')) {//json
  14.             var error = {
  15.                 message: err.message, 
  16.                 stack: err.stack
  17.             };
  18.             for (var prop in err) error[prop] = err[prop];
  19.             var json = JSON.stringify({
  20.                 error: error
  21.             });
  22.             res.setHeader('Content-Type', 'application/json');
  23.             res.end(json);
  24.         // plain text
  25.         } else {
  26.             res.writeHead(res.statusCode, {
  27.                 'Content-Type': 'text/plain'
  28.             });
  29.             res.end(err.stack);
  30.         }
  31.     }
  32. })

再回过头来看控制器部分,从模板中生成的controller非常简单:

 
 
 
  1. mass.define("comments_controller",function(){
  2.     return {
  3.         "index":function(){},
  4.         "create":function(){},
  5.         "new":function(){},
  6.         "edit":function(){},
  7.         "destroy":function(){},
  8.         "update":function(){},
  9.         "show":function(){}
  10.     }
  11.  });

因此你需要动手改到其可用,如

 
 
 
  1. "show":function(req,res){
  2.   
  3.     res.writeHead(200, {
  4.         "Content-Type": "text/html"
  5.     });
  6.     var html = fs.readFileSync(mass.adjustPath("app/views/tests/show.html"))
  7.     res.write(html);
  8.     res.end();
  9.              
  10. }

以后会判定action的结果自动调用视图。

当然现在框架还很简单,只用了半天时间而已。它必须支持ORM与静态文件缓存才行。此外还有cookie,session等支持,这些做成一个拦截器就行了。

总结如下:

◆ 判定网站是否存在,没有通过手脚架构建一个

◆ 读取routes等配置文件,生成MVC系统所需要的控制器,视图与模型。

◆ 通过热部署功能,监视用户对配置文件的修改,进一步智能生成需要控制器,视图与模型。

◆ 通过一系列拦截器处理请来,直到matcher拦截器里面进入MVC系统,这时通过模型操作数据库,渲染页面。拦截器群集的应用大大提高应用的伸缩性。现在还没有来得及得node.js的多线程,可能这里面能发掘出许多好东西呢。

原文:http://www.cnblogs.com/rubylouvre/archive/2011/12/13/2286280.html

名称栏目:Node.js后端框架设计构想
标题路径:http://www.shufengxianlan.com/qtweb/news24/317074.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联