Node.js后端框架设计构想是怎样的,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
后端的核心文件mass.js包含批量创建与删除文件夹,MD5加密,类型识别与模块加载等功能。现在网站名与网站的路径也还是混淆在里面,以后会独立到一个配置文件中。只要运行node mass.js这命令就立即从模板文件中构建一个样板网站出来。下面就是它建站的最主要代码:
//--------开始创建网站--------- //你想建立的网站的名字(请修正这里) mass.appname = "jslouvre"; //在哪个目录下建立网站(请修正这里) mass.approot = process.cwd(); //用于修正路径的方法,可以传N个参数 mass.adjustPath = function(){ [].unshift.call(arguments,mass.approot, mass.appname); return require("path").join.apply(null,arguments) } var dir = mass.adjustPath("") // mass.rmdirSync(dir);//...... mass.require("http,fs,path,scaffold,intercepters",function(http,fs,path,scaffold,intercepters){ mass.log("<code style="color:blue;">=========================</code>",true) if(path.existsSync(dir)){ mass.log("<code style="color:red">此网站已存在</code>",true); }else{ fs.mkdir(dir,0755) mass.log("<code style="color:green">开始利用内部模板建立您的网站……</code>",true); } global.mapper = scaffold(dir);//取得路由系统 http.createServer(function(req, res) { var arr = intercepters.concat(); //有关HTTP状态的解释 http://www.cnblogs.com/rubylouvre/archive/2011/05/18/2049989.html req.on("err500",function(err){ res.writeHead(500, { "Content-Type": "text/html" }); var html = fs.readFileSync(mass.adjustPath("public/500.html")) var arr = [] for(var i in err){ arr.push("<li>"+i+" : "+err[i]+" </li>") } res.write((html+"").replace("{{url}}",arr.join(""))); res.end(); }); req.on("next_intercepter",function(){ try{ var next = arr.shift(); next && next.apply(null,arguments) }catch(err){ req.emit("err500",err); } }); req.emit("next_intercepter",req, res); }).listen(8888); console.log("start server in 8888 port") });
只要运行mass.js,它会根据appname与approot判定目标路径是否存在此网站,没有就创建相应文件夹 fs.mkdir(dir,0755)。但更多的文件夹与文件是由scaffold.js完成的。scaffold里面个文件夹列表,用于让程序从templates把相应的文件夹拷贝到网站的路径下,并建立505.html, 404.html, favicon.ico, routes.js等文件。其中最重头的是routes,它是用来定义路由规则。
//routes.js //最重要的部分,根据它生成controller, action, model, views mass.define("routes",function(){ return function(map){ //方法路由 // map.get('/','site#index'); // map.get('/get_comments/:post_id','site#get_comments'); // map.post('/add_comment','site#add_comment'); // //资源路由 // map.resources('posts'); // map.resources('users'); // map.get('/view/:post_name','site#view_post'); // map.get('/rss','site#rss'); // map.resources('posts', {path: 'articles', as: 'stories'}); //嵌套路由 // map.resources('posts', function (post) { // post.resources('users'); // }); //命名空间路由 map.namespace("tests",function(tests){ tests.resources('comments'); }) // map.resources('users', { // only: ['index', 'show'] // }); // // map.resources('users', { // except: ['create', 'destroy'] // }); // map.resources('users', function (user) { // user.get('avatar', 'users#avatar'); // }); // map.root("home#index") } });
上面就是routes.js的所有内容。允许建立五种路由:根路由,资源路由,方法路由(get,delete,put,post),命名空间路由,嵌套路由。其实它们统统都会归化为资源路由,每个URL都对应一个控制器与其下的action。它会调用router.js,让里面的Router实例mapper调用router.js里面的内容,然后返回mapper。
//scaffold.js var routes_url = mass.adjustPath('config/routes.js'), action_url = "app/controllers/", view_url = "app/views/", mapper = new Router mass.require("routes("+routes_url+")",function(fn){//读取routes.js配置文件 fn(mapper) }); //这里省掉,一会儿解说 return mapper;
Router实例mapper在routes运行完毕后,那么它的几个属性就会添加了N多成员与元素,我们再利用它来进一步构建我们的控制器,视图与模型。
//如 this.controllers = {};现在变为 { comments: { actions: [ 'index', 'create', 'new', 'edit', 'destroy', 'update', 'show' ], views: [ 'index', 'new', 'edit', 'show' ], namespace: 'tests' } } // this.GET = [];现在变为 [ { controller: 'comments', action: 'index', method: 'GET', namespace: '/tests/', url: '/tests/comments.:format?', helper: 'tests_comments', matcher: /^\/tests\/comments$/i }, { controller: 'comments', action: 'new', method: 'GET', namespace: '/tests/', url: '/tests/comments/new.:format?', helper: 'new_tests_comments', matcher: /^\/tests\/comments\/new$/i }, { controller: 'comments', action: 'edit', method: 'GET', namespace: '/tests/', url: '/tests/comments/:id/edit.:format?', helper: 'edit_tests_comment', matcher: /^\/tests\/comments\/\d+\/edit$/i }, { controller: 'comments', action: 'show', method: 'GET', namespace: '/tests/', url: '/tests/comments/:id.:format?', helper: 'tests_comment', 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 统一加载的。
//intercepters.js mass.intercepter = function(fn){//拦截器的外壳 return function(req, res, err){ if(err ){ req.emit("next_intercepter", req, res, err); }else if(fn(req,res) === true){ req.emit("next_intercepter", req, res) } } } var deps = ["mime","postData","query","methodOverride","json","favicon","matcher","handle404"];//"more", mass.define("intercepters", deps.map(function(str){ return "intercepters/"+str }).join(","), function(){ console.log("取得一系列栏截器"); return [].slice.call(arguments,0) });
每个拦截器都会对原始数据进行处理,并决定是继续启用下一个拦截器。比如mime拦截器:
mass.define("intercepters/mime",function(){ console.log("本模块用于取得MIME,并作为request.mime而存在"); return mass.intercepter(function(req, res){ console.log("进入MIME回调"); var str = req.headers['content-type'] || ''; req.mime = str.split(';')[0]; return true; }) })
postData拦截器
mass.define("intercepters/postData","querystring",function(qs){ console.log("本模块用于取得POST请求过来的数据,并作为request.body而存在"); return mass.intercepter(function(req,res){ console.log("进入postData回调"); reqreq.body = req.body || {}; if ( req._body || /GET|HEAD/.test(req.method) || 'application/x-www-form-urlencoded' !== req.mime ){ return true; } var buf = ''; req.setEncoding('utf8'); function buildBuffer(chunk){ buf += chunk } req.on('data', buildBuffer); req.once('end',function(){ try { if(buf != ""){ req.body = qs.parse(buf); req._body = true; } req.emit("next_intercepter",req,res) } catch (err){ req.emit("next_intercepter",req,res,err) }finally{ req.removeListener("data",buildBuffer) } }) }); });
query拦截器
mass.define("intercepters/query","querystring,url",function(qs,URL){ console.log("本模块用于取得URL的参数并转为一个对象,作为request.query而存在"); return mass.intercepter(function(req, res){ req.query = ~req.url.indexOf('?') ? qs.parse(URL.parse(req.url).query) : {}; return true; }) })
methodOverride拦截器
mass.define("intercepters/methodOverride",function(){ console.log("本模块用于校正method属性"); var methods = { "PUT":"PUT", "DELETE":"DELETE" }, method = mass.configs.method || "_method"; return mass.intercepter(function(req, res){ reqreq.originalMethod = req.method; var defaultMethod = req.method === "HEAD" ? "GET" : req.method; var _method = req.body ? req.body[method] : req.headers['x-http-method-override'] _method = (_method || "").toUpperCase(); req.method = methods[_method] || defaultMethod; if(req.body){ delete req.body[method]; } return true; }) })
json拦截器
mass.define("intercepters/json",function(){ console.log("本模块处理前端发过来的JSON数据"); return mass.intercepter(function(req, res, err){ reqreq.body = req.body || {}; if (req._body || 'GET' == req.method || !~req.mime.indexOf("json")){ console.log("进入json回调") return true; }else{ var buf = ''; req.setEncoding('utf8'); function buildBuffer(chunk){ buf += chunk; } req.on('data', buildBuffer); req.once('end', function(){ try { req.body = JSON.parse(buf); req._body = true; req.emit("next_intercepter",req,res); } catch (err){ err.status = 400; req.emit("next_intercepter",req,res,err); }finally{ req.removeListener("data",buildBuffer); } }); } }) })
而在这么多拦截器中,最重要的是matcher拦截器,它进入框架MVC系统的入口。把原始请求的pathname取出来,然后通过正则匹配它,只要一个符合就停下来,然后加载对应的控制器文件,调用相应的action处理请求!
mass.define("intercepters/matcher","url",function(URL){ console.log("用于匹配请求过来的回调") return mass.intercepter(function(req,res){ console.log("进入matcher回调"); var pathname = URL.parse(req.url).pathname, is404 = true,method = req.method, arr = mapper[method]; for(var i =0, obj; obj = arr[i++];){ if(obj.matcher.test(pathname)){ is404 = false var url = mass.adjustPath("app/controllers/",obj.namespace, obj.controller+"_controller.js") mass.require(obj.controller+"_controller("+url +")",function(object){ object[obj.action](req,res);//进入控制器的action!!! console.log(obj.action) },function(){ var err = new Error; err.statusCode = 404 req.emit("next_intercepter",req,res,err); }) break; } } if(is404){ var err = new Error; err.statusCode = 404 req.emit("next_intercepter",req,res,err); } }) })
***殿后的是handle404拦截器:
mass.define("intercepters/handle404","fs,path",function(fs){ console.log("本模块用于处理404错误"); return function(req, res, err){ console.log("进入handle404回调"); var accept = req.headers.accept || ''; if (~accept.indexOf('html')) { res.writeHead(404, { "Content-Type": "text/html" }); var html = fs.readFileSync(mass.adjustPath("public/404.html")) res.write((html+"").replace("{{url}}",req.url)); res.end(); } else if (~accept.indexOf('json')) {//json var error = { message: err.message, stack: err.stack }; for (var prop in err) error[prop] = err[prop]; var json = JSON.stringify({ error: error }); res.setHeader('Content-Type', 'application/json'); res.end(json); // plain text } else { res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' }); res.end(err.stack); } } })
再回过头来看控制器部分,从模板中生成的controller非常简单:
mass.define("comments_controller",function(){ return { "index":function(){}, "create":function(){}, "new":function(){}, "edit":function(){}, "destroy":function(){}, "update":function(){}, "show":function(){} } });
因此你需要动手改到其可用,如
"show":function(req,res){ res.writeHead(200, { "Content-Type": "text/html" }); var html = fs.readFileSync(mass.adjustPath("app/views/tests/show.html")) res.write(html); res.end(); }
以后会判定action的结果自动调用视图。
当然现在框架还很简单,只用了半天时间而已。它必须支持ORM与静态文件缓存才行。此外还有cookie,session等支持,这些做成一个拦截器就行了。
总结如下:
◆ 判定网站是否存在,没有通过手脚架构建一个
◆ 读取routes等配置文件,生成MVC系统所需要的控制器,视图与模型。
◆ 通过热部署功能,监视用户对配置文件的修改,进一步智能生成需要控制器,视图与模型。
◆ 通过一系列拦截器处理请来,直到matcher拦截器里面进入MVC系统,这时通过模型操作数据库,渲染页面。拦截器群集的应用大大提高应用的伸缩性。现在还没有来得及得node.js的多线程,可能这里面能发掘出许多好东西呢。
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。