12_Node.js Web 開發_部落格網站
下面開始用 Node.js 進行 Web 開發。
我是通過《Node.js開發指南》這本書來學習 Node.js Web 開發的,書中使用的 Express 框架是 2.5.8,而我的是 4.14.1,所以遇到了許多問題,在文章中我都有提到並講解。
一、快速開始
1、建立專案
《Node.js開發指南》中建立專案的方式是:express -t ejs microblog,但是這種方式對於高版本的 Express 新建的標籤替換引擎並不是 .ejs,而是 .jade,如果要使用 .ejs 我們可以通過下面命令建立網站基本結構。
express -e NodeJSBlog 複製程式碼
執行命令後在當前目錄下出現了一些檔案,並且下邊提示我們通過 npm install 安裝依賴。



在 npm install 之後,開啟 NodeJSBlog 目錄下的 package.json,可以看到已安裝的包及對應的版本號。

2、啟動伺服器
注意,我們之前開啟 Node.js 伺服器,都是執行 node xxx.js,然後去瀏覽器訪問即可,但是 Express 4.x 以上就不是這種方式了,應該是 npm start,埠配置在 bin/www 中。


啟動成功訪問 localhost:3000/。

3、專案結構
我們看一下 express 在 NodeJSBlog 這個目錄下都生成了哪些檔案。

app.js
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 index = 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'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', index); 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 handler app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); module.exports = app; 複製程式碼
app.js 是專案的入口,首先引入了一系列我們所需要的模組,然後引入了 routes 目錄下的兩個本地模組,它的功能是為指定路徑組織返回內容,相當於 MVC 架構中的控制器。
接下來是檢視引擎設定, app.set() 是 Express 的引數設定工具,接受一個鍵(key)和一個值(value),可用的引數如下所示:
- basepath:基礎地址,通常用於 res.redirect() 跳轉。
- views:檢視檔案的目錄,存放模板檔案。
- view engine:檢視模板引擎。
- view options:全域性檢視引數物件。
- view cache:啟用檢視快取。
- case sensitive routes:路徑區分大小寫。
- strict routing:嚴格路徑,啟用後不會忽略路徑末尾的“ / ”。
- jsonp callback:開啟透明的 JSONP 支援
Express 依賴於 connect,提供了大量的中介軟體,可以通過 app.use() 啟用
routes/index.js
var express = require('express'); var router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { res.render('index', { title: 'Express' }); }); module.exports = router; 複製程式碼
routes/index.js 是路由檔案,相當於控制器,用於組織展示的內容,app.js 中通過 app.get('/', routes.index); 將“ / ”路徑對映到 exports.index 函式下,其中只有一個語句 res.render('index', { title: 'Express' }),功能是呼叫模板解析引擎,翻譯名為 index 的模板,並傳入一個物件作為引數,這個物件只有一個屬性,即 title: 'Express'。
views/index.ejs
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> </body> </html> 複製程式碼
index.ejs 是模板檔案,即 routes/index.js 中呼叫的模板,內容是:
<h1><%= title %></h1> <p>Welcome to <%= title %></p> 複製程式碼
它的基礎是 HTML 語言,其中包含了形如 <%= title %> 的標籤,功能是顯示引用的變數,即 res.render 函式第二個引數傳入的物件的屬性。
補充 include
在書中 views 目錄下是有 layout.ejs 的,它可以讓所有模板去繼承它,<%- body %> 中是獨特的內容,其他部分是共有的,可以看作是頁面框架。
書中 layout.ejs:
<head> <title> <%= title %> </title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <%- body %> </body> </html> 複製程式碼
但是 Express 4.x 就沒有 layout.ejs 了,解決方法:
官方推薦了 include 方式,它不僅能實現 layout 的功能,還是將 view 的那些可複用的 html 片段提取成模組,在需要使用的地方直接用 <% include xxx %>。
例如先在 views 目錄下新建一個 public_file.ejs ,在裡面新增需要引用的公共檔案:
<link rel='stylesheet' href='/stylesheets/style.css' /> 複製程式碼
然後修改一下 index.ejs,使用 <% include listitem %> 方式引用上邊公共檔案:
<!DOCTYPE html> <html> <head> <title><%= title %></title> <% include public_file %> </head> <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> </body> </html> 複製程式碼
重啟服務,訪問 localhost:3000/,可以看到 style.css 檔案正常引用。

每一次修改我們都需要重啟服務才能看到修改後的結果,您可以看我的 《Node.js 應用程式自動重啟》 這篇文章去安裝 supervisor 或 nodemon 兩個外掛來實現應用程式自動重啟。
二、路由控制
1、建立頁面路由
簡單說一下新增一個頁面的流程。
首先在 views 目錄下新建一個模板,例如 hello.ejs:

然後開啟 index.js 檔案,新增頁面路由資訊:

訪問 localhost:3000/hello。

補充:在《Node.js開發指南》這本書中,還需要向 app.js 檔案中新增頁面路由資訊,但在 Express 4.x 中是不需要的。
2、路徑匹配
上面的例子是為固定的路徑設定路由規則,Express 還支援更高階的路徑匹配模式,例如我們想要展示一個使用者的個人頁面,路徑為 /user/[username],可以用下面的方法定義路由 規則:
router.get('/user/:username', function(req, res, next) { res.send('user: ' + req.params.username); }); 複製程式碼
重啟專案,訪問 localhost:3000/user/LiuZhenghe。

注意:呼叫模板解析引擎,用 res.render(),只是向頁面傳送資料,用 res.send()。
路徑規則 /user/:username 會被自動編譯為正則表示式,類似於 /user/([^/]+)/? 這樣的形式,路徑引數可以在響應函式中通過 req.params 的屬性訪問。
路徑規則同樣支援 JavaScript 正則表示式,例如 app.get(/user/([^/]+)/?,callback),這樣的好處在於可以定義更加複雜的路徑規則,而不同之處是匹配的引數是匿名的,因此需要通過 req.params[0]、req.params[1] 這樣的形式訪問。
3、REST 風格的路由規則
Express 支援 REST 風格的請求方式,在介紹之前我們先說明一下什麼是 REST。
REST 的意思是表徵狀態轉移(Representational State Transfer),它是一種基於 HTTP 協議的網路應用的介面風格,充分利用 HTTP 的方法實現統一風格介面的服務。
HTTP 協議定義了以下 8 種標準的方法:
- GET:請求獲取指定資源。
- HEAD:請求指定資源的響應頭。
- POST:向指定資源提交資料。
- PUT:請求伺服器儲存一個資源。
- DELETE:請求伺服器刪除指定資源。
- TRACE:回顯伺服器收到的請求,主要用於測試或診斷。
- CONNECT:HTTP/1.1 協議中預留給能夠將連線改為管道方式的代理伺服器。
- OPTIONS:返回伺服器支援的HTTP請求方法。
其中我們經常用到的是 GET、POST、PUT 和 DELETE 方法,根據 REST 設計模式,這4種方法通常分別用於實現以下功能。
- GET:獲取
- POST:新增
- PUT:更新
- DELETE:刪除
這是因為這 4 種方法有不同的特點,按照定義,它們的特點如下表所示:
請求方式 | 安全 | 冪等 |
---|---|---|
GET | 是 | 是 |
POST | 否 | 否 |
PUT | 否 | 是 |
DELETE | 否 | 是 |
所謂安全是指沒有副作用,即請求不會對資源產生變動,連續訪問多次所獲得的結果不受訪問者的影響,而冪等指的是重複請求多次與一次請求的效果是一樣的,比如獲取和更新操作是冪等的,這與新增不同,刪除也是冪等的,即重複刪除一個資源,和刪除一次是一樣的。
Express 對每種 HTTP 請求方法都設計了不同的路由繫結函式,例如前面例子全部是 app.get,表示為該路徑綁定了 GET 請求,向這個路徑發起其他方式的請求不會被響應。
下表是 Express 支援的所有 HTTP 請求的繫結函式。
請求方式 | 繫結函式 |
---|---|
GET | app.get(path, callback) |
POST | app.post(path, callback) |
PUT | app.put(path, callback) |
DELETE | app.delete(path, callback) |
PATCH | app.patch(path, callback) |
TRACE | app.trace(path, callback) |
CONNECT | app.connect(path, callback) |
OPTIONS | app.options(path, callback) |
所有方法 | app.all(path, callback) |
例如我們要繫結某個路徑的 POST 請求,則可以用 app.post(path, callback) 的 方法,需要注意的是 app.all 函式,它支援把所有的請求方式繫結到同一個響應函式,是一個非常靈活的函式,在後面我們可以看到許多功能都可以通過它來實現。
4、控制權轉移
Express 支援同一路徑繫結多個路由響應函式,例如:
index.js
// ... /* 路徑匹配模式 */ router.all('/user/:username', function(req, res, next) { res.send('all methods captured'); }); router.get('/user/:username', function(req, res, next) { res.send('user: ' + req.params.username); }); // ... 複製程式碼
當再次訪問 localhost:3000/user/LiuZhenghe 時,發現頁面被第一條路由規則捕獲。

原因是 Express 在處理路由規則時,會優先匹配先定義的路由規則,因此後面相同的規則被遮蔽。
Express 提供了路由控制權轉移的方法,即回撥函式的第三個引數 next,通過呼叫 next(),會將路由控制權轉移給後面的規則,例如:
// ... /* 路徑匹配模式 */ router.all('/user/:username', function(req, res, next) { console.log('all methods captured'); next(); }); router.get('/user/:username', function(req, res, next) { res.send('user: ' + req.params.username); }); // ... 複製程式碼
此時重新整理頁面,在控制檯可以看到“all methods captured”,瀏覽器顯示了 user: LiuZhenghe。

這是一個非常有用的工具,可以讓我們輕易地實現中介軟體,而且還能提高程式碼的複用程度,例如我們針對一個使用者查詢資訊和修改資訊的操作,分別對應了 GET 和 PUT 操作,而兩者共有的一個步驟是檢查使用者名稱是否合法,因此可以通過 next() 方法實現。
三、模板引擎
模板引擎也就是檢視,檢視決定了使用者最終能看到什麼,這裡我們用 ejs 為例介紹模板引擎的使用方法。
1、使用模板引擎
在 app.js 中,以下兩句設定了模板引擎和頁面模板的位置:
app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); 複製程式碼
在 index.js 中通過 res.render() 呼叫模板,res.render() 的功能是呼叫模板引擎,並將其產生的頁面直接返回給客戶端,它接受兩個引數,第一個是模板的名稱,即 views 目錄下的模板檔名,不包含檔案的副檔名;第二個引數是傳遞給模板的資料,用於模板翻譯。
ejs 的標籤系統非常簡單,它只有以下 3 種標籤:
- <% code %>:JavaScript 程式碼。
- <%= code %>:顯示替換過 HTML 特殊字元的內容。
- <%- code %>:顯示原始 HTML 內容。
我們可以用它們實現頁面模板系統能實現的任何內容。
2、片段檢視
《Node.js開發指南》中所講的片段檢視(partials)在 Express 4.x 中已經不支援了,在上面專案結構分析那一節中我曾補充過 include,這裡再次介紹一下它的用法。
官方推薦了 include 方式,它不僅能實現 layout 的功能,還是將 view 的那些可複用的 html 片段提取成模組,在需要使用的地方直接用 <% include xxx %>,看下面這個例子:
首先在 index.js 中新增以下內容:
// 片斷檢視 router.get('/list', function(reg, res) { res.render('list', { title: "List", items: [2019, 'Node.js', 'NodeJSBlog', 'Express'] }); }); 複製程式碼
然後新建 list.ejs 檔案並新增以下內容:
<ul> <% items.forEach(function(listitem){ %> <% include listitem %> <% }) %> </ul> 複製程式碼
同時新建 listitem.ejs 檔案並新增:
<li><%= listitem %></li> 複製程式碼
訪問 localhost:3000/list,可以看到以下內容:

四、開始建立部落格網站
1、功能分析
部落格網站首先應該有登入註冊功能,然後是最核心的功能——資訊發表,這個功能涉及到許多方面,包括資料庫訪問,前端顯示等。
一個完整的部落格系統,應該有評論,收藏,轉發等功能,處於本人目前的能力水平還不能都實現,先做一個部落格網站的雛形吧。
2、路由規劃
根據功能設計,我們把路由按照以下方案規劃:
- /:首頁
- /u/[user]:使用者的主頁
- /post:發表資訊
- /reg:使用者註冊
- /login:使用者登入
- /logout:使用者登出
以上頁面還可以根據使用者狀態細分,發表資訊以及使用者登出頁面必須是已登入使用者才能操作的功能,而使用者註冊和使用者登入所面向的物件必須是未登入的使用者,首頁和使用者主頁則針對已登入和未登入的使用者顯示不同的內容。
在 index.js 中新增以下內容:
router.get('/', function(req, res) { res.render('index', { title: 'Express' }); }); router.get('/u/:user', function(req, res) {}); router.post('/post', function(req, res) {}); router.get('/reg', function(req, res) {}); router.post('/reg', function(req, res) {}); router.get('/login', function(req, res) {}); router.post('/login', function(req, res) {}); router.get('/logout', function(req, res) {}); 複製程式碼
其中 /post、/login 和 /reg 由於要接受表單資訊,因此使用 router.post 註冊路由,/login 和 /reg 還要顯示使用者註冊時要填寫的表單,所以要以 router.get 註冊。
3、使用 Bootstrap
下載 jquery.js,bootstrap.css 和 bootstrap.js,放到 public 下對應的目錄中。

