Node.js入門—用MongoDB改造LoginDemo

來源:互聯網
上載者:User

標籤:node-js   mongodb   mongoose   angular   express   

這次的樣本基於之前的LoginDemo(見使用cookie保持登入),我們引入MongoDB來儲存使用者資料。要運行這個樣本,前提是MongoDB資料要正常運行(見Node.js開發入門——MongoDB與Mongoose)。樣本啟動並執行結果呢,和之前的LoginDemo是一樣一樣的。因此,我們就只分析引入資料庫時項目本身的變化吧。

安裝mongoose

命令列環境下導航到LoginDemo目錄,執行下面的命令:

npm install mongoose --save

它會自動幫我們安裝依賴,也會把mongoose作為依賴項寫入項目的package.json檔案。

資料庫初始化指令碼dbInit.js

dbInit.js用來初始化資料庫,建立一個名為users的庫、一個名為accounts的集合、插入兩個賬戶。代碼如下:

var crypto = require(‘crypto‘);var mongoose = require(‘mongoose‘);mongoose.connect(‘mongodb://localhost/users‘);function hashPW(userName, pwd){  var hash = crypto.createHash(‘md5‘);  hash.update(userName + pwd);  return hash.digest(‘hex‘);}var db = mongoose.connection;db.on(‘error‘, console.error.bind(console, ‘connection error:‘));var count = 2;function checkClose(){  count = count - 1;  if(count==0) mongoose.disconnect();}db.once(‘open‘, function() {  console.log(‘mongoose opened!‘);  var userSchema = new mongoose.Schema({      name: {type: String, unique: true},       hash: String,      last: String    },     {collection: "accounts"}    );  var User = mongoose.model(‘accounts‘, userSchema);  var doc = new User({    name:"admin", hash: hashPW("admin","123456"), last:""  });  doc.save(function(err, doc){    if(err)console.log(err);    else console.log(doc.name + ‘ saved‘);    checkClose();  });   doc = new User({    name:"foruok", hash: hashPW("foruok","888888"), last:""  });  doc.save(function(err, doc){    if(err)console.log(err);    else console.log(doc.name + ‘ saved‘);    checkClose();  }); });

在啟動網站前,執行“node dbInit.js”來做初始化。

在dbInit.js裡,我使用了Mongoose的Document對象的save方法儲存建立的文檔,在“MongoDB與Mongoose”一文中已經用到了,不再囉嗦。

3. 重寫users.js

備份一下原來的users.js,新的users.js如下:

var express = require(‘express‘);var router = express.Router();var crypto = require(‘crypto‘);var mongoose = require(‘mongoose‘);mongoose.connect(‘mongodb://localhost/users‘);var db = mongoose.connection;db.on(‘error‘, console.error.bind(console, ‘connection error:‘));var User = null;db.once(‘open‘, function() {  console.log(‘mongoose opened!‘);  var userSchema = new mongoose.Schema({      name: {type: String, unique: true},       hash: String,      last: String    },     {collection: "accounts"}    );  User = mongoose.model(‘accounts‘, userSchema);});function hashPW(userName, pwd){  var hash = crypto.createHash(‘md5‘);  hash.update(userName + pwd);  return hash.digest(‘hex‘);}function getLastLoginTime(userName, callback){  if(!User){    callback("");    return;  }  var loginTime = Date().toString();  User.findOne({name:userName}, function(err, doc){    if(err) callback("");    else{      callback(doc.last);      //update login time      doc.update({$set:{last: loginTime}}, function(err, doc){        if(err) console.log("update login time error: " + err);        else console.log("update login time for " + doc.name);      });    }  });}function authenticate(userName, hash, callback){  if(!User){ callback(2); return;}  var query = User.findOne().where(‘name‘, userName);  query.exec(function(err, doc){    if(err || !doc){ console.log("get user error: " + err); callback(2); return}    if(doc.hash === hash) callback(0);    else callback(1);  });}router.requireAuthentication = function(req, res, next){  if(req.path == "/login"){    next();    return;  }  if(req.cookies["account"] != null){    var account = req.cookies["account"];    var user = account.account;    var hash = account.hash;    authenticate(user, hash, function(ret){       if(ret==0){        console.log(req.cookies.account.account + " had logined.");        next();      }else{        console.log("invalid user or pwd, redirect to /login");        res.redirect(‘/login?‘+Date.now());      }    });  }else{    console.log("not login, redirect to /login");    res.redirect(‘/login?‘+Date.now());  }};router.post(‘/login‘, function(req, res, next){  var userName = req.body.login_username;  var hash = hashPW(userName, req.body.login_password);  console.log("login_username - " + userName + " password - " + req.body.login_password + " hash - " + hash);  authenticate(userName, hash, function(ret){    switch(ret){      case 0: //success        getLastLoginTime(userName, function(lastTime){        console.log("login ok, last - " + lastTime);        res.cookie("account", {account: userName, hash: hash, last: lastTime}, {maxAge: 60000});        //res.cookie("logined", 1, {maxAge: 60000});        res.redirect(‘/profile?‘+Date.now());        console.log("after redirect");      });      break;    case 1: //password error      console.log("password error");      res.render(‘login‘, {msg:"密碼錯誤"});      break;    case 2: //user not found      console.log("user not found");      res.render(‘login‘, {msg:"使用者名稱不存在"});      break;    }  });});router.get(‘/login‘, function(req, res, next){  console.log("cookies:");  console.log(req.cookies);  if(req.cookies["account"] != null){    var account = req.cookies["account"];    var user = account.account;    var hash = account.hash;    authenticate(user, hash, function(ret){      if(ret == 0) res.redirect(‘/profile?‘+Date.now());      else res.render(‘login‘);    });  }else{    res.render(‘login‘);  }});router.get(‘/logout‘, function(req, res, next){  res.clearCookie("account");  res.redirect(‘/login?‘+Date.now());});router.get(‘/profile‘, function(req, res, next){  res.render(‘profile‘,{    msg:"您登入狀態:"+req.cookies["account"].account,     title:"登入成功",    lastTime:"上次登入:"+req.cookies["account"].last  });});module.exports = router;

代碼量和原來差不多,但邏輯複雜了一些。主要是mongoose查詢資料庫都是非同步,原來我們把帳號內建在記憶體裡,查詢時是同步的。從同步轉到非同步,代碼發生了翻天覆地的變化,如果你細看一下,會發現,哇哦,到處都是callback和callback的嵌套啊。幸好我是C出身,不然真被搞死了。

為了與Mongoose的非同步方式配合,users.js幾乎全部重寫了。我們以authenticate方法為例講一下。先看原來的authenticate:

function authenticate(userName, hash){  for(var i = 0; i < userdb.length; ++i){    var user = userdb[i];    if(userName === user.userName){      if(hash === user.hash){          return 0;      }else{          return 1;      }    }  }  return 2;}

這是典型的同步方式,簡單直接,遍曆數組比較。再看新的authenticate:

function authenticate(userName, hash, callback){  if(!User){ callback(2); return;}  var query = User.findOne().where(‘name‘, userName);  query.exec(function(err, doc){    if(err || !doc){ console.log("get user error: " + err); callback(2); return}    if(doc.hash === hash) callback(0);    else callback(1);  });}

這是非同步方式了。Node.js是事件驅動的,是一個主線程+多個工作者線程(線程池)的模型,耗時的操作都會投遞到線程池裡來執行,執行完畢再通過事件通知主線程,主線程處理事件,在適當的時候調用回調。Mongoose處理資料庫,也是這種邏輯。

在authenticate裡,我使用Model.findOne().where構造了一個Query對象,然後調用Query的exec方法來做查詢,而exec提交的查詢資料庫動作,實際上會線上程池裡完成。查詢結束後,事件通知主線程,回調我們提供的函數。

現在這種寫法,當authenticate()被調用時,很快就返回了,但是查詢卻被投遞到線程池去執行,調用方期望的結果並沒有立即到來,依賴調用結果展開的邏輯必須被延宕到回調發生。因此調用方必須改造代碼,將部分邏輯放到回呼函數裡,把回呼函數提供給新的authenticate方法。

可以參看requireAuthentication方法的代碼,對照下面的同步版本來體會其間異同。

router.requireAuthentication = function(req, res, next){  if(req.path == "/login"){    next();    return;  }  if(req.cookies["account"] != null){    var account = req.cookies["account"];    var user = account.account;    var hash = account.hash;    if(authenticate(user, hash)==0){      console.log(req.cookies.account.account + " had logined.");      next();      return;    }  }  console.log("not login, redirect to /login");  res.redirect(‘/login?‘+Date.now());};

關於Mongoose操作資料庫,CRUD之類的,給兩個連結參考下:

  • https://cnodejs.org/topic/51ff720b44e76d216afe34d9
  • http://www.cnblogs.com/aaronjs/p/4489354.html

其它文章:

  • Node.js開發入門——MongoDB與Mongoose
  • Node.js開發入門——使用cookie保持登入
  • Node.js開發入門——使用AngularJS內建服務
  • Node.js開發入門——Angular簡單樣本
  • Node.js開發入門——使用AngularJS
  • Node.js開發入門——使用jade模板引擎
  • Node.js開發入門——Express裡的路由和中介軟體
  • Node.js開發入門——Express安裝與使用
  • Node.js開發入門——HTTP檔案伺服器
  • Node.js開發入門——HelloWorld再分析
  • Node.js開發入門——環境搭建與HelloWorld

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

Node.js入門—用MongoDB改造LoginDemo

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.