這個小應用使用到了node.js bootstrap express 以及數據庫的操做 :使用mongoose對象模型來操做 mongodbjavascript
若是沒了解過的能夠先去基本瞭解一下相關概念~php
首先註明一下版本,由於express由於版本的不一樣使用的方式也不一樣,我這算是目前最新的了吧css
尚未裝express的能夠移步到 這裏 看看express框架的獲取安裝html
1.簡單地項目初始化java
進入你的nodejs安裝路徑下邊,如圖,而後執行命令 express -e test (這裏把項目名設置爲test)node
出現如上圖所示,看到install dependencies沒有,它說若是你想安裝依賴就先進入項目test目錄,而後執行 npm install安裝依賴模塊。jquery
那就開始吧,網絡環境差的可能安裝會出錯..出現很長一大串通常就好了git
如此一來,項目初始已經完成,能夠運行一下項目 npm start 看是否正常。github
ok 還算正常,下面先來基本分析一下生成的初始項目:ajax
以前 那篇文章 已經說過
項目建立成功以後,生成四個文件夾,主文件app.js與配置信息文件packetage.json
bin是項目的啓動文件,配置以什麼方式啓動項目,默認 npm start
public是項目的靜態文件,放置js css img等文件
routes是項目的路由信息文件,控制地址路由
views是視圖文件,放置模板文件ejs或jade等(其實就至關於html形式文件啦~)
express這樣的MVC框架模式,是一個Web項目的基本構成。
先來看看文件信息package.json 通常項目的主要信息都會在這裏產生
{ "name": "test", "version": "0.0.0", "private": true, "scripts": { "start": "node ./bin/www" }, "dependencies": { "body-parser": "~1.12.0", "cookie-parser": "~1.3.4", "debug": "~2.1.1", "ejs": "~2.3.1", "express": "~4.12.2", "morgan": "~1.5.1", "serve-favicon": "~2.2.0" } }
看看主文件 app.js 這是它的初始形式,這個模塊還要繼續導出給 bin文件夾下的www文件使用
1 var express = require('express'); 2 var path = require('path'); 3 var favicon = require('serve-favicon'); 4 var logger = require('morgan'); 5 var cookieParser = require('cookie-parser'); 6 var bodyParser = require('body-parser'); 7 8 var routes = require('./routes/index'); 9 var users = require('./routes/users'); 10 11 var app = express(); 12 13 // view engine setup 14 app.set('views', path.join(__dirname, 'views')); 15 app.set('view engine', 'ejs'); 16 17 // uncomment after placing your favicon in /public 18 //app.use(favicon(__dirname + '/public/favicon.ico')); 19 app.use(logger('dev')); 20 app.use(bodyParser.json()); 21 app.use(bodyParser.urlencoded({ extended: false })); 22 app.use(cookieParser()); 23 app.use(express.static(path.join(__dirname, 'public'))); 24 25 app.use('/', routes); 26 app.use('/users', users); 27 28 // catch 404 and forward to error handler 29 app.use(function(req, res, next) { 30 var err = new Error('Not Found'); 31 err.status = 404; 32 next(err); 33 }); 34 35 // error handlers 36 37 // development error handler 38 // will print stacktrace 39 if (app.get('env') === 'development') { 40 app.use(function(err, req, res, next) { 41 res.status(err.status || 500); 42 res.render('error', { 43 message: err.message, 44 error: err 45 }); 46 }); 47 } 48 49 // production error handler 50 // no stacktraces leaked to user 51 app.use(function(err, req, res, next) { 52 res.status(err.status || 500); 53 res.render('error', { 54 message: err.message, 55 error: {} 56 }); 57 }); 58 59 60 module.exports = app;
www文件內容:這裏擁有着http服務器的基本配置
再來介紹一下項目使用到的ejs模板,好比看看這個view裏邊的index.ejs (咱們待會能夠直接把它轉爲html,差很少的)
<!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>
<%= title %> 這就是ejs的使用範例,title的值經過路由routes文件夾下index.js代碼傳入(後面再談)
好了,基本介紹了項目的初始狀況
2.基於初始項目的改進-- 註冊登陸功能
設計以下:
一個初始界面(其實就是原始地址:好比 localhost:3000(index.html 路徑爲/ ) ,在初始界面選擇登陸或註冊
跳進來以後會先跳進登陸界面(login.html 路徑爲 /login),能夠選擇先註冊(跳轉 register.html 路徑爲/register)
跳進註冊界面後就會跳進(register.html 路徑爲 /register),註冊成功後就跳轉登陸界面(login.html 路徑爲 /login)
在登陸界面登陸成功後就跳轉(home.html 路徑爲 /home). 在home這裏還提供了註銷的功能(無頁面文件,它的路徑爲 /logout
若是瀏覽器直接輸入localhost:3000/home 要先判斷是否登陸成功,未登陸不容許進入
看到上訴,應該瞭解到:咱們是經過一個路徑,而後經過這個路徑的解析,從而渲染出這個路徑對應的模板文件,其中咱們這裏的模板文件爲.html後綴的
首先展現一下基本界面形態:
而後先註冊吧,點擊註冊
填入用戶名密碼,這裏稍微設置了兩次密碼相同的判斷,註冊成功它會自動跳轉登陸界面
用mongoVUE看看數據的建立
那就登陸吧,登陸成功跳轉home界面
註銷吧,註銷後清除session值,而後跳轉到根路徑
而後試一下瀏覽器直接進入 home路徑? 瀏覽器地址輸入 localhost:3000/home 回車, ok 它自動跳轉到登陸界面
好如今開始解析如何構建這個小項目:
由於咱們直接使用了後綴名 .html ,因此咱們要先修改一下ejs模板 ,再把原來views目錄下模板文件後綴改爲 .html
var app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.engine("html",require("ejs").__express); // or app.engine("html",require("ejs").renderFile); //app.set("view engine","ejs"); app.set('view engine', 'html');
其實就是加一句再改一句。 __express 和renderFile均可以, 不用管它是什麼,它能那樣用就好了
而後咱們知道須要這些模板文件,那就建立它們吧
index.html 其中 <%= title %>使用到了模板 鏈接<a> 直接使用了路由路徑的方法
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> <style type="text/css"> a{margin-left: 20px; text-decoration: none;} a:hover{text-decoration: underline;} </style> </head> <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> <p><a href="/login">登陸 </a> <a href="/register"> 註冊</a> </p> </body> </html>
register.html 註冊方式主要是把原始 form表單 onsubmit="return false" 防止默認提交,而後在輸入信息正確的狀況下,經過ajax,把表單信息post到路徑/register
而後咱們就經過路由功能根據此路徑來處理信息(這個跟ajax和php交互是同一個道理)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title><%= title %></title> <link rel="stylesheet" href="stylesheets/bootstrap.min.css" media="screen"> <style type="text/css"> .m15{ margin: 15px;} .tc{ text-align: center;font-size: 18px;font-weight: 600;} </style> </head> <body screen_capture_injected="true"> <div class="container"> <%- message %> <form class="col-sm-offset-4 col-sm-4 form-horizontal" role="form" method="post" onsubmit="return false"> <fieldset> <legend></legend> <div class="panel panel-default"> <div class="panel-heading"> <p class="tc">註冊信息</p> </div> <div class="panel-body m15"> <div class="form-group"> <div class="input-group"> <span class="input-group-addon"> <span class="glyphicon glyphicon-user"></span> </span> <input type="text" class="form-control" id="username" name="username" placeholder="請輸入用戶名" required> </div> </div> <div class="form-group"> <div class="input-group"> <span class="input-group-addon"> <span class="glyphicon glyphicon-lock"></span> </span> <input type="text" class="form-control" id="password" name="password" placeholder="請輸入密碼" required> </div> </div> <div class="form-group"> <div class="input-group"> <span class="input-group-addon"> <span class="glyphicon glyphicon-lock"></span> </span> <input type="text" class="form-control" id="password1" name="password1" placeholder="請再次輸入密碼" required> </div> </div> <div class="form-group"> <button type="submit" class="btn btn-primary btn-block" id="register1">註冊</button> </div> <div class="form-group"> <button type="button" class="btn btn-info col-sm-2 col-sm-offset-10" id="login1">登陸</button> </div> </div> </div> </fieldset> </form> </div> <script type="text/javascript" src="javascripts/jquery.min.js"></script> <script type="text/javascript" src="javascripts/bootstrap.min.js"></script> <script type="text/javascript"> $(function(){ $("#login1").click(function(){ location.href = 'login'; }); $("#register1").click(function(){ var username = $("#username").val(); var password = $("#password").val(); var password1 = $("#password1").val(); if(password !== password1){ $("#password").css("border","1px solid red"); $("#password1").css("border","1px solid red"); }else if(password === password1){ var data = {"uname":username,"upwd":password}; $.ajax({ url: '/register', type: 'post', data: data, success: function(data,status){ if(status == 'success'){ location.href = 'login'; } }, error: function(data,err){ location.href = 'register'; } }); } }); }); </script> </body> </head> </html>
login.html 跟上面register.html原理差很少
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title><%= title %></title> <link rel="stylesheet" href="stylesheets/bootstrap.min.css" media="screen"> <style type="text/css"> .m15{ margin: 15px;} .tc{ text-align: center;font-size: 18px;font-weight: 600;} </style> </head> <body screen_capture_injected="true"> <div class="container"> <%- message %> <form class="col-sm-offset-4 col-sm-4 form-horizontal" role="form" method="post" onsubmit="return false"> <fieldset> <legend></legend> <div class="panel panel-default"> <div class="panel-heading"> <p class="tc">請先登陸</p> </div> <div class="panel-body m15"> <div class="form-group"> <div class="input-group"> <span class="input-group-addon"> <span class="glyphicon glyphicon-user"></span> </span> <input type="text" class="form-control" id="username" name="username" placeholder="請輸入用戶名" required> </div> </div> <div class="form-group"> <div class="input-group"> <span class="input-group-addon"> <span class="glyphicon glyphicon-lock"></span> </span> <input type="text" class="form-control" id="password" name="password" placeholder="請輸入密碼" required> </div> </div> <div class="form-group"> <button type="submit" class="btn btn-primary btn-block" id="login0">登陸</button> </div> <div class="form-group"> <button type="button" class="btn btn-info col-sm-2 col-sm-offset-10" id="register0">註冊</button> </div> </div> </div> </fieldset> </form> </div> <script type="text/javascript" src="javascripts/jquery.min.js"></script> <script type="text/javascript" src="javascripts/bootstrap.min.js"></script> <script type="text/javascript"> $(function(){ $("#register0").click(function(){ location.href = 'register'; }); $("#login0").click(function(){ var username = $("#username").val(); var password = $("#password").val(); var data = {"uname":username,"upwd":password}; $.ajax({ url:'/login', type:'post', data: data, success: function(data,status){ if(status == 'success'){ location.href = 'home'; } }, error: function(data,status){ if(status == 'error'){ location.href = 'login'; } } }); }); }); </script> </body> </head> </html>
最後是 home.html 裏頭的 user.name 就是使用ejs模板經過session.user來獲取user對象,這裏user有name和password的屬性
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> <style type="text/css"> a{margin-left: 20px; text-decoration: none;} a:hover{text-decoration: underline;} </style> </head> <body> <h1>Your name: <%- user.name %></h1> <p>Welcome to your home ~</p> <p><a href="/logout">我要註銷 </a> </p> </body> </html>
模板文件就是這些,接下來給主文件 app.js增長路由配置,讓瀏覽器訪問到路徑後得以被解析
app.use('/', routes); // 即爲爲路徑 / 設置路由 app.use('/users', users); // 即爲爲路徑 /users 設置路由 app.use('/login',routes); // 即爲爲路徑 /login 設置路由 app.use('/register',routes); // 即爲爲路徑 /register 設置路由 app.use('/home',routes); // 即爲爲路徑 /home 設置路由 app.use("/logout",routes); // 即爲爲路徑 /logout 設置路由
app.use是一箇中間件的用法,這裏的routes看初始項目的那句代碼,就是引用了routes文件夾下的index.js模塊
var routes = require('./routes/index'); var users = require('./routes/users');
因此待會咱們還得繼續修改完善index.js(我這裏是直接把全部路徑的處理方法所有放到index.js中,實際作的時候能夠考慮細分出模塊)
這裏先不說index.js,由於還有不少更寬泛的工做沒弄
1.註冊登陸,因此咱們得須要數據庫
這裏使用到了mongodb . 據我所知mongodb主要有兩種使用方法,這裏使用了其中的一種:使用 mongoose
Mongoose是MongoDB的一個對象模型工具,是基於node-mongodb-native開發的MongoDB nodejs驅動,能夠在異步的環境下執行。
同時它也是針對MongoDB操做的一個對象模型庫,封裝了MongoDB對文檔的的一些增刪改查等經常使用方法,讓NodeJS操做Mongodb數據庫變得更加靈活簡單。
咱們經過Mongoose去建立一個「集合」並對其進行增刪改查,就要用到它的三個屬性:Schema(數據屬性模型)、Model、Entity
這裏簡單介紹一下,更詳細的用法能夠自行查閱~
Schema —— 一種以文件形式存儲的數據庫模型骨架,沒法直接通往數據庫端,也就是說它不具有對數據庫的操做能力,僅僅只是數據庫模型在程序片斷中的一種表現,能夠說是數據屬性模型(傳統意義的表結構),又或着是「集合」的模型骨架。
好比定義一個Schema:
var mongoose = require("mongoose"); var TestSchema = new mongoose.Schema({ name : { type:String },//屬性name,類型爲String age : { type:Number, default:0 },//屬性age,類型爲Number,默認爲0 time : { type:Date, default:Date.now }, email: { type:String,default:''} });
Model —— 由Schema構造生成的模型,除了Schema定義的數據庫骨架之外,還具備數據庫操做的行爲,相似於管理數據庫屬性、行爲的類。
好比定義一個Model:
var db = mongoose.connect("mongodb://127.0.0.1:27017/test"); // 建立Model var TestModel = db.model("test1", TestSchema);
Entity —— 由Model建立的實體,使用save方法保存數據,Model和Entity都有能影響數據庫的操做,但Model比Entity更具操做性。
好比定義一個Entity:
var TestEntity = new TestModel({ name : "Lenka", age : 36, email: "lenka@qq.com" }); console.log(TestEntity.name); // Lenka console.log(TestEntity.age); // 36
基本就介紹到這裏
由於咱們要使用數據庫,那就來建立它。使用的就是上述的方法
首先,在項目根目錄下創建一個database文件夾,創建文件 models.js 而後創建model處理文件 dbHandel.js
寫入文件 models.js 一個user集合,裏面有name和password屬性
module.exports = { user:{ name:{type:String,required:true}, password:{type:String,required:true} } };
寫入文件 dbHandel.js 裏邊主要是獲取 Schema 而後處理獲取 model ,最後就是返回一個model了(提供其餘文件對model的操做 -- Entity是使用)
var mongoose = require('mongoose'); var Schema = mongoose.Schema; var models = require("./models"); for(var m in models){ mongoose.model(m,new Schema(models[m])); } module.exports = { getModel: function(type){ return _getModel(type); } }; var _getModel = function(type){ return mongoose.model(type); };
創建好基本文件後咱們就在app.js中調用使用它:要使用multer和mongoose模塊
項目沒有,因此咱們要安裝
app.js中加上
var multer = require('multer'); var mongoose = require('mongoose'); global.dbHandel = require('./database/dbHandel'); global.db = mongoose.connect("mongodb://localhost:27017/nodedb"); // 下邊這裏也加上 use(multer()) app.use(bodyParser.urlencoded({ extended: true })); app.use(multer()); app.use(cookieParser());
2.由於咱們使用到了session(好比進入home的時候判斷session值是否爲空),因此須要express-session 模塊
而後在app.js中引用它並做初始設置:
var session = require('express-session'); var app = express(); app.use(session({ secret: 'secret', cookie:{ maxAge: 1000*60*30; } })); app.use(function(req,res,next){ res.locals.user = req.session.user; // 從session 獲取 user對象 var err = req.session.error; //獲取錯誤信息 delete req.session.error; res.locals.message = ""; // 展現的信息 message if(err){ res.locals.message = '<div class="alert alert-danger" style="margin-bottom:20px;color:red;">'+err+'</div>'; } next(); //中間件傳遞 });
好如今想一想咱們還剩下什麼:
數據庫已經提供出model接口給咱們使用(給它填數據)
已經初始化了路徑處理
初始化了session信息 數據庫配置等
頁面模板也已經作完
因此剩下的就是路徑處理的部分:去routes目錄下 修改index.js吧
/ 路徑
/* GET index page. */ router.get('/', function(req, res,next) { res.render('index', { title: 'Express' }); // 到達此路徑則渲染index文件,並傳出title值供 index.html使用 });
/login 路徑
/* GET login page. */ router.route("/login").get(function(req,res){ // 到達此路徑則渲染login文件,並傳出title值供 login.html使用 res.render("login",{title:'User Login'}); }).post(function(req,res){ // 今後路徑檢測到post方式則進行post數據的處理操做 //get User info //這裏的User就是從model中獲取user對象,經過global.dbHandel全局方法(這個方法在app.js中已經實現) var User = global.dbHandel.getModel('user'); var uname = req.body.uname; //獲取post上來的 data數據中 uname的值 User.findOne({name:uname},function(err,doc){ //經過此model以用戶名的條件 查詢數據庫中的匹配信息 if(err){ //錯誤就返回給原post處(login.html) 狀態碼爲500的錯誤 res.send(500); console.log(err); }else if(!doc){ //查詢不到用戶名匹配信息,則用戶名不存在 req.session.error = '用戶名不存在'; res.send(404); // 狀態碼返回404 // res.redirect("/login"); }else{ if(req.body.upwd != doc.password){ //查詢到匹配用戶名的信息,但相應的password屬性不匹配 req.session.error = "密碼錯誤"; res.send(404); // res.redirect("/login"); }else{ //信息匹配成功,則將此對象(匹配到的user) 賦給session.user 並返回成功 req.session.user = doc; res.send(200); // res.redirect("/home"); } } }); });
/register 路徑
/* GET register page. */ router.route("/register").get(function(req,res){ // 到達此路徑則渲染register文件,並傳出title值供 register.html使用 res.render("register",{title:'User register'}); }).post(function(req,res){ //這裏的User就是從model中獲取user對象,經過global.dbHandel全局方法(這個方法在app.js中已經實現) var User = global.dbHandel.getModel('user'); var uname = req.body.uname; var upwd = req.body.upwd; User.findOne({name: uname},function(err,doc){ // 同理 /login 路徑的處理方式 if(err){ res.send(500); req.session.error = '網絡異常錯誤!'; console.log(err); }else if(doc){ req.session.error = '用戶名已存在!'; res.send(500); }else{ User.create({ // 建立一組user對象置入model name: uname, password: upwd },function(err,doc){ if (err) { res.send(500); console.log(err); } else { req.session.error = '用戶名建立成功!'; res.send(200); } }); } }); });
/home 路徑
/* GET home page. */ router.get("/home",function(req,res){ if(!req.session.user){ //到達/home路徑首先判斷是否已經登陸 req.session.error = "請先登陸" res.redirect("/login"); //未登陸則重定向到 /login 路徑 } res.render("home",{title:'Home'}); //已登陸則渲染home頁面 });
/logout 路徑
/* GET logout page. */ router.get("/logout",function(req,res){ // 到達 /logout 路徑則登出, session中user,error對象置空,並重定向到根路徑 req.session.user = null; req.session.error = null; res.redirect("/"); });
固然了,把因此路徑的處理放在同一個index.js事實上有點糟糕,能夠考慮分着寫:(這裏提供一種思路分出模塊)
好比一個home.js模塊裏邊:
module.exports = function ( app ) { app.get('/logout', function(req, res){ req.session.user = null; req.session.error = null; res.redirect('/'); }); }
從而只須要在index.js模塊裏邊引用便可
module.exports = function ( app ) { require('./logout')(app); };
在app.js模塊中再引用一下就能夠(routes目錄下index.js是默認文件,因此能夠省略index)
require('./routes')(app);
3.好了,一個簡單的註冊登陸功能已經完成了,啓動項目吧
(注意:由於要使用到mongodb數據庫,因此要先開啓數據庫服務,否則沒法訪問,由於咱們使用了nodedb 這個數據庫,因此最後也要先在mongodb中建立它,否則也有可能出錯 未安裝數據庫的能夠看看 這篇 ,檢測數據庫服務是否開啓:瀏覽器打開localhost:27017 就能訪問 ,而後給數據庫添加nodedb吧)
服務開啓
初始化nonedb能夠相似這樣
啓動項目,npm start
上面那個bson錯誤的不用管它..我也不知咋處理,據說能夠直接 npm install bson 或者 npm update 就行
但我試了貌似沒什麼效果
好了,項目已經打開,瀏覽器輸入 localhost:3000 訪問吧 (期間能夠本身查看mongodb數據庫裏邊nodedb --> user 數據的改動,使用mongoVUE或者命令查看)
須要代碼的可移步至Github: https://github.com/imwtr/nodejs_express_login_register