node.js基於express框架搭建一個簡單的注冊登錄Web功能
這個小應用使用到了node.js bootstrap express 以及數據庫的操作 :使用mongoose對象模型來操作 mongodb
如果沒了解過的可以先去基本了解一下相關概念~
首先注明一下版本,因為express因為版本的不同使用的方式也不同,我這算是目前最新的了吧

還沒有裝express的可以移步到 這里 看看express框架的獲取安裝
1.簡單地項目初始化
進入你的nodejs安裝路徑下邊,如圖,然后執行命令 express -e test (這里把項目名設置為test)

出現如上圖所示,看到install dependencies沒有,它說如果你想安裝依賴就先進入項目test目錄,然后執行 npm install安裝依賴模塊。
那就開始吧,網絡環境差的可能安裝會出錯..出現很長一大串一般就行了

如此一來,項目初始已經完成,可以運行一下項目 npm start 看是否正常。


ok 還算正常,下面先來基本分析一下生成的初始項目:

之前 那篇文章 已經說過
項目創建成功之后,生成四個文件夾,主文件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服務器的基本配置
View Code
再來介紹一下項目使用到的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
------------------------------ 原創作品,轉載還請注明 -------------------------------

