溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Node.js后端框架設(shè)計構(gòu)想是怎樣的

發(fā)布時間:2021-11-17 16:06:33 來源:億速云 閱讀:103 作者:柒染 欄目:web開發(fā)

Node.js后端框架設(shè)計構(gòu)想是怎樣的,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

后端的核心文件mass.js包含批量創(chuàng)建與刪除文件夾,MD5加密,類型識別與模塊加載等功能?,F(xiàn)在網(wǎng)站名與網(wǎng)站的路徑也還是混淆在里面,以后會獨立到一個配置文件中。只要運行node mass.js這命令就立即從模板文件中構(gòu)建一個樣板網(wǎng)站出來。下面就是它建站的最主要代碼:

//--------開始創(chuàng)建網(wǎng)站---------   //你想建立的網(wǎng)站的名字(請修正這里)   mass.appname = "jslouvre";   //在哪個目錄下建立網(wǎng)站(請修正這里)   mass.approot = process.cwd();   //用于修正路徑的方法,可以傳N個參數(shù)   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">此網(wǎng)站已存在</code>",true);       }else{           fs.mkdir(dir,0755)           mass.log("<code style="color:green">開始利用內(nèi)部模板建立您的網(wǎng)站&hellip;&hellip;</code>",true);       }       global.mapper = scaffold(dir);//取得路由系統(tǒng)       http.createServer(function(req, res) {           var arr = intercepters.concat();           //有關(guān)HTTP狀態(tài)的解釋 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,它會根據(jù)appname與approot判定目標路徑是否存在此網(wǎng)站,沒有就創(chuàng)建相應(yīng)文件夾 fs.mkdir(dir,0755)。但更多的文件夾與文件是由scaffold.js完成的。scaffold里面?zhèn)€文件夾列表,用于讓程序從templates把相應(yīng)的文件夾拷貝到網(wǎng)站的路徑下,并建立505.html, 404.html, favicon.ico, routes.js等文件。其中最重頭的是routes,它是用來定義路由規(guī)則。

//routes.js  //最重要的部分,根據(jù)它生成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的所有內(nèi)容。允許建立五種路由:根路由,資源路由,方法路由(get,delete,put,post),命名空間路由,嵌套路由。其實它們統(tǒng)統(tǒng)都會歸化為資源路由,每個URL都對應(yīng)一個控制器與其下的action。它會調(diào)用router.js,讓里面的Router實例mapper調(diào)用router.js里面的內(nèi)容,然后返回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多成員與元素,我們再利用它來進一步構(gòu)建我們的控制器,視圖與模型。

//如 this.controllers = {};現(xiàn)在變?yōu)?nbsp; { comments:     { actions: [ 'index', 'create', 'new', 'edit', 'destroy', 'update', 'show' ],          views: [ 'index', 'new', 'edit', 'show' ],       namespace: 'tests' } }     //   this.GET = [];現(xiàn)在變?yōu)?nbsp; [ { 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有四個數(shù)組屬性,GET,POST,DELETE,PUT,我稱之為匹配棧,這些數(shù)組的元素都是一個個對象,對象都有一個matcher的正則屬性,就是用來匹配請求過來的URL的pathname屬性,當然首先我們先取得其method,讓相應(yīng)的匹配棧去處理它。

現(xiàn)在手腳架scaffold.js還很簡鄙,以后它會結(jié)合熱部署功能,當用戶修改routes.js或其他配置文件時,它將會自動生成更多的視圖與控制器等等。

然后我們就啟動服務(wù)器了,由于req是EventEmitter的實例,因此我們可以隨意在上面綁定自定義事件,這里有兩個事件next_intercepter與err500。err500就不用說了,next_intercepter是用來啟動攔截器群集。這里我們只需要啟動***個。它在回調(diào)中會自動啟動下一個。這些攔截器是由intercepters.js 統(tǒng)一加載的。

//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)  });

每個攔截器都會對原始數(shù)據(jù)進行處理,并決定是繼續(xù)啟用下一個攔截器。比如mime攔截器:

mass.define("intercepters/mime",function(){      console.log("本模塊用于取得MIME,并作為request.mime而存在");      return mass.intercepter(function(req, res){          console.log("進入MIME回調(diào)");          var str = req.headers['content-type'] || '';          req.mime = str.split(';')[0];          return true;      })  })

postData攔截器

mass.define("intercepters/postData","querystring",function(qs){      console.log("本模塊用于取得POST請求過來的數(shù)據(jù),并作為request.body而存在");      return mass.intercepter(function(req,res){          console.log("進入postData回調(diào)");          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的參數(shù)并轉(zhuǎn)為一個對象,作為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("本模塊處理前端發(fā)過來的JSON數(shù)據(jù)");      return mass.intercepter(function(req, res, err){          reqreq.body = req.body || {};          if (req._body  || 'GET' == req.method || !~req.mime.indexOf("json")){              console.log("進入json回調(diào)")              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系統(tǒng)的入口。把原始請求的pathname取出來,然后通過正則匹配它,只要一個符合就停下來,然后加載對應(yīng)的控制器文件,調(diào)用相應(yīng)的action處理請求!

mass.define("intercepters/matcher","url",function(URL){      console.log("用于匹配請求過來的回調(diào)")      return mass.intercepter(function(req,res){          console.log("進入matcher回調(diào)");          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回調(diào)");          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的結(jié)果自動調(diào)用視圖。

當然現(xiàn)在框架還很簡單,只用了半天時間而已。它必須支持ORM與靜態(tài)文件緩存才行。此外還有cookie,session等支持,這些做成一個攔截器就行了。

總結(jié)如下:

◆ 判定網(wǎng)站是否存在,沒有通過手腳架構(gòu)建一個

◆ 讀取routes等配置文件,生成MVC系統(tǒng)所需要的控制器,視圖與模型。

◆ 通過熱部署功能,監(jiān)視用戶對配置文件的修改,進一步智能生成需要控制器,視圖與模型。

◆ 通過一系列攔截器處理請來,直到matcher攔截器里面進入MVC系統(tǒng),這時通過模型操作數(shù)據(jù)庫,渲染頁面。攔截器群集的應(yīng)用大大提高應(yīng)用的伸縮性?,F(xiàn)在還沒有來得及得node.js的多線程,可能這里面能發(fā)掘出許多好東西呢。

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI