Models 是你與你的數據庫交互的一些文件。它們包含了你處理你的數據的全部方法和功能。它們不只僅包含了建立、讀取、更新和刪除的方法,還包含了業務邏輯。例如,若是你有一個 car model,你能夠有一個 mountTyres 方法。css
在你的數據庫中,針對每種類型的數據,你應該建立至少一個文件。在咱們的例子中,咱們有 users 和 comments,所以咱們有 user model 和 comment model。有時候,當一個 model 文件很大,更好的作法是基於內部的邏輯將這個 model 文件分紅好幾個文件。html
你應該讓你的 models 獨立於外部。models 之間不該該相互引用。它們不須要知道哪一個 controller 調用它們。它們永遠不要接收 request 或 reponse 對象,它們永遠不要返回 http 的錯誤,可是它們應該返回 model 的錯誤。web
全部的這些將會使你的 models 更好維護。由於它們是獨立的,因此能夠很好地測試它們。Models 能夠移動到任何須要用到它的地方。改變一個 model,不會應該其餘的東西,由於它是獨立的。數據庫
基於上面提的的點,讓咱們來看看如何實現咱們例子中的 model。下面是 comment model。express
var db = require('../db') // Create new comment in your database and return its id // 在你的數據庫中建立一條新的 comment exports.create = function(user, text, cb) { var comment = { user: user, text: text, date: new Date().toString() } db.save(comment, cb) } // Get a particular comment exports.get = function(id, cb) { db.fetch({id:id}, function(err, docs) { if (err) return cb(err) cb(null, docs[0]) }) } // Get all comments exports.all = function(cb) { db.fetch({}, cb) } // Get all comments by a particular user exports.allByUser = function(user, cb) { db.fetch({user: user}, cb) }
user model 沒有包含進來。comment model 不關心它是什麼,它僅僅關心它怎麼存儲。session
var db = require('../db') , crypto = require('crypto') hash = function(password) { return crypto.createHash('sha1').update(password).digest('base64') } exports.create = function(name, email, password, cb) { var user = { name: name, email: email, password: hash(password), } db.save(user, cb) } exports.get = function(id, cb) { db.fetch({id:id}, function(err, docs) { if (err) return cb(err) cb(null, docs[0]) }) } exports.authenticate = function(email, password) { db.fetch({email:email}, function(err, docs) { if (err) return cb(err) if (docs.length === 0) return cb() user = docs[0] if (user.password === hash(password)) { cb(null, docs[0]) } else { cb() } }) } exports.changePassword = function(id, password, cb) { db.update({id:id}, {password: hash(password)}, function(err, affected) { if (err) return cb(err) cb(null, affected > 0) }) }
除了建立和管理用戶所須要的功能以外,那還有用於用戶身份驗證和密碼管理的方法。再一次的,這個 model 不知道已經存在的其餘的 model、controller 或者應用的其餘部分。app
這個文件夾包含了你應用全部須要渲染的模板。一般,團隊中的設計師會在這裏工做。post
你想每個 controllers 所對應的模板都有一個子文件夾。這樣的話,你將會爲相同的任務組合模板。測試
選擇一個模板語言會讓人困惑,由於有不少的選擇。咱們最喜歡的模板語言,是 Jade 和 Mustache,咱們一直在用。Jade 很適合生成 html 頁面。它使得寫 html 標籤更短和更加可讀。針對於條件和迭代,它也可使用 JavaScript。Mustache 在另一方面,專一於渲染各類各樣的模板,它提供了儘量少的邏輯運算符而且處理數據的方法不多。這使得它很是適合編寫很是乾淨的模板,這些模板專一於顯示你的數據而不是處理數據。fetch
寫好一個模板的最佳實踐是避免在模板中作任何處理。若是你的數據須要在顯示以前進行處理,在你的 controller 中處理。也要避免添加太多的邏輯,尤爲是這個邏輯能夠被移至 controller。
doctype html html head title Your comment web app body h1 Welcome and leave your comment each comment in comments article.Comment .Comment-date= comment.date .Comment-text= comment.text
如你所見,在渲染這個模板時,數據預計已經被處理好了。
這是一個文件夾,你將會定義你應用全部的路由在這個文件夾中。你的 controllers 將會處理 web 請求,將模板提供給用戶,而且和你的 models 進行交互,以處理和檢索數據。這是膠水,可以鏈接和控制你的 web 應用。
一般,對於你應用中的每個邏輯部分,你至少會有一個文件。例如,一個文件處理評論,另一個文件處理關於用戶的請求等等。來自同一個 controller 的全部路由都有相同的前綴,這是一個好的實踐。例如 /comments/all 和 /comments/new。
有時候很難決定什麼應該進入 controller,什麼應該進入 model。一個最好的實踐是應該永遠不會直接訪問數據庫。它永遠不該該調用 write,update,fetch 這些數據庫提供的方法,而應該依靠 model 中的方法。例如若是你有一個 car model,你想要把 4 個輪子安裝到這個 car 上,controller 不會調用 db.update(id, { wheels: 4 }),而是會調用像 car.mountwheels(id, 4) 這樣的方法。
下面是負責評論的 controller。
var express = require('express') , router = express.Router() , Comment = require('../models/comment') , auth = require('../middlewares/auth') router.post('/', auth, function(req, res) { user = req.user.id text = req.body.text Comment.create(user, text, function (err, comment) { res.redirect('/') }) }) router.get('/:id', function(req, res) { Comment.get(req.params.id, function (err, comment) { res.render('comments/comment', {comment: comment}) }) }) module.exports = router
在 controller 文件夾中,也有一個 index.js 文件夾。它的目的是加載全部其餘的 controllers,和可能定義一些沒有相同前綴的路徑,例如 home 頁面路由。
var express = require('express') , router = express.Router() , Comment = require('../models/comment') router.use('/comments', require('./comments')) router.use('/users', require('./users')) // 與 comments 和 users 不一樣的是,home 頁面不須要前綴(comments 或 users) router.get('/', function(req, res) { Comments.all(function(err, comments) { res.render('index', {comments: comments}) }) }) module.exports = router
這個文件將會處理你全部的路由。你的應用在啓動的必須加載的惟一的路由器。
在這個文件夾中,你將會存儲全部你 Express 的中間件。中間件的目的是爲了提取常見的 controller 代碼,它將會在多個請求中執行,而且一般會修改 請求/響應 對象。
就像一個 controller,一箇中間件永遠不該該訪問數據庫,相反,對於它要完成的每一項任務,它應該使用你的 models。
下面是一個 users 中間件,來自 middlewares/users.js 文件。它的目的是加載發出請求的用戶。
User = require('../models/user') module.exports = function(req, res, next) { if (req.session && req.session.user) { User.get(req.session.user, function(err, user) { if (user) { req.user = user } else { delete req.user delete req.session.user } next() }) } else { next() } }
這個中間件使用 user model,而且它沒有直接訪問數據庫。
下一步,authorization 中間件,當你想要阻止沒有權限訪問相同路由的時候,能夠用到這個中間件。
module.exports = function(req, res, next) { if (req.user) { next() } else { res.status(401).end() } }
它沒有任何外部的依賴。若是你看看上面的 controllers 文件,你能夠看看如何它是如何應用的。
這個文件夾包含實用的代碼,這些代碼被用在多個 models,middlewares 或者 controllers 中,可是 helpers 不屬於 models,middlewares 或 controllers 的範疇。一般,你對不一樣的常見任務,會有不一樣的文件。
一個例子就是 helper 文件提供一些方法來管理日期和時間。
這個文件件只是提供靜態文件。一般,它會有子文件夾,像 css,libs,img 用於 css 樣式,圖片和 JavaScript 庫就像 jQuery。這個文件夾可以提供服務的最好實踐不是經過你的應用,而是經過一個 Nginx 或者 Apache 服務,它們比起 Node 在靜態文件的服務更好。
每一個項目都須要測試,而且你須要將全部的測試彙集到一塊兒。爲了幫助管理它們,你將它們分離在不一樣的子文件中。