上一篇对项目的目录结构和 app.js 等一些文件作了一些改造,然而那只是开始。node
接下来将作进一步的改造和完善。web
咱们先看看几个主要的脚本文件,下面的代码是我稍微修改过并添加注释的,方便理解每句代码的意思。express
app.js:npm
var express = require('express'), path = require('path'), favicon = require('serve-favicon'), logger = require('morgan'), cookieParser = require('cookie-parser'), bodyParser = require('body-parser'), routes = require('./routes/index'), users = require('./routes/users'); //生成一个 express 实例 var app = express(); //指定 web 应用的标题栏小图标的路径为:/static/favicon.ico app.use(favicon(path.join(__dirname, 'static', 'favicon.ico'))); //加载日志中间件 app.use(logger('dev')); //加载解析 json 的中间件 app.use(bodyParser.json()); //加载解析 urlencoded 请求体的中间件 app.use(bodyParser.urlencoded({ extended: false })); //加载解析 cookie 的中间件 app.use(cookieParser()); //设置 static 文件夹为存放静态文件的目录 app.use(express.static(path.join(__dirname, 'static'))); //路由控制器 app.use('/', routes); app.use('/users', users); //捕获404错误,并转发到错误处理器 app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); //错误处理器 if (app.get('env') === 'development') { //开发环境下的错误处理器,返回完整的错误对象 app.use(function(err, req, res, next) { res.status(err.status || 500); res.send({
code: 0, message: err.message, error: err }); }); } app.use(function(err, req, res, next) { //生产环境下的错误处理器,只返回错误提示消息 res.status(err.status || 500); res.send({
code: 0, message: err.message }); }); //导出app实例供其余模块调用 module.exports = app;
本来错误处理器里面调用了 res.render 方法,但它依赖于模版引擎。而前面我已经把模版引擎配置相关的代码移除掉了,因此这里改用 res.send 方法实现。json
代码里指定了网站小图标,记得将小图标文件放到对应位置,不然会报错,实在没有小图片就把那行代码注释掉吧。api
start.js(PS:也就是以前的 bin/www)浏览器
#!/usr/bin/env node //上面一行代表此文件是 node 可执行文件 var app = require('./app'),//引入 app.js 导出的 app 实例 debug = require('debug')('test:server'),//引入 debug 模块,打印调试日志 http = require('http'),//引入 http 模块,用以建立 http 服务 port = process.env.PORT || '3000',//环境变量若是设置了端口号,就用环境变量设置的,不然使用默认值3000 server = null; //设置端口号 app.set('port', port); //建立 http 服务 server = http.createServer(app); //监听端上面设置的口号 server.listen(port); //绑定错误事件处理函数 server.on('error', onError); //绑定监听事件处理函数 server.on('listening', onListening); //错误事件处理函数 function onError(error) { if (error.syscall !== 'listen') { throw error; } var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; //对特定的错误类型作友好的处理 switch (error.code) { case 'EACCES': console.error(bind + ' requires elevated privileges'); process.exit(1); break; case 'EADDRINUSE': console.error(bind + ' is already in use'); process.exit(1); break; default: throw error; } } //监听事件处理函数 function onListening() { var addr = server.address(), bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; debug('Listening on ' + bind); }
原本在 start.js 里有一个 normalizePort 函数,用来作端口号转换的。但我以为没有什么必要,只要在配置端口号的时候注意便可,不必作这样的适配,因而将它移除,简化代码。cookie
routes/index.js 和 routes/users.js:app
//routes/index.js
var express = require('express'); var router = express.Router(); router.get('/', function(req, res, next) { res.send('hello world'); }); module.exports = router;
//routes/users.js
var express = require('express'); var router = express.Router(); router.get('/', function(req, res, next) { res.send('respond with a resource'); }); module.exports = router;
上面两段脚本,生成一个路由实例用来捕获访问主页的 GET 请求,导出这个路由并在 app.js 中经过 app.use 方法被加载。当访问对应的 url 时,就会执行相应的处理回调。函数
index.js 原本也是使用了 res.render 方法,也被改为使用 res.send 方法了。缘由和上面提到过的一样修改点相同,这里就再也不重复。
然而,目前的这种路由配置形式我以为不怎么美丽,由于咱们能够试想一下,假如应用比较复杂,路由配置也相应的更复杂,那么在 app.js 就会出现大量的路由配置相关代码...
为了不这种状况,致使 app.js 过于臃肿,因而我将路由配置相关的代码从新规划了一下。
先看看改造后的目录结构(主要变更在 routes):
原本 routes 目录下有多个 js 文件,如今变成只有一个 main.js 了,而后里面又多了一个 modules 目录,里面会有多个 js 文件,它们都是 main.js 的子模块。
好比图中看到的两个 menuMod.js 和 userMod.js,分别是菜单以及用户相关的业务逻辑,以此规则划分模块。
而 routes 目录下的 main.js 则是一个路由配置的主文件,经过引入 routes/modules 目录中的各个子模块,指定不一样请求方式,不一样 url 所对应的不一样回调函数。
改造过程当中,根据目录结构的改变,还要对 app.js 做对应修改,下面看看修改后的代码...
app.js
var express = require('express'), path = require('path'), favicon = require('serve-favicon'), logger = require('morgan'), cookieParser = require('cookie-parser'), bodyParser = require('body-parser'), routes = require('./routes/main'); //生成一个 express 实例 var app = express(); //指定 web 应用的标题栏小图标的路径为:/static/favicon.ico app.use(favicon(path.join(__dirname, 'static', 'favicon.ico'))); //加载日志中间件 app.use(logger('dev')); //加载解析 json 的中间件 app.use(bodyParser.json()); //加载解析 urlencoded 请求体的中间件 app.use(bodyParser.urlencoded({ extended: false })); //加载解析 cookie 的中间件 app.use(cookieParser()); //设置 static 文件夹为存放静态文件的目录 app.use(express.static(path.join(__dirname, 'static'))); //配置路由 routes(app); //捕获404错误,并转发到错误处理器 app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); //错误处理器 if (app.get('env') === 'development') { //开发环境下的错误处理器,将错误信息渲染 error 模版并显示到浏览器中 app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: err }); }); } app.use(function(err, req, res, next) { //生产环境下的错误处理器,不会将错误信息泄露给用户 res.status(err.status || 500); res.render('error', { message: err.message, error: {} }); }); //导出 app 实例供其余模块调用 module.exports = app;
routes/main.js
var _ = require('underscore'),//引入 underscore 模块 userMod = require('./modules/userMod'),//引入 user 模块 menuMod = require('./modules/menuMod'),//引入 menu 模块 config = { get: { '/user/list': userMod.getUserList, '/user/status': userMod.checkStatus, '/menu/list': menuMod.getMenuList }, post: { '/user/save': userMod.save, '/menu/save': menuMod.save } };//路由配置 module.exports = function (app) { //分析路由配置对象,逐一处理 _.each(config, function (subConfig, method) { _.each(subConfig, function (func, url) { app[method](url, func); }); }); };
routes/modules/userMod.js
var userMod = { getUserList: function (req, res) { res.send('userList'); }, checkStatus: function (req, res) { res.send('userStatus'); }, save: function (req, res) { res.send('userSave'); } }; module.exports = userMod;
routes/modules/menuMod.js
var menuMod = { getMenuList: function (req, res) { res.send('menuList'); }, save: function (req, res) { res.send('menuSave'); } }; module.exports = menuMod;
上面的改造,还添加了一个项目依赖库,它就是知名的 underscore。
打开命令行工具,进入项目根目录,执行下面指令,安装 underscore,并将它加入到 package.json 的 dependencies 之中:
npm install underscore --save
通过这样的改造后,之后修改路由规则,只须要在 routes/main.js 里修改 config 配置对象,而后根据须要添加新的子模块,或者在原有的子模块里加入新的方法便可。
须要注意的是,每一个子模块里的每个方法,都有两个同样的参数 req 和 res,也就是 request 对象 和 response 对象。
至于这两个对象都提供了什么方法,什么属性,能够去查 API,传送门:http://expressjs.jser.us/api
OK,到目前为止,整个项目从建立到如今,已经面目全非了。
但我竟然跑都没跑过一次...是否是感受很不靠谱?
其实这个项目是根据以前的经验从新整理的,因此在弄的时候,基本就没测试是否能运行了。
但为防百密一疏,或者说是谨慎起见,这里咱们运行一下试试看。
打开命令行工具,进入到项目根目录,而后运行下面指令:
node start
若是没看到报错信息,说明服务成功运行了。
接下来,咱们用浏览器访问 http://localhost:3000/user/list
若是能看到浏览区里出现 userList,说明成功返回了
再试试用浏览器访问 http://localhost:3000/user/status
浏览区里出现 userStatus,OK,没问题了
至此,咱们的应用已经成功实现了根据不一样的请求,作出相应相应的基本功能。后面能够根据须要慢慢完善细节了~