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 在静态文件的服务更好。
每一个项目都须要测试,而且你须要将全部的测试汇集到一块儿。为了帮助管理它们,你将它们分离在不一样的子文件中。