Express: ?web application framework for?Node.js?Express 是一個簡潔、靈活的 node.js Web 應用程式開發架構, 它提供一系列強大的特性,協助你建立各種 Web 和行動裝置應用。
目錄
此文重點介紹Express4.x(具體是4.10.4)的開發架構,其中還會涉及到Mongoose,Ejs,Bootstrap等相關內容。
建立工程
目錄結構
Express4.x設定檔
Ejs模板使用
Bootstrap介面架構
路由功能
Session使用
頁面提示
頁面存取控制
開發環境:
Ubuntu
MonogoDB: v2.6.4
nodejs:v0.11.2
npm 2.1.10 ( 如果nodejs安裝的時候是1.2.19版本,本文升級到了2.x版本)
1. 建立工程
進入工程目錄
mkdir workplace
cd workplace
全域安裝express,express作為命令被安裝到了系統中.
npm install -g express
查看express版本
express -V
注意:在express4.x版本中已經不含有express命令了。
需要安裝 express-generator
npm install express-generator -g
詳細安裝過程見:《準備Nodejs開發環境Ubuntu》
使用express命令建立工程,並支援ejs
hadoop@sven:~/workspace/nodeJs$ express -e nodejs-demo
create : nodejs-demo (項目名)
create : nodejs-demo/package.json (項目包的資訊)
create : nodejs-demo/app.js (主程式)
create : nodejs-demo/public (公開目錄)
create : nodejs-demo/public/images
create : nodejs-demo/public/javascripts
create : nodejs-demo/public/stylesheets
create : nodejs-demo/public/stylesheets/style.css
create : nodejs-demo/routes (路由目錄,以後在這個目錄下開發程式,然後在app.js裡use)
create : nodejs-demo/routes/index.js
create : nodejs-demo/routes/users.js
create : nodejs-demo/views (視圖目錄,視圖模板檔案等)
create : nodejs-demo/views/index.ejs
create : nodejs-demo/views/error.ejs
create : nodejs-demo/bin
create : nodejs-demo/bin/www (開機檔案,用於啟動app.js)
install dependencies:
$ cd nodejs-demo && npm install
run the app:
$ DEBUG=nodejs-demo ./bin/www
項目建立成功。
根據上述提示安裝依賴:
複製代碼 代碼如下:
cd nodejs-demo && npm install
根據提示啟動web:
複製代碼 代碼如下:
$ DEBUG=nodejs-demo ./bin/www
不過我們這裡不打算使用該方法啟動程式。原因是我們在開發過程中需要使用nodemon這麼一個工具。
nodemon用於動態識別開發過程中項目的改變,然後動態載入(這是Eclipse種開發java web類似)。該工具是開發web的必備啊
安裝nodemon:
npm install nodemon -g
那麼為什麼我們上面不使用./bin/www指令碼呢?
原因是nodemon ./bin/www 這樣是沒有辦法識別項目的改動。(我個人實驗的,如有知道的大牛,望賜教)
修改app.js:
把最有一行//module.exports = app;注釋掉
換成:app.listen(3000);
使用下面命令啟動app.js主程式:
hadoop@sven:~/workspace/nodeJs/nodejs-demo$ nodemon app.js
然後修改程式,看看是否會動態載入。會有下面提示:
1 Dec 16:22:07 – [nodemon] restarting due to changes…
1 Dec 16:22:07 – [nodemon] starting `node app.js`
表示生效。
測試:
本地的3000連接埠被開啟,通過瀏覽器訪問: localhost:3000
2. 目錄結構
./
drwxrwxr-x 5 hadoop hadoop 4096 12月 1 15:57 ../
-rw-rw-r– 1 hadoop hadoop 1495 12月 1 16:22 app.js
-rw-r–r– 1 hadoop hadoop 12288 12月 1 16:22 .app.js.swp
drwxr-xr-x 2 hadoop hadoop 4096 12月 1 15:57 bin/
drwxrwxr-x 9 hadoop hadoop 4096 12月 1 15:58 node_modules/
-rw-rw-r– 1 hadoop hadoop 326 12月 1 15:57 package.json
drwxr-xr-x 5 hadoop hadoop 4096 12月 1 15:57 public/
drwxr-xr-x 2 hadoop hadoop 4096 12月 1 15:57 routes/
drwxr-xr-x 2 hadoop hadoop 4096 12月 1 15:57 views/
目錄介紹:
node_modules, 存放所有的項目依賴庫。(每個專案管理自己的依賴,與Maven,Gradle等不同)
package.json,項目依賴配置及開發人員資訊
app.js,程式主入口
public,靜態檔案(css,js,img)
routes,路由檔案(MVC中的C,controller)
Views,分頁檔(Ejs模板)
nodejs-demo/bin/www (開機檔案,用於啟動app.js)
開啟app.js查看內容:
/*** 模組依賴*/var express = require('express'), routes = require('./routes'), user = require('./routes/user'), http = require('http'), path = require('path');var app = express();//環境變數app.set('port', process.env.PORT || 3000);app.set('views', __dirname + '/views');app.set('view engine', 'ejs');app.use(express.favicon());app.use(express.logger('dev'));app.use(express.bodyParser());app.use(express.methodOverride());app.use(app.router);app.use(express.static(path.join(__dirname, 'public')));// 開發模式if ('development' == app.get('env')) {app.use(express.errorHandler());}// 路徑解析app.get('/', routes.index);app.get('/users', user.list);// 啟動及連接埠http.createServer(app).listen(app.get('port'), function(){console.log('Express server listening on port ' + app.get('port'));});
4. Ejs模板使用
讓ejs模板檔案,使用副檔名為html的檔案。
修改:app.js
var ejs = require('ejs'); //引入ejs。重新安裝依賴>
app.engine('.html', ejs.__express);
app.set('view engine', 'html'); // app.set('view engine', 'ejs');
重新命名:views/*.ejs 為 views/*.html
訪問localhost:3000正確
主要要重新命名index.ejs等檔案
5. 增加Bootstrap介面架構
其實就是把js,css檔案複製到項目中對應該的目錄裡。 包括4個檔案:
複製到public/stylesheets目錄
bootstrap.min.css
bootstrap-responsive.min.css
複製到public/javascripts目錄
bootstrap.min.js
jquery-1.9.1.min.js
接下來,我們把index.html頁面切分成3個部分:header.html, index.html, footer.html
header.html, 為html頁面的頭部地區
index.html, 為內容顯示地區
footer.html,為頁面底部地區
header.html
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title><%=: title %></title><!-- Bootstrap --><link href="http://www.geedoo.info/stylesheets/bootstrap.min.css" rel="stylesheet" media="screen"><!-- <link href="http://www.geedoo.info/css/bootstrap-responsive.min.css" rel="stylesheet" media="screen"> --></head><body screen_capture_injected="true">index.html<% include header.html %><h1><%= title %></h1><p>Welcome to <%= title %></p><% include footer.html %>footer.html<script src="http://www.geedoo.info/javascripts/jquery-1.9.1.min.js"></script><script src="http://www.geedoo.info/javascripts/bootstrap.min.js"></script></body></html>
訪問localhost:3000正確。
我們已經成功的使用了EJS模板的功能,把公用的頭部和底部從頁面中分離出來了。
並已經引入了bootstrap介面架構。
6. 路由功能
我們設計一下使用者登陸業務需求。
訪問路徑:/,頁面:index.html,不需要登陸,可以直接存取。
訪問路徑:/home,頁面:home.html,必須使用者登陸後,才可以訪問。
訪問路徑:/login,頁面:login.html,登陸頁面,使用者名稱密碼輸入正確,自動跳轉到home.html
訪問路徑:/logout,頁面:無,退出登陸後,自動回到index.html頁面
開啟app.js檔案,在增加路由配置
app.get('/',routes.index);app.route('/login').get(routes.login)post(routes.doLogin);app.get('/logout',routes.logout);app.get('/home',routes.home);
註:get為get請求,post為post請求,all為所有針對這個路徑的請求
我們開啟routes/index.js檔案,增加對應的方法。
exports.index=function(req, res) { res.render('index', { title: 'index' });};exports.login=function(req,res){ res.render('login',{title: '使用者登入'});};exports.doLogin=function(req,res){ var user = { username:"admin", password:"admin"}if(req.body.username==user.username && req.body.password==user.password){ res.redirect('/home');}res.redirect('/login');};exports.logout = function(req,res){ res.redirect('/');};exports.home = function(req,res){ var user = { username:'admin', password:'admin' } res.render('home',{title:'Home',user:user});};
建立views/login.html和views/home.html兩個檔案
login.html
<% include header.html %> <div class="container-fluid"><form class="form-horizontal" method="post"><fieldset><legend>使用者登陸</legend><div class="control-group"><label class="control-label" for="username">使用者名稱</label><div class="controls"><input type="text" class="input-xlarge" id="username" name="username"></div></div><div class="control-group"><label class="control-label" for="password">密碼</label><div class="controls"><input type="password" class="input-xlarge" id="password" name="password"></div></div><div class="form-actions"><button type="submit" class="btn btn-primary">登陸</button></div></fieldset></form></div><% include footer.html %>home.html:<% include header.html %><h1>Welcome <%= user.username %>, 歡迎登陸!!</h1><a claa="btn" href="http://www.geedoo.info/logout">退出</a><% include footer.html %>
修改index.html,增加登陸連結
index.html
<% include header.html %><h1>Welcome to <%= title %></h1><p><a href="http://www.geedoo.info/login">登陸</a></p><% include footer.html %>
路由及頁面我們都寫好了,快去網站上試試吧。
7. Session使用
從剛來的例子上面看,執行exports.doLogin時,如果使用者名稱和密碼正確,我們使用redirect方法跳轉到的home
res.redirect('/home');
執行exports.home時,我們又用render渲染頁面,並把user對象傳給home.html頁面
res.render('home', { title: 'Home',user: user});
為什麼不能在doLogin時,就把user對象賦值給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 session = require('express-session');var connect = require('connect');var SessionStore = require("session-mongoose")(connect);var store = new SessionStore({url:"mongodb://localhost/session", interval: 120000});app.use(session({ secret: 'test.com', store: store, cookie:{maxAge:10000} //expire session in 10 seconds}));//用於把登入使用者佈建到res.locals裡面,在home.html裡顯示app.use(function(req,res,next){ res.locals.user = req.session.user; console.log('Session is = ',req.session.user); next();});
需要添加中介軟體connect、session-mongoose。
其中session-mongoose是用於串連mongodb資料庫。然後自動寫入session資訊到資料庫中(也可以用mongoose中介軟體,但是要自己實現session的寫入)
app.use(session(….)) 這裡是全域設定了session的資訊及session資訊儲存的方式。
註:app.js檔案有順序要求,一定要注意!!!
安裝session-mongoose依賴庫
npm install connect
npm install session-mongoose
npm install session-mongoose
安裝有錯誤但是沒關係。
訪問: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是express4.10.4的寫法,與express4之前的版本是不一樣的。
訪問頁面可以查看mongodb有沒有資料:
nodejs-mongodb
nodejs-mongodb
由於上面配置的 cookie:{maxAge:10000} //expire session in 10 seconds
到期時間,因此你會看到mongodb裡面的資料過一段時間就被清除了。
參考:
Mongoose:http://mongoosejs.com/
關於express4.2.0與express3.x操作的區別:http://blog.csdn.net/u013758116/article/details/38758351
8. 頁面提示
登陸的大體我們都已經講完了,最後看一下登陸失敗的情況。
我們希望如果使用者登陸時,使用者名稱或者密碼出錯了,會給使用者提示,應該如何去實現。
開啟app.js的,增加res.locals.message
登陸的大體我們都已經講完了,最後看一下登陸失敗的情況。
我們希望如果使用者登陸時,使用者名稱或者密碼出錯了,會給使用者提示,應該如何去實現。
開啟app.js的,增加res.locals.message
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-danger">' + err + '</div>'; next();});
修改login.html頁面,<%- message %>
<% include header.html %><div class="container-fluid"><form class="form-horizontal" method="post"><fieldset><legend>使用者登陸</legend><%- message %><div class="control-group"><label class="control-label" for="username">使用者名稱</label><div class="controls"><input type="text" class="input-xlarge" id="username" name="username" value="admin"></div></div><div class="control-group"><label class="control-label" for="password">密碼</label><div class="controls"><input type="password" class="input-xlarge" id="password" name="password" value="admin"></div></div><div class="form-actions"><button type="submit" class="btn btn-primary">登陸</button></div></fieldset></form></div><% include footer.html %>
修改routes/index.js,增加req.session.error
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 { req.session.error='使用者名稱或密碼不正確'; return res.redirect('/login'); }};
讓我們來看看效果: http://localhost:3000/login 輸入錯誤的和密碼, 使用者名稱:dad,密碼:da
boostrap-nodejs
9. 頁面存取控制
網站登陸部分按照我們的求已經完成了,但網站並不安全。
localhost:3000/home,頁面本來是登陸以後才訪問的,現在我們不要登陸,直接在瀏覽器輸入也可訪問。
頁面報錯了,提示<%= user.username %> 變數出錯。
GET /home?user==a 500 15ms
TypeError: D:\workspace\project\nodejs-demo\views\home.html:2
1| <% include header.html %>
>> 2| <h1>Welcome <%= user.username %>, 歡迎登陸!!</h1>
3| <a claa="btn" href="http://www.geedoo.info/logout">退出</a>
4| <% include header.html %>
Cannot read property 'username' of null
at eval (eval at <anonymous> (D:\workspace\project\nodejs-demo\node_modules\ejs\lib\ejs.js:
at eval (eval at <anonymous> (D:\workspace\project\nodejs-demo\node_modules\ejs\lib\ejs.js:
at D:\workspace\project\nodejs-demo\node_modules\ejs\lib\ejs.js:249:15
at Object.exports.render (D:\workspace\project\nodejs-demo\node_modules\ejs\lib\ejs.js:287:
at View.exports.renderFile [as engine] (D:\workspace\project\nodejs-demo\node_modules\ejs\l
at View.render (D:\workspace\project\nodejs-demo\node_modules\express\lib\view.js:75:8)
at Function.app.render (D:\workspace\project\nodejs-demo\node_modules\express\lib\applicati
at ServerResponse.res.render (D:\workspace\project\nodejs-demo\node_modules\express\lib\res
at exports.home (D:\workspace\project\nodejs-demo\routes\index.js:36:8)
at callbacks (D:\workspace\project\nodejs-demo\node_modules\express\lib\router\index.js:161
這個頁面被開啟發,因為沒有user.username參數。我們避免這樣的錯誤發生。
還記錄路由部分裡說的get,post,all的作用嗎?我現在要回到路由配置中,再做點事情。
修改app.js檔案
app.get('/',routes.index);
app.route('/login')
.all(notAuthentication)
.get(routes.login)
.post(routes.doLogin);
app.route('/logout')
app.get('/',routes.index);
app.route('/login')
.all(notAuthentication)
.get(routes.login)
.post(routes.doLogin);
app.route('/logout')
.get(authentication)
.get(routes.logout);
app.route('/home')
.get(authentication)
.get(routes.home);
存取控制:
/ ,誰訪問都行,沒有任何控制
/login,用all攔截所有訪問/login的請求,先調用authentication,使用者登陸檢查
/logout,用get攔截訪問/login的請求,先調用notAuthentication,使用者不登陸檢查
/home,用get攔截訪問/home的請求,先調用Authentication,使用者登陸檢查
修改app.js檔案,增加authentication,notAuthentication兩個方法
function authentication(req, res, next) { if (!req.session.user) { req.session.error='請先登陸'; return res.redirect('/login'); } next();}function notAuthentication(req, res, next) { if (req.session.user) { req.session.error='已登陸'; return res.redirect('/home'); } next();}
配置好後,我們未登陸,直接存取localhost:3000/home時或者localhost:3000/logout,就會跳到/login頁面
登入後, 訪問localhost:3000/login 則直接跳到/home頁面
到此,express4 相關內容到此為止。
以上內容是小編給大家分享的Nodejs Express4.x開發架構隨手筆記,希望大家喜歡。