【深入浅出Node.js系列十一】Node.js开发框架Express4.x
2016-05-27 16:38
671 查看
1 建立项目
让我们从头开始Express4.x的安装和使用吧,安装Node和NPM在本文就不多说了。Linux环境安装请参考文章,Node.js&NPM的安装与配置,Window环境安装直接下载Node的安装文件,双击安装就行了。首先,我们需要安装express库。在Express3.6.x之前的版本,Express需要全局安装的,项目构建器模块是合并在Express项目中的,后来这个构建器被拆分出来,独立成为了一个项目express-generator,
现在我们只需要全局安装express-generator项目就行了。
~ npm install -g express-generator@4 #全局安装-g C:\Users\Administrator\AppData\Roaming\npm\express -> C:\Users\Administrator\AppData\Roaming\npm\node_modules\express-ge nerator\bin\express express-generator@4.11.2 C:\Users\Administrator\AppData\Roaming\npm\node_modules\express-generator ├── sorted-object@1.0.0 ├── commander@2.6.0 └── mkdirp@0.5.0 (minimist@0.0.8)
安装好express-generator包后,我们在命令行就可以使用express命令了。
~ express -V # 检查express的版本 4.11.2 ~ express -h # 检查看express的帮助命令 Usage: express [options] [dir] Options: -h, --help output usage information -V, --version output the version number -e, --ejs add ejs engine support (defaults to jade) --hbs add handlebars engine support -H, --hogan add hogan.js engine support -c, --css add stylesheet support (less|stylus|compass) (defaults to plain css) --git add .gitignore -f, --force force on non-empty directory
接下来,我们使用express的命令,来创建项目了。
~ cd D:\workspace\javascript # 进入工作目录 ~ D:\workspace\javascript>express -e nodejs-demo # 创建项目 create : nodejs-demo create : nodejs-demo/package.json create : nodejs-demo/app.js create : nodejs-demo/public/javascripts create : nodejs-demo/public/images create : nodejs-demo/public create : nodejs-demo/public/stylesheets create : nodejs-demo/public/stylesheets/style.css create : nodejs-demo/views create : nodejs-demo/views/index.ejs create : nodejs-demo/views/error.ejs create : nodejs-demo/routes create : nodejs-demo/routes/index.js create : nodejs-demo/routes/users.js create : nodejs-demo/bin create : nodejs-demo/bin/www install dependencies: $ cd nodejs-demo && npm install run the app: $ DEBUG=nodejs-demo:* ./bin/www
进入项目目录,下载依赖库,构建项目。
~ D:\workspace\javascript>cd nodejs-demo && npm install
启动项目。
~ D:\workspace\javascript\nodejs-demo>npm start > express4-demo@0.0.0 start D:\workspace\javascript\nodejs-demo > node ./bin/www module.js:338 throw err; ^ Error: Cannot find module './routes/users' at Function.Module._resolveFilename (module.js:336:15) at Function.Module._load (module.js:278:25) at Module.require (module.js:365:17) at require (module.js:384:17) at Object. (D:\workspace\javascript\nodejs-demo\app.js:9:13) at Module._compile (module.js:460:26) at Object.Module._extensions..js (module.js:478:10) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Module.require (module.js:365:17)
第一次启动发生了错误,可能是express-generator和express不匹配造成的,找到问题在app.js文件中,注释第9行和第26行。
.. //var users = require('./routes/users'); .. //app.use('/users', users); ..
再次启动项目。
D:\workspace\javascript\nodejs-demo>npm start > express4-demo@0.0.0 start D:\workspace\javascript\nodejs-demo > node ./bin/www
项目启动成功,打开浏览器 http://localhost:3000,就可以看到显示的页面了。
2 目录结构
接下来,我们详细看一下Express4项目的结构、配置和使用。bin, 存放启动项目的脚本文件
node_modules, 存放所有的项目依赖库。
public,静态文件(css,js,img)
routes,路由文件(MVC中的C,controller)
views,页面文件(Ejs模板)
package.json,项目依赖配置及开发者信息
app.js,应用核心配置文件
3 package.json项目配置
package.json用于项目依赖配置及开发者信息,scripts属性是用于定义操作命令的,可以非常方便的增加启动命令,比如默认的start,
用npm start代表执行node ./bin/www命令。查看package.json文件。
{ "name": "express4-demo", "version": "0.0.0", "private": true, "scripts": { "start": "node ./bin/www" }, "dependencies": { "body-parser": "~1.10.2", "cookie-parser": "~1.3.3", "debug": "~2.1.1", "ejs": "~2.2.3", "express": "~4.11.1", "morgan": "~1.5.1", "serve-favicon": "~2.2.0" } }
4 app.js核心文件
从Express3.x升级到Express4.x,主要的变化就在app.js文件中。查看app.js文件,我已经增加注释说明。// 加载依赖库,原来这个类库都封装在connect中,现在需地注单独加载 var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); // 加载路由控制 var routes = require('./routes/index'); //var users = require('./routes/users'); // 创建项目实例 var app = express(); // 定义EJS模板引擎和模板文件位置,也可以使用jade或其他模型引擎 app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); // 定义icon图标 app.use(favicon(__dirname + '/public/favicon.ico')); // 定义日志和输出级别 app.use(logger('dev')); // 定义数据解析器 app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); // 定义cookie解析器 app.use(cookieParser()); // 定义静态文件目录 app.use(express.static(path.join(__dirname, 'public'))); // 匹配路径和路由 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); }); // 开发环境,500错误处理和错误堆栈跟踪 if (app.get('env') === 'development') { app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: err }); }); } // 生产环境,500错误处理 app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: {} }); }); // 输出模型app module.exports = app;
我们看到在app.js中,
原来调用connect库的部分都被其他的库所代替,serve-favicon、morgan、cookie-parser、body-parser,默认项目中,只用到了最基本的几个库,还没有其他需要替换的库,在本文最后有详细列出。
另外,
原来用于项目启动代码也被移到./bin/www的文件,www文件也是一个node的脚本,用于分离配置和启动程序。查看./bin/www文件。
#!/usr/bin/env node /** * 依赖加载 */ var app = require('../app'); var debug = require('debug')('nodejs-demo:server'); var http = require('http'); /** * 定义启动端口 */ var port = normalizePort(process.env.PORT || '3000'); app.set('port', port); /** * 创建HTTP服务器实例 */ var server = http.createServer(app); /** * 启动网络服务监听端口 */ server.listen(port); server.on('error', onError); server.on('listening', onListening); /** * 端口标准化函数 */ function normalizePort(val) { var port = parseInt(val, 10); if (isNaN(port)) { return val; } if (port >= 0) { return port; } return false; } /** * HTTP异常事件处理函数 */ function onError(error) { if (error.syscall !== 'listen') { throw error; } var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port // handle specific listen errors with friendly messages 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(); var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; debug('Listening on ' + bind); }
5 Bootstrap界面框架
创建Bootstrap界面框架,直接在index.ejs文件上面做修改。可以手动下载Bootstrap库放到项目中对应的位置引用,也可以通过bower来管理前端的Javascript库,参考文章 bower解决js的依赖管理。
另外还可以直接使用免费的CDN源加载Bootstrap的css和js文件。下面我就直接使用bower来管理前端的JavaScript库的方式。编辑views/index.ejs文件:
<!DOCTYPE html> <html lang="zh-CN"> <head> <title><%= title %></title> <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css"> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <div class="well jumbotron"> <h1><%= title %></h1> <p>This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p> <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p> </div> <script src="/bower_components/jquery/dist/jquery.min.js"></script> <script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> </body> </html>
效果如下,已经加入了bootstrap的样式了。
接下来,我们
把index.ejs页面切分成3个部分:header.ejs, index.ejs, footer.ejs,用于网站页面的模块化。
header.ejs, 为页面的头部区域
index.ejs, 为内容显示区域
footer.ejs, 为页面底部区域
编辑header.ejs:
<!DOCTYPE html> <html lang="zh-CN"> <head> <title><%= title %></title> <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css"> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body>
编辑footer.ejs:
<script src="/bower_components/jquery/dist/jquery.min.js"></script> <script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> </body> </html>
编辑index.ejs:
<% include header.ejs %> <div class="well jumbotron"> <h1><%= title %></h1> <p>This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p> <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p> </div> <% include footer.ejs %>
把页表和页底的代码分离后,让index.ejs页面的核心代码更少,更容易维护。
6 路由功能
路由功能,是Express4以后全面改版的功能。在应用程序加载隐含路由中间件,不用担心在中间件被装载相对于路由器中间件的顺序。定义路由的方式是不变的,路由系统中增加2个新的功能。
app.route()函数,创建可链接的途径处理程序的路由路径。
express.Router类,创建模块化安装路径的处理程序。
app.route方法会返回一个Route实例,它可以继续使用所有的HTTP方法,包括get,post,all,put,delete,head等。
app.route('/users') .get(function(req, res, next) {}) .post(function(req, res, next) {})
express.Router类,则可以帮助我们更好的组织代码结构。在app.js文件中,定义了app.use(‘/’, routes); routes是指向了routes目录下的index.js文件,./routes/index.js文件中,express.Router被定义使用,路径/*处理都会由routes/index.js文件里的Router来处理。如果我们要管理不同的路径,那么可以直接配置为多个不同的Router。
app.use('/user', require('./routes/user').user); app.use('/admin', require('./routes/admin').admin); app.use('/', require('./routes'));
7 Ejs模板使用
让ejs模板文件,使用扩展名为html的文件。修改:app.jsapp.engine('.html', ejs.__express); app.set('view engine', 'html'); // app.set('view engine', 'ejs');
修改后,ejs变量没有定义,supervisor的程序会一直报错
ReferenceError: ejs is not defined at Object. (D:\workspace\project\nodejs-demo\app.js:17:21) at Module._compile (module.js:456:26) at Object.Module._extensions..js (module.js:474:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Function.Module.runMain (module.js:497:10) at startup (node.js:119:16) at node.js:901:3 DEBUG: Program node app.js exited with code 8
在app.js中增加ejs变量:
var express = require('express') , routes = require('./routes') , user = require('./routes/user') , http = require('http') , path = require('path') , ejs = require('ejs');
8 Session使用
session这个问题,其实是涉及到服务器的底层处理方式。像Java的web服务器,是多线程调用模型。每用户请求会打开一个线程,每个线程在内容中维护着用户的状态。像PHP的web服务器,是交行CGI的程序处理,CGI是无状态的,所以一般用cookie在客户的浏览器是维护用户的状态。但cookie在客户端维护的信息是不够的,所以CGI应用要模仿用户session,就需要在服务器端生成一个session文件存储起来,让原本无状态的CGI应用,通过中间文件的方式,达到session的效果。
Nodejs的web服务器,也是CGI的程序无状态的,与PHP不同的地方在于,单线程应用,所有请求都是异步响应,通过callback方式返回数据。如果我们想保存session数据,也是需要找到一个存储,通过文件存储,redis,Mongdb都可以。
接下来,我将演示如何通过mongodb来保存session,并实现登陆后用户对象传递。
app.js文件:
var express = require('express') , routes = require('./routes') , user = require('./routes/user') , http = require('http') , path = require('path') , ejs = require('ejs') , SessionStore = require("session-mongoose")(express); var store = new SessionStore({ url: "mongodb://localhost/session", interval: 120000 }); .... app.use(express.favicon()); app.use(express.logger('dev')); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(express.cookieParser()); app.use(express.cookieSession({secret : 'fens.me'})); app.use(express.session({ secret : 'fens.me', store: store, cookie: { maxAge: 900000 } })); app.use(function(req, res, next){ res.locals.user = req.session.user; next(); }); app.use(app.router); app.use(express.static(path.join(__dirname, 'public')));
注:app.js文件有顺序要求,一定要注意!!!
安装session-mongoose依赖库:
D:\workspace\project\nodejs-demo>npm install session-mongoose D:\workspace\project\nodejs-demo\node_modules\session-mongoose\node_modules\mongoose\node_modules\mongodb\node_modules\bson>node "D:\toolkit\nodejs\node_modules\npm\bin\node-gyp-bin\\..\..\node_modules\node-gyp\bin\node-gyp.js" rebuild C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.Cpp.InvalidPlatform.Targets(23,7): error MSB8007: 项目“kerberos.vcxproj”的平台无效。平台为“x64”。您会看到此消息的可能原因是,您尝试在没有解决方案文件的情况下生成项目,并且为 oose\node_modules\mongoose\node_modules\mongodb\node_modules\bson\build\bson.vcxproj] session-mongoose@0.2.2 node_modules\session-mongoose └── mongoose@3.6.10 (mpath@0.1.1, ms@0.1.0, hooks@0.2.1, sliced@0.0.3, muri@0.3.1, mpromise@0.2.1, mongodb@1.3.3)
安装有错误但是没关系。访问:http://localhost:3000/login,正常
修改routes/index.js文件,exports.doLogin方法:
exports.doLogin = function(req, res){ var user={ username:'admin', password:'admin' } if(req.body.username===user.username && req.body.password===user.password){ req.session.user=user; return res.redirect('/home'); } else { return res.redirect('/login'); } };
exports.logout方法:
exports.logout = function(req, res){ req.session.user=null; res.redirect('/'); };
exports.home方法:
exports.home = function(req, res){ res.render('home', { title: 'Home'}); };
这个时候session已经起作用了,exports.home的user显示传值已经被去掉了。 是通过app.js中app.use的res.locals变量,通过框架进行的赋值。
app.use(function(req, res, next){ res.locals.user = req.session.user; next(); });
注:这个session是express3.0的写法,与express2.x是不一样的。
原理是在框架内每次赋值,把我们刚才手动传值的过程,让框架去完成了
相关文章推荐
- 【深入浅出Node.js系列十】一个简单的静态文件合并服务器
- 【深入浅出Node.js系列九】一起撸Node.js
- nodeJS(express-session中间件使用)报错: Cannot set property ' ' of undefined
- 【NodeJS】log4js打印日志
- 【leetcode】24. Swap Nodes in Pairs
- node js fs模块
- NodeJs 搭建简单的聊天室
- Node入门_转
- 116. Populating Next Right Pointers in Each Node
- nodejs之初识模块化
- NodeJS、NPM安装配置步骤(windows版本)
- Windows node.js npm install express -g Cannot find module 'express'
- Swap Nodes in Pairs
- Nodejs pk Java 第1回合:初次交锋
- nodejs child_process
- YARN的入门学习心得:
- NodeJS Addon 多线程
- HDFS入门了解心得
- node前端自动化
- nodejs,npm 安装配置步骤