?
目錄
- 開發環境
- 1、建立工程
- 2、目錄結構
- 3、Express配置文件
- 4、Ejs模板
- 5、安裝常用庫及頁面分離
- 6、路由
- 7、session
- 8、頁面訪問控制及提示
JS是腳本語言,腳本語言都需要一個解析器才能運行。對于寫在HTML頁面里 的JS,瀏覽器充當了解析器的角色。而對于需要獨立運行的JS,NodeJS就是一個解析器。每一種解析器都是一個運行環境,不但允許JS定義各種數據結 構,進行各種計算,還允許JS使用運行環境提供的內置對象和方法做一些事情。例如運行在瀏覽器中的JS的用途是操作DOM,瀏覽器就提供了 document之類的內置對象。而運行在NodeJS中的JS的用途是操作磁盤文件或搭建HTTP服務器,NodeJS就相應提供了fs、http等內 置對象。Express作為NodeJS的Web應用框架,可以幫助我們快速開發Web網站。
開發環境
- NodeJS:v0.10.30
- npm:1.4.21
- OS:Win7旗艦版 32bit
- Express:4.2.0
- MongoDB:2.6.3 12
E:\project> node -v
v0.
10.30
E:\project> npm -v
1.4
.
21
E:\project> express -V
4.2
.
0
?
?
1、建立工程
使用express命令建立工程,并支持ejs:
?
根據提示下載依賴包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | E:\project> cd .\nodejs-demo E:\project\nodejs-demo> npm install npm WARN deprecated static-favicon@1.0.2: use serve-favicon module static-favicon@1.0.2 node_modules\static-favicon debug@0.7.4 node_modules\debug ejs@0.8.8 node_modules\ejs cookie-parser@1.0.1 node_modules\cookie-parser ├── cookie-signature@1.0.3 └── cookie@0.1.0 morgan@1.0.1 node_modules\morgan └── bytes@0.3.0 body-parser@1.0.2 node_modules\body-parser ├── qs@0.6.6 ├── raw-body@1.1.7 (bytes@1.0.0, string_decoder@0.10.25-1) └── type -is@1.1.0 (mime@1.2.11) express@4.2.0 node_modules\express ├── parseurl@1.0.1 ├── utils-merge@1.0.0 ├── cookie@0.1.2 ├── merge-descriptors@0.0.2 ├── escape-html@1.0.1 ├── range-parser@1.0.0 ├── fresh@0.2.2 ├── cookie-signature@1.0.3 ├── debug@0.8.1 ├── methods@1.0.0 ├── buffer-crc32@0.2.1 ├── serve-static@1.1.0 ├── path-to-regexp@0.1.2 ├── qs@0.6.6 ├── send@0.3.0 (debug@0.8.0, mime@1.2.11) ├── accepts@1.0.1 (negotiator@0.4.7, mime@1.2.11) └── type -is@1.1.0 (mime@1.2.11) E:\project\nodejs-demo> |
?
?
工程建立成功,啟動服務:
1 | E:\project\nodejs-demo> npm start > nodejs-demo@0.0.1 start E:\project\nodejs-demo > node . /bin/www |
?
?
?
本地3000端口被打開,在瀏覽器地址欄輸入localhost:3000,訪問成功。
2、目錄結構
- bin——存放命令行程序。
- node_modules——存放所有的項目依賴庫。
- public——存放靜態文件,包括css、js、img等。
- routes——存放路由文件。
- views——存放頁面文件(ejs模板)。
- app.js——程序啟動文件。
- package.json——項目依賴配置及開發者信息。
1 2 3 4 5 6 7 8 9 10 11 | E:\project\nodejs-demo> dir ???? 目錄: E:\project\nodejs-demo Mode??????????????? LastWriteTime???? Length Name ----??????????????? -------------???? ------ ---- d----???????? 2014 /8/16 ???? 21:55 bin d----???????? 2014 /8/16 ???? 22:03 node_modules d----???????? 2014 /8/16 ???? 21:55 public d----???????? 2014 /8/16 ???? 21:55 routes d----???????? 2014 /8/16 ???? 21:55 views -a---???????? 2014 /8/16 ???? 21:55?????? 1375 app.js -a---???????? 2014 /8/16 ???? 21:55??????? 327 package.json |
?
?
3、Express配置文件
打開app.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | var express = require( 'express' ); var path = require( 'path' ); var favicon = require( 'static-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(); // view engine setup app.set( 'views' , path.join(__dirname, 'views' )); app.set( 'view engine' , 'ejs' ); app.use(favicon()); app.use(logger( 'dev' )); app.use(bodyParser.json()); app.use(bodyParser.urlencoded()); app.use(cookieParser()); app.use(express. static (path.join(__dirname, 'public' ))); app.use( '/' , routes); app.use( '/users' , users); /// catch 404 and forward to error handler app.use( function (req, res, next) { ???? var err = new Error( 'Not Found' ); ???? err.status = 404; ???? next(err); }); /// error handlers // development error handler // will print stacktrace 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 ???????? }); ???? }); } // production error handler // no stacktraces leaked to user app.use( function (err, req, res, next) { ???? res.status(err.status || 500); ???? res.render( 'error' , { ???????? message: err.message, ???????? error: {} ???? }); }); module.exports = app; |
?
?
4、Ejs模板
修改app.js,讓ejs模板文件使用擴展名為html的文件:
1 2 3 4 5 | 13 // view engine setup 14 app.set( 'views' , path.join(__dirname, 'views' )); 15 //app.set('view engine', 'ejs'); 16 app.engine( 'html' , require( 'ejs' ).renderFile); 17 app.set( 'view engine' , 'html' ); |
?
?
?
修改完成后,重命名views/index.ejs為views/index.html。重啟服務,訪問成功。
5、安裝常用庫及頁面分離
添加bootstrap和jQuery:
E:\project\nodejs-demo> npm install bootstrap bootstrap@3.2.0 node_modules\bootstrap E:\project\nodejs-demo> npm install jquery jquery@2.1.1 node_modules\jquery E:\project\nodejs-demo>
接下來,把index.html分成三個部分:
- header.html——頁面頭部區域。
- index.html——頁面內容區域。
- footer.html——頁面底部區域。
header.html
1 2 3 4 5 6 7 8 9 | <! DOCTYPE html> < html lang = "en" > < head > ???? < meta charset = "utf-8" > ???? < title ><%= title %></ title > ???? <!-- Bootstrap --> ???? < link href = "/stylesheets/bootstrap.min.css" rel = "stylesheet" media = "screen" > </ head > < body screen_capture_injected = "true" > |
?
?
index.html
1 2 3 4 | 1 <% include header.html %> 2 < h1 ><%= title %></ h1 > 3 < p >Welcome to <%= title %></ p > 4 <% include footer.html %> |
?
?
footer.html?
1 2 3 4 5 6 | <script src= "/javascripts/jquery.min.js" ></script> ? <script src= "/javascripts/bootstrap.min.js" ></script> ? </body> ? </html> |
?
?
?
重啟服務,訪問成功。
6、路由
登錄設計:
訪問路徑 | 頁面 | 描述 |
/ | index.html | 不需要登錄,可以直接訪問。 |
/home | home.html | 必須用戶登錄以后,才可以訪問。 |
/login | login.html | 登錄頁面,用戶名密碼輸入正確,自動跳轉到home.html。 |
/logout | 無 | 退出登錄后,自動跳轉到index.html。 |
打開app.js文件,增加路由配置:
1 2 3 4 5 | 26 app.use( '/' , routes); 27 app.use( '/users' , users); 28 app.use( '/login' , routes); 29 app.use( '/logout' , routes); 30 app.use( '/home' , routes); |
?
?
打開routes/index.js文件,添加對應方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | var express = require( 'express' ); var router = express.Router(); /* GET home page. */ router.get( '/' , function (req, res) { ?? res.render( 'index' , { title: 'Express' }); }); router.route( '/login' ) .get( function (req, res) { ???? res.render( 'login' , { title: '用戶登錄' }); }) .post( function (req, res) { ???? var user={ ???????? username: 'admin' , ???????? password: '123456' ???? } ???? if (req.body.username === user.username && req.body.password === user.password){ ???????? res.redirect( '/home' ); ???? } ???? res.redirect( '/login' ); }); router.get( '/logout' , function (req, res) { ???? res.redirect( '/' ); }); router.get( '/home' , function (req, res) { ???? var user={ ???????? username: 'admin' , ???????? password: '123456' ???? } ???? res.render( 'home' , { title: 'Home' , user: user }); }); module.exports = router; |
?
?
創建views/login.html和views/home.html兩個文件:
login.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <% include header.html %> < div class = "container" > ???? < form class = "col-sm-offset-4 col-sm-4 form-horizontal" role = "form" method = "post" > ???????? < fieldset > ???????????? < legend >用戶登錄</ legend > ???????????? < div class = "form-group" > ???????????????? < label class = "col-sm-3 control-label" for = "username" >用戶名</ label > ???????????????? < div class = "col-sm-9" > ???????????????????? < input type = "text" class = "form-control" id = "username" name = "username" placeholder = "用戶名" required> ???????????????? </ div > ???????????? </ div > ???????????? < div class = "form-group" > ???????????????? < label class = "col-sm-3 control-label" for = "password" >密碼</ label > ???????????????? < div class = "col-sm-9" > ???????????????????? < input type = "password" class = "form-control" id = "password" name = "password" placeholder = "密碼" required> ???????????????? </ div > ???????????? </ div > ???????????? < div class = "form-group" > ???????????????? < div class = "col-sm-offset-3 col-sm-9" > ???????????????????? < button type = "submit" class = "btn btn-primary" >登錄</ button > ???????????????? </ div > ???????????? </ div > ???????? </ fieldset > ???? </ form > </ div > <% include footer.html %> |
?
?
home.html
1 2 3 4 | 1 <% include header.html %> 2 <h1>Welcome <%= user.username %>, 歡迎登錄!!</h1> 3 <a class = "btn" href= "/logout" >退出</a> 4 <% include footer.html %> |
?
?
修改index.html,增加登錄鏈接:
?
1 2 3 4 | 1 <% include header.html %> 2???? < h1 >Welcome to <%= title %></ h1 > 3???? < p >< a href = "/login" >登錄</ a ></ p > 4 <% include footer.html %> |
?
?
路由及頁面已準備好,重啟服務,訪問成功。?
7、session
安裝中間件express-session:
1 2 3 4 5 | E:\project\nodejs-demo> npm install express-session express-session@1.7.5 node_modules\express-session ├── cookie@0.1.2 ├── cookie-signature@1.0.4 ├── on-headers@1.0.0 ├── utils-merge@1.0.0 ├── parseurl@1.3.0 ├── buffer-crc32@0.2.3 ├── depd@0.4.4 ├── debug@1.0.4 (ms@0.6.2) └── uid-safe@1.0.1 (base64-url@1.0.0, mz@1.0.0) E:\project\nodejs-demo> |
?
?
安裝中間件connect-mongodb:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | E:\project\nodejs-demo> npm install connect-mongodb \ > kerberos@0.0.3 install E:\project\nodejs-demo\node_modules\connect-mongodb\nod e_modules\mongodb\node_modules\kerberos > (node-gyp rebuild 2> builderror.log) || ( exit 0) | E:\project\nodejs-demo\node_modules\connect-mongodb\node_modules\mongodb\node_mo dules\kerberos>node "C:\Program Files\nodejs\node_modules\npm\bin\node-gyp-bin\\ ..\..\node_modules\node-gyp\bin\node-gyp.js" rebuild | > bson@0.2.11 install E:\project\nodejs-demo\node_modules\connect-mongodb\node_m odules\mongodb\node_modules\bson > (node-gyp rebuild 2> builderror.log) || ( exit 0) E:\project\nodejs-demo\node_modules\connect-mongodb\node_modules\mongodb\node_mo dules\bson>node "C:\Program Files\nodejs\node_modules\npm\bin\node-gyp-bin\\..\. .\node_modules\node-gyp\bin\node-gyp.js" rebuild connect-mongodb@1.1.5 node_modules\connect-mongodb ├── connect@1.9.2 (mime@1.2.11, formidable@1.0.15, qs@1.2.2) └── mongodb@1.4.8 (kerberos@0.0.3, readable-stream@1.0.27-1, bson@0.2.11) E:\project\nodejs-demo> |
?
?
安裝中間件mongodb:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | E:\project\nodejs-demo> npm install mongodb - > kerberos@0.0.3 install E:\project\nodejs-demo\node_modules\mongodb\node_module s\kerberos > (node-gyp rebuild 2> builderror.log) || ( exit 0) - E:\project\nodejs-demo\node_modules\mongodb\node_modules\kerberos>node "C:\Progr am Files\nodejs\node_modules\npm\bin\node-gyp-bin\\..\..\node_modules\node-gyp\b in \node-gyp.js" rebuild | > bson@0.2.11 install E:\project\nodejs-demo\node_modules\mongodb\node_modules\b son > (node-gyp rebuild 2> builderror.log) || ( exit 0) E:\project\nodejs-demo\node_modules\mongodb\node_modules\bson>node "C:\Program F iles\nodejs\node_modules\npm\bin\node-gyp-bin\\..\..\node_modules\node-gyp\bin\n ode-gyp.js" rebuild mongodb@1.4.8 node_modules\mongodb ├── kerberos@0.0.3 ├── readable-stream@1.0.27-1 (isarray@0.0.1, string_decoder@0.10.25-1, inheri ts@2.0.1, core-util-is@1.0.1) └── bson@0.2.11 (nan@1.2.0) E:\project\nodejs-demo> |
?
?
添加database/settings.js和database/msession.js這兩個文件:
settings.js
1 2 3 4 5 6 7 8 9 | module.exports = { ???? COOKIE_SECRET: 'ywang1724.com' , ???? URL: 'mongodb://127.0.0.1:27017/nodedb' , ???? DB: 'nodedb' , ???? HOST: '127.0.0.1' , ???? PORT: 27017, ???? USERNAME: 'admin' , ???? PASSWORD: '123456' }; |
?
?
msession.js
?
1 2 3 4 5 6 | 1 var Settings = require( './settings' ); 2 var Db = require( 'mongodb' ).Db; 3 var Server = require( 'mongodb' ).Server; 4 var db = new Db(Settings.DB, new Server(Settings.HOST, Settings.PORT, {auto_reconnect: true , native_parser: true }),{safe: false }); 5 6 module.exports = db; |
?
?
?
修改app.js文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | var express = require( 'express' ); var path = require( 'path' ); var favicon = require( 'static-favicon' ); var logger = require( 'morgan' ); var cookieParser = require( 'cookie-parser' ); var bodyParser = require( 'body-parser' ); //采用connect-mongodb中間件作為Session存儲? var session = require( 'express-session' );? var Settings = require( './database/settings' );? var MongoStore = require( 'connect-mongodb' );? var db = require( './database/msession' ); var routes = require( './routes/index' ); var users = require( './routes/users' ); var app = express(); // view engine setup app.set( 'views' , path.join(__dirname, 'views' )); //app.set('view engine', 'ejs'); app.engine( 'html' , require( 'ejs' ).renderFile); app.set( 'view engine' , 'html' ); app.use(favicon()); app.use(logger( 'dev' )); app.use(bodyParser.json()); app.use(bodyParser.urlencoded()); app.use(cookieParser()); //session配置 app.use(session({ ???? cookie: { maxAge: 600000 }, ???? secret: Settings.COOKIE_SECRET, ???? store: new MongoStore({? ???????? username: Settings.USERNAME, ???????? password: Settings.PASSWORD, ???????? url: Settings.URL, ???????? db: db}) })) app.use( function (req, res, next){ ???? res.locals.user = req.session.user; ???? next(); }); app.use(express. static (path.join(__dirname, 'public' ))); ...... |
?
?
修改index.js文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | var express = require( 'express' ); var router = express.Router(); /* GET home page. */ router.get( '/' , function (req, res) { ???? res.render( 'index' , { title: 'Express' }); }); router.route( '/login' ) .get( function (req, res) { ???? res.render( 'login' , { title: '用戶登錄' }); }) .post( function (req, res) { ???? var user = { ???????? username: 'admin' , ???????? password: '123456' ???? } ???? if (req.body.username === user.username && req.body.password === user.password){ ???????? req.session.user = user; ???????? res.redirect( '/home' ); ???? } else { ???????? res.redirect( '/login' ); ???? } }); router.get( '/logout' , function (req, res) { ???? req.session.user = null ; ???? res.redirect( '/' ); }); router.get( '/home' , function (req, res) { ???? res.render( 'home' , { title: 'Home' }); }); module.exports = router; |
?
?
本地安裝數據庫MongoDB,新建用戶nodedb。重啟服務,訪問成功。
8、頁面訪問控制及提示
訪問控制設計:
訪問路徑 | 描述 |
/ | 任何人都可以訪問,不需要認證。 |
/home | 攔截get請求,調用authentication()進行認證,不通過則自動跳轉到登錄頁面。 |
/login | 任何人都可以訪問,不需要認證。 |
/logout | 任何人都可以訪問,不需要認證。 |
修改index.js文件:
1 2 3 4 5 6 7 8 9 10 | router.get( '/home' , function (req, res) { ???? authentication(req, res); ???? res.render( 'home' , { title: 'Home' }); }); function authentication(req, res) { ???? if (!req.session.user) { ???????? return res.redirect( '/login' ); ???? } } |
?
?
重啟服務,訪問成功。
添加頁面提示,修改app.js文件,增加res.locals.message:
1 2 3 4 5 6 7 8 9 10 | app.use( function (req, res, next) { ???? res.locals.user = req.session.user; ???? var err = req.session.error; ???? delete req.session.error; ???? res.locals.message = '' ; ???? if (err) { ???????? res.locals.message = '<div class="alert alert-warning">' + err + '</div>' ; ???? } ???? next(); }); |
?
?
修改index.js文件,增加req.session.error:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | var express = require( 'express' ); var router = express.Router(); /* GET home page. */ router.get( '/' , function (req, res) { ???? res.render( 'index' , { title: 'Express' }); }); router.route( '/login' ) .get( function (req, res) { ???? if (req.session.user) { ???????? res.redirect( '/home' ); ???? } ???? res.render( 'login' , { title: '用戶登錄' }); }) .post( function (req, res) { ???? var user = { ???????? username: 'admin' , ???????? password: '123456' ???? } ???? if (req.body.username === user.username && req.body.password === user.password) { ???????? req.session.user = user; ???????? res.redirect( '/home' ); ???? } else { ???????? req.session.error= '用戶名或密碼不正確' ; ???????? res.redirect( '/login' ); ???? } }); router.get( '/logout' , function (req, res) { ???? req.session.user = null ; ???? res.redirect( '/' ); }); router.get( '/home' , function (req, res) { ???? authentication(req, res); ???? res.render( 'home' , { title: 'Home' }); }); function authentication(req, res) { ???? if (!req.session.user) { ???????? req.session.error= '請先登錄' ; ???????? return res.redirect( '/login' ); ???? } } module.exports = router; |
?
?
修改login.html,增加<%- message %>:
?
1 2 3 | 5 <legend>用戶登錄</legend> 6 <%- message %> 7 <div class = "form-group" > |
?
?
?
重啟服務,訪問成功。輸入錯誤用戶名密碼: