12_Node.js Web 開發_博客網站

下面開始用 Node.js 進行 Web 開發。css

我是經過《Node.js開發指南》這本書來學習 Node.js Web 開發的,書中使用的 Express 框架是 2.5.8,而個人是 4.14.1,因此遇到了許多問題,在文章中我都有提到並講解。html

GitHub 地址前端

1、快速開始

一、創建項目

《Node.js開發指南》中創建項目的方式是:express -t ejs microblog,可是這種方式對於高版本的 Express 新建的標籤替換引擎並非 .ejs,而是 .jade,若是要使用 .ejs 咱們能夠經過下面命令創建網站基本結構。node

express -e NodeJSBlog
複製代碼

執行命令後在當前目錄下出現了一些文件,而且下邊提示咱們經過 npm install 安裝依賴。jquery

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

二、啓動服務器

注意,咱們以前開啓 Node.js 服務器,都是執行 node xxx.js,而後去瀏覽器訪問便可,可是 Express 4.x 以上就不是這種方式了,應該是 npm start,端口配置在 bin/www 中。github

啓動成功訪問 localhost:3000/。正則表達式

三、項目結構

咱們看一下 express 在 NodeJSBlog 這個目錄下都生成了哪些文件。mongodb

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 兩個插件來實現應用程序自動重啓。

2、路由控制

一、建立頁面路由

簡單說一下新增一個頁面的流程。

首先在 views 目錄下新建一個模板,例如 hello.ejs:

而後打開 index.js 文件,添加頁面路由信息:

訪問 localhost:3000/hello。

補充:在《Node.js開發指南》這本書中,還須要向 app.js 文件中添加頁面路由信息,但在 Express 4.x 中是不須要的。

二、路徑匹配

上面的例子是爲固定的路徑設置路由規則,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] 這樣的形式訪問。

三、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 函數,它支持把全部的請求方式綁定到同一個響應函數,是一個很是靈活的函數,在後面咱們能夠看到許多功能均可以經過它來實現。

四、控制權轉移

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() 方法實現。

3、模板引擎

模板引擎也就是視圖,視圖決定了用戶最終能看到什麼,這裏咱們用 ejs 爲例介紹模板引擎的使用方法。

一、使用模板引擎

在 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 內容。

咱們能夠用它們實現頁面模板系統能實現的任何內容。

二、片斷視圖

《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,能夠看到如下內容:

4、開始創建博客網站

一、功能分析

博客網站首先應該有登陸註冊功能,而後是最核心的功能——信息發表,這個功能涉及到許多方面,包括數據庫訪問,前端顯示等。

一個完整的博客系統,應該有評論,收藏,轉發等功能,處於本人目前的能力水平還不能都實現,先作一個博客網站的雛形吧。

二、路由規劃

根據功能設計,咱們把路由按照如下方案規劃:

  • /:首頁
  • /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 註冊。

三、使用 Bootstrap

下載 jquery.js,bootstrap.css 和 bootstrap.js,放到 public 下對應的目錄中。

在 public_file.ejs 中引用,可使用 include 方法給模板添加公共文件。

Bootstrap官網 查看並使用所需的模板或組件。

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

5、用戶註冊和登陸

一、訪問數據庫

咱們選用 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 輸出了建立的數據庫鏈接,在後面的小節中咱們會用到這個模塊,因爲模塊只會被加載一次,之後咱們在其餘文件中使用時均爲這一個實例。

二、會話支持

《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.一、註冊

經過可視化工具或 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 安裝。

未完待續......


期待您的關注!

相關文章
相關標籤/搜索