在 public_file.ejs 中引用,可以使用 include 方法給模板新增公共檔案。

去Bootstrap官網 檢視並使用所需的模板或元件。
下圖是我從 Bootstrap 官網找的一個模板,並放到了 index.ejs 目錄下並進行了簡單地修改,並將頁面公共部分(頭尾部分)取出,用 include 方式來複用。


五、使用者註冊和登入
1、訪問資料庫
我們選用 MongoDB 作為網站的資料庫系統,它是一個開源的 NoSQL 資料庫,相比 MySQL 那樣的關係型資料庫,它更為輕巧、靈活,非常適合在資料規模很大、事務性不強的場合下使用。
連線資料庫
通過 npm 安裝 mongodb。
npm install mongodb --save 複製程式碼
補充:通過 --save 安裝,包名和版本號將會出現在 package.json 中。
接下來在專案主目錄中建立 settings.js 檔案,這個檔案用於儲存資料庫的連線資訊,我們將用到的資料庫命名為 NodeJSBlog,資料庫伺服器在本地,因此 settings.js 檔案的內容如下:
settings.js
module.exports = { cookieSecret: 'NodeJSBlogbyvoid', db: 'NodeJSBlog', host: 'localhost', }; 複製程式碼
其中,db 是資料庫的名稱,host 是資料庫的地址,cookieSecret 用於 Cookie 加密與資料庫無關,我們留作後用。
接下來新建 models 目錄,並在目錄中建立 db.js:
models/db.js
var settings = require('../settings'), Db = require('mongodb').Db, Connection = require('mongodb').Connection, Server = require('mongodb').Server; module.exports = new Db(settings.db, new Server(settings.host, 27017, {}), { safe: true }); 複製程式碼
以上程式碼通過 module.exports 輸出了建立的資料庫連線,在後面的小節中我們會用到這個模組,由於模組只會被載入一次,以後我們在其他檔案中使用時均為這一個例項。
2、會話支援
《Node.js開發指南》中對會話支援是這樣描述的:
會話是一種持久的網路協議,用於完成伺服器和客戶端之間的一些互動行為。會話是一個比連線粒度更大的概念,一次會話可能包含多次連線,每次連線都被認為是會話的一次操作。在網路應用開發中,有必要實現會話以幫助使用者互動。例如網上購物的場景,使用者瀏覽了多個頁面,購買了一些物品,這些請求在多次連線中完成。許多應用層網路協議都是由會話支援的,如 FTP、Telnet 等,而 HTTP 協議是無狀態的,本身不支援會話,因此在沒有額外手段的幫助下,前面場景中伺服器不知道使用者購買了什麼。
為了在無狀態的 HTTP 協議之上實現會話,Cookie 誕生了。Cookie 是一些儲存在客戶端的資訊,每次連線的時候由瀏覽器向伺服器遞交,伺服器也向瀏覽器發起儲存 Cookie 的請求,依靠這樣的手段伺服器可以識別客戶端。我們通常意義上的 HTTP 會話功能就是這樣實現的。具體來說,瀏覽器首次向伺服器發起請求時,伺服器生成一個唯一識別符號併發送給客戶端瀏覽器,瀏覽器將這個唯一識別符號儲存在 Cookie 中,以後每次再發起請求,客戶端瀏覽器都會向伺服器傳送這個唯一識別符號,伺服器通過這個唯一識別符號來識別使用者。
對於開發者來說,我們無須關心瀏覽器端的儲存,需要關注的僅僅是如何通過這個唯一識別符號來識別使用者。很多服務端指令碼語言都有會話功能,如 PHP,把每個唯一識別符號儲存到檔案中。Express 也提供了會話中介軟體,預設情況下是把使用者資訊儲存在記憶體中,但我們既然已經有了 MongoDB,不妨把會話資訊儲存在資料庫中,便於持久維護。
但是如果你的 Express 版本是 4.x,按照書中接下來的內容編寫程式碼,就會出各種問題,下面我來說一下 Express 4.x 中的會話支援。
在 Express 4.x 中我們需要自己安裝 express-session 包,然後新增引用:
var session = require('express-session'); 複製程式碼
然後再引用 connect-mongo 包:
var MongoStore = require('connect-mongo')(session); 複製程式碼
最終在 app.js 中新增的內容就是:
var settings = require('./settings'); var session = require('express-session'); var MongoStore = require('connect-mongo')(session); app.use(session({ secret: settings.cookieSecret, store: new MongoStore({ db: settings.db, }) })); 複製程式碼
但是此時程式會報錯:'Connection strategy not found':

