沉沙
2018-06-27
来源 :
阅读 1442
评论 0
摘要:后端的核心文件mass.js包含批量创建与删除文件夹,MD5加密,类型识别与模块加载等功能。希望阅读本篇文章以后大家有所收获,帮助大家对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("<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状态的解释 //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. })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的多线程,可能这里面能发掘出许多好东西呢。
本文由职坐标整理发布,学习更多的Node.js相关知识,请关注职坐标WEB前端Node.js频道!
喜欢 | 0
不喜欢 | 0
您输入的评论内容中包含违禁敏感词
我知道了

请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号