從網上查詢到該問題之後找到了解決辦法,開啟 package.json 檔案,將 connect-mongo 的版本改為 0.8.2,然後執行 npm install 即可解決。


如果程式沒有報錯,那麼就成功了,你可以使用 mongo.exe 或視覺化工具來檢視資料庫是否新建成功。


3、註冊和登入
3.1、註冊
通過視覺化工具或 mongodb 終端新建一個使用者表 users。
註冊頁面:
首先添加註冊頁面的模板 views/reg.ejs,並新增表單結構:
<div class="container"> <div class="bs-example" data-example-id="basic-forms"> <form> <div class="form-group"> <label for="username">使用者名稱</label> <input type="text" class="form-control" id="username" placeholder="請輸入使用者名稱"> </div> <div class="form-group"> <label for="password">密碼</label> <input type="password" class="form-control" id="password" placeholder="請輸入密碼"> </div> <div class="form-group"> <label for="password_repeat">再次輸入密碼</label> <input type="password" class="form-control" id="password_repeat" placeholder="再次輸入密碼"> </div> <button type="submit" class="btn btn-default">註冊</button> </form> </div> </div> 複製程式碼
然後開啟 index.js 添加註冊頁面的路由資訊:
index.js
router.get('/reg', function(req, res) { res.render('reg', { title: '使用者註冊' }); }); 複製程式碼
然後訪問 localhost:3000/reg。

註冊響應:
在書中使用了 flash,但是最新版本的 Express 已經不支援 flash 了,你需要先通過 npm 安裝 connect-flash。

然後在 app.js 中新增如下程式碼:
var flash = require('connect-flash'); 複製程式碼
在 routes/index.js 中新增 /reg 的 POST 響應函式:
routes/index.js
// 註冊響應 var crypto = require('crypto'); var User = require('../models/user.js'); var MongoClient = require('mongodb').MongoClient; const DB_CONN_STR='mongodb://localhost:27017/users'; router.post('/reg', function(req, res) { let newUser = { username: req.body.username, password: req.body.password, password_repeat: req.body.password_repeat }; let addStr = [{ username: newUser.username, password: newUser.password }]; MongoClient.connect(DB_CONN_STR, function(err, db) { db.collection('users').findOne({ username: newUser.username }, function(err, result) { if (!result) { if (newUser.password === newUser.password_repeat) { MongoClient.connect(DB_CONN_STR, function(err, db) { req.session.error = '註冊成功,請登入!'; db.collection('users').insert(addStr); db.close(); return res.redirect('/login'); }); } else { req.session.error = '兩次密碼不一致!'; return res.redirect('/register'); } } else { req.session.error = '使用者名稱已存在!'; return res.redirect('/register'); } }) db.close(); }); }); 複製程式碼
- req.body 就是 POST 請求資訊解析過後的物件,例如我們要訪問使用者傳遞的 password 域的值,只需訪問 req.body['password'] 即可。
- req.flash 是 Express 提供的一個奇妙的工具,通過它儲存的變數只會在使用者當前和下一次的請求中被訪問,之後會被清除,通過它我們可以很方便地實現頁面的通知和錯誤資訊顯示功能。
- res.redirect 是重定向功能,通過它會向用戶返回一個 303 See Other 狀態,通知瀏覽器轉向相應頁面。
- crypto 是 Node.js 的一個核心模組,功能是加密並生成各種雜湊,使用它之前首先要宣告 var crypto = require('crypto'),我們程式碼中使用它計算了密碼的雜湊值。
- User 是我們設計的使用者物件,在後面我們會詳細介紹,這裡先假設它的介面都是可用的,使用前需要通過 var User = require('../models/user.js') 引用。
- User.get 的功能是通過使用者名稱獲取已知使用者,在這裡我們判斷使用者名稱是否已經存在,User.save 可以將使用者物件的修改寫入資料庫。
- 通過 req.session.user = newUser 向會話物件寫入了當前使用者的資訊,在後面我們會通過它判斷使用者是否已經登入。
使用者模型
在前面的程式碼中,我們直接使用了 User 物件,User 是一個描述資料的物件,即 MVC 架構中的模型,前面我們使用了許多檢視和控制器,這是第一次接觸到模型。與檢視和控制器不同,模型是真正與資料打交道的工具,沒有模型,網站就只是一個外殼,不能發揮真實的作用,因此它是框架中最根本的部分。現在就讓我們來實現 User 模型吧。
在 models 目錄中建立 user.js 的檔案,內容如下:
models/user.js
var mongodb = require('./db'); function User(user) { this.name = user.name; this.password = user.password; }; module.exports = User; User.prototype.save = function save(callback) { // 存入 Mongodb 的文件 var user = { name: this.name, password: this.password, }; mongodb.open(function(err, db) { if (err) { return callback(err); } // 讀取 users 集合 db.collection('users', function(err, collection) { if (err) { mongodb.close(); return callback(err); } // 為 name 屬性新增索引 collection.ensureIndex('name', { unique: true }); // 寫入 user 文件 collection.insert(user, { safe: true }, function(err, user) { mongodb.close(); callback(err, user); }); }); }); }; User.get = function get(username, callback) { mongodb.open(function(err, db) { if (err) { return callback(err); } // 讀取 users 集合 db.collection('users', function(err, collection) { if (err) { mongodb.close(); return callback(err); } // 查詢 name 屬性為 username 的文件 collection.findOne({ name: username }, function(err, doc) { mongodb.close(); if (doc) { // 封裝文件為 User 物件 var user = new User(doc); callback(err, user); } else { callback(err, null); } }); }); }); }; 複製程式碼
以上程式碼實現了兩個介面,User.prototype.save 和 User.get,前者是物件例項的方法,用於將使用者物件的資料儲存到資料庫中,後者是物件建構函式的方法,用於從資料庫中查詢指定的使用者。
檢視互動
現在幾乎已經萬事俱備,只差檢視的支援了。為了實現不同登入狀態下頁面呈現不同內容的功能,我們需要建立動態檢視助手,通過它我們才能在檢視中訪問會話中的使用者資料,同時為了顯示錯誤和成功的資訊,也要在動態檢視助手中增加響應的函式。
在書中,在 app.js 中新增的檢視互動程式碼是:
app.dynamicHelpers({ user: function(req, res) { return req.session.user; }, error: function(req, res) { var err = req.flash('error'); if (err.length) return err; else return null; }, success: function(req, res) { var succ = req.flash('success'); if (succ.length) return succ; else return null; }, }); 複製程式碼
但是在 Express 4.x 中會報錯“app.dynamicHelpers is not a function ”,此處應該新增:
app.js
app.use(flash()); app.use(function(req, res, next) { res.locals.user = req.session.user; res.locals.post = req.session.post; var error = req.flash('error'); res.locals.error = error.length ? error : null; var success = req.flash('success'); res.locals.success = success.length ? success : null; next(); }); 複製程式碼
注意,這段程式碼不要放的太靠後,應該放到路由控制程式碼之前。

接下來修改公共導航部分。
header.ejs
<nav class="navbar navbar-inverse"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">NodeJS Blog</a> </div> <div id="navbar" class="navbar-collapse collapse"> <form class="navbar-form navbar-right"> <% if (!user) { %> <a href="/login" type="submit" class="btn btn-success">登入</a> <a href="/reg" type="submit" class="btn btn-success">註冊</a> <% } else { %> <a href="" type="submit" class="btn">登出</a> <% } %> </form> </div> </div> </nav> 複製程式碼
然後開啟註冊頁,輸入使用者名稱、密碼,點選註冊按鈕,發現又遇到坑了...“db.collection is not a function”。

引起這個錯誤的原因是你通過 npm 安裝的 mongodb 的版本和你 Node.js 操作資料的 api 版本不一致,查看了一下 package.json,發現 mongodb 的版本是 3.1.13。
解決方法,下載低版本 mongodb,例:
"mongodb": "^2.2.33", 複製程式碼
npm install 安裝。
未完待續......
