[一起学Node.js] Express MongoDB搭建多人博客-2

注册和登陆

我们已经准备好了数据库访问和会话存储的相关信息,接下来我们完成用户注册和登录功能。

页面设计

首先我们来完成主页、登录页和注册页的页面设计。 
修改 views/index.ejs 如下:

?
1
2
3
<%- include header %>
这是主页
<%- include footer %>

在 views 下新建 header.ejs,添加如下代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
<head>
<meta charset= "UTF-8" />
<title>Blog</title>
<link rel= "stylesheet" href= "stylesheets/style.css" >
</head>
<body>
 
<header>
<h1><%= title %></h1>
</header>
 
<nav>
<span><a title= "主页" href= "/" >home</a></span>
<span><a title= "登录" href= "/login" >login</a></span>
<span><a title= "注册" href= "/reg" >register</a></span>
</nav>
 
<article>

在 views 下新建 footer.ejs,添加如下代码:

?
1
2
3
</article>
</body>
</html>

修改 public/stylesheets/style.css 如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
/* inspired by http://yihui.name/cn/ */
*{padding: 0 ;margin: 0 ;}
body{width:600px;margin:2em auto;padding: 0 2em;font-size:14px;font-family: "Microsoft YaHei" ;}
p{line-height:24px;margin:1em 0 ;}
header{padding:.5em 0 ;border-bottom:1px solid #cccccc;}
nav{ float :left;font-family: "Microsoft YaHei" ;font-size: 1 .1em;text-transform:uppercase;margin-left:-12em;width:9em;text-align:right;}
nav a{display:block;text-decoration:none;padding:.7em 1em;color:# 000000 ;}
nav a:hover{background-color:#ff0000;color:#f9f9f9;-webkit-transition:color .2s linear;}
article{font-size:16px;padding-top:.5em;}
article a{color:#dd0000;text-decoration:none;}
article a:hover{color:# 333333 ;text-decoration:underline;}
.info{font-size:14px;}

此时运行 app.js ,主页显示如下:

接下来在 views 下新建 login.ejs,内容如下:

?
1
2
3
4
5
6
7
<%- include header %>
<form method= "post" >
   用户名:<input type= "text" name= "name" /><br />
   密码:  <input type= "password" name= "password" /><br />
          <input type= "submit" value= "登录" />
</form>
<%- include footer %>

登录页面显示如下:

在 views 下新建reg.ejs,内容如下:

?
1
2
3
4
5
6
7
8
9
<%- include header %>
<form method= "post" >
   用户名:  <input type= "text" name= "name" /><br />
   密码:    <input type= "password" name= "password" /><br />
   确认密码:<input type= "password" name= "password-repeat" /><br />
   邮箱:    <input type= "email" name= "email" /><br />
            <input type= "submit" value= "注册" />
</form>
<%- include footer %>

注册页面显示如下:

至此,未登录时的主页、注册页、登录页都已经完成。

页面通知

接下来我们将实现用户注册和登陆,在这之前我们需要引入 flash 模块来实现页面的通知和错误信息显示的功能。

什么是 flash?

req.flash 是 Express 提供的一个奇妙的工具,通过它保存的变量只会在用户当前和下一次的请求中被访问( req.flash 是存放在session里的),之后会被清除,通过它我们可以很方便地实现页面的通知和错误信息显示功能。

——《Node.js开发指南》

在 package.json 添加一行代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
   "name" : "blog" ,
   "version" : "0.0.1" ,
   "private" : true ,
   "scripts" : {
     "start" : "node app.js"
   },
   "dependencies" : {
     "express" : "3.2.4" ,
     "ejs" : "*" ,
     "mongodb" : "*" ,
     "connect-mongo" : "*" ,
     "connect-flash" : "*"
   }
}

然后 npm install 安装 flash 模块,修改 app.js 在 , settings = require('./settings') 后添加:

?
1
, flash = require( 'connect-flash' );

在 app.set('view engine', 'ejs'); 后添加:

?
1
app.use(flash());

现在我们就可以使用 flash 功能了。

注册响应

前面我们已经完成了注册页,当然现在点击注册是没有效果的,因为我们还没有实现处理 POST 请求的功能,下面就来实现它。

在 models 文件夹下新建 user.js,添加如下代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
var mongodb = require( './db' );
 
function User(user){
   this .name = user.name;
   this .password = user.password;
   this .email = user.email;
};
 
module.exports = User;
 
User.prototype.save = function(callback) { //存储用户信息
   //要存入数据库的用户文档
   var user = {
       name: this .name,
       password: this .password,
       email: this .email
   };
   //打开数据库
   mongodb.open(function(err, db){
     if (err){
       return callback(err);
     }
     //读取 users 集合
     db.collection( 'users' , function(err, collection){
       if (err){
         mongodb.close();
         return callback(err);
       }
       //将用户数据插入 users 集合
       collection.insert(user,{safe: true }, function(err, user){
         mongodb.close();
         callback(err, user); //成功!返回插入的用户信息
       });
     });
   });
};
 
User.get = function(name, 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 值为 name文档
       collection.findOne({
         name: name
       },function(err, doc){
         mongodb.close();
         if (doc){
           var user = new User(doc);
           callback(err, user); //成功!返回查询的用户信息
         } else {
           callback(err, null ); //失败!返回null
         }
       });
     });
   });
};

我们暂时先不去研究这些代码,我们只需知道通过 User.prototype.save 实现了用户信息的存储,通过 User.get 实现了用户信息的读取。

打开 routes/index.js ,在最前面添加如下代码:

?
1
2
var crypto = require( 'crypto' ),
     User = require( '../models/user.js' );

通过 require 引入 user.js 用户模型和 crypto 模块,crypto 是 Node.js 的一个核心模块,我们后面用它生成散列值来加密密码。

修改 app.post('/reg') 如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
app.post( '/reg' , function(req,res){
   var name = req.body.name,
       password = req.body.password,
       password_re = req.body[ 'password-repeat' ];
   //检验用户两次输入的密码是否一致
   if (password_re != password){
     req.flash( 'error' , '两次输入的密码不一致!' );
     return res.redirect( '/reg' );
   }
   //生成密码的散列值
   var md5 = crypto.createHash( 'md5' ),
       password = md5.update(req.body.password).digest( 'hex' );
   var newUser = new User({
       name: req.body.name,
       password: password,
       email: req.body.email
   });
   //检查用户名是否已经存在
   User.get(newUser.name, function(err, user){
     if (user){
       err = '用户已存在!' ;
     }
     if (err){
       req.flash( 'error' , err);
       return res.redirect( '/reg' );
     }
     //如果不存在则新增用户
     newUser.save(function(err){
       if (err){
         req.flash( 'error' ,err);
         return res.redirect( '/reg' );
       }
       req.session.user = newUser; //用户信息存入session
       req.flash( 'success' , '注册成功!' );
       res.redirect( '/' );
     });
   });
});

注意:我们把用户信息存储在了 session 里,以后就可以通过 req.session.user 读取用户信息。

  • req.body: 就是 POST 请求信息解析过后的对象,例如我们要访问用户传递的 name=“password” 域的值,只需访问 req.body['password'] 或 req.body.password 即可。
  • res.redirect: 重定向功能,实现了页面的跳转,更多关于 res.redirect 的信息请查阅:http://expressjs.com/api.html#res.redirect
  • User:在前面的代码中,我们直接使用了 User 对象。User 是一个描述数据的对象,即 MVC 
    架构中的模型。前面我们使用了许多视图和控制器,这是第一次接触到模型。与视图和控制器不同,模型是真正与数据打交道的工具,没有模型,网站就只是一个外壳,不能发挥真实的作用,因此它是框架中最根本的部分。

在桌面新建 “启动app.bat” 并写入 :

?
1
2
supervisor d:\blog\app
//node d:\blog\app

以后我们可以通过打开 “启动app.bat” 来启动我们的博客,而不必每次都要在命令行中启动。

现在先后运行你的 “启动mongodb.bat” 和 “启动app.bat” ,打开浏览器输入 localhost:3000/reg 注册试试吧!注册成功后显示如下:

我们查看数据库中是否存入了用户的信息,运行 d:\mongodb\bin\mongo ,输入:

用户信息已经成功存入数据库。但这还不是我们想要的效果,我们想要的效果是当注册成功返回主页时,左侧导航显示 HOME 、POST 、LOGOUT ,右侧显示 “注册成功!”字样。下面我们来实现它。

修改 index.js ,将 app.get('/') 修改如下:

?
1
2
3
4
5
6
7
8
app.get( '/' ,function(req,res){
   res.render( 'index' ,{
     title: '主页' ,
     user: req.session.user,
     success:req.flash( 'success' ).toString(),
     error:req.flash( 'error' ).toString()
   });
});

修改 header.ejs,将<nav></nav>修改如下:

?
1
2
3
4
5
6
7
8
9
10
<nav>
<span><a title= "主页" href= "/" >home</a></span>
<% if (locals.user){ %>
   <span><a title= "发表" href= "/post" >post</a></span>
   <span><a title= "登出" href= "/logout" >logout</a></span>
<% } else { %>
   <span><a title= "登录" href= "/login" >login</a></span>
   <span><a title= "注册" href= "/reg" >register</a></span>
<% } %>
</nav>

在 <article> 后添加如下代码:

?
1
2
3
4
5
6
<% if (locals.success) { %>
   <div ><%= locals.success %></div>
<% } %>
<% if (locals.error) { %>
   <div><%= locals.error %> </div>
<% } %>

现在注册成功后显示如下:

我们通过对 session 的使用实现了对用户状态的检测,再根据不同的用户状态显示不同的导航信息。 
简单解释一下流程:用户在注册成功后,把用户信息存入 session ,同时页面跳转到主页显示 “注册成功!” 。然后把 session 中的用户信息赋给全局变量 res.locals.user ,在渲染 ejs 文件时通过检测 locals.user 判断用户是否在线,根据用户状态的不同显示不同的导航信息。

登录与登出响应

现在我们来实现登陆的响应。 
打开 index.js 将 app.post('/login') 修改如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
app.post( '/login' , function(req, res){
   //生成密码的散列值
   var md5 = crypto.createHash( 'md5' ),
       password = md5.update(req.body.password).digest( 'hex' );
   //检查用户是否存在
   User.get(req.body.name, function(err, user){
     if (!user){
       req.flash( 'error' , '用户不存在!' );
       return res.redirect( '/login' );
     }
     //检查密码是否一致
     if (user.password != password){
       req.flash( 'error' , '密码错误!' );
       return res.redirect( '/login' );
     }
     //用户名密码都匹配后,将用户信息存入 session
     req.session.user = user;
     req.flash( 'success' , '登陆成功!' );
     res.redirect( '/' );
   });
});

接下来我们实现登出响应。修改 app.get('/logout') 如下:

?
1
2
3
4
5
app.get( '/logout' , function(req, res){
   req.session.user = null ;
   req.flash( 'success' , '登出成功!' );
   res.redirect( '/' );
});

通过把 req.session.user 赋值 null 丢掉 session 中用户的信息,实现用户的退出。 
登录后页面显示如下:

登出后页面显示如下:

至此,我们实现了用户注册与登陆的功能,并且根据用户登录状态显示不同的导航。

页面权限控制

我们虽然已经完成了用户注册与登陆的功能,但并不能阻止比如已经登陆的用户访问 localhost:3000/reg 页面(读者可亲自尝试下)。为此,我们需要为页面设置访问权限。即注册和登陆页面应该阻止已登陆的用户访问,登出及后面我们将要实现的发表页只对已登录的用户开放。如何实现页面权限的控制呢?我们可以把用户登录状态的检查放到路由中间件中,在每个路径前增加路由中间件,即可实现页面权限控制。我们添加 checkNotLogin 和 checkLogin 函数。 
最终 index.js 中代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
var crypto = require( 'crypto' ),
     User = require( '../models/user.js' );
 
module.exports = function(app){
   app.get( '/' ,function(req,res){
     res.render( 'index' ,{
     title: '主页' ,
     user: req.session.user,
     success:req.flash( 'success' ).toString(),
     error:req.flash( 'error' ).toString()
   });
});
 
   app.get( '/reg' , checkNotLogin);
   app.get( '/reg' ,function(req,res){
     res.render( 'reg' , {
       title: '注册' ,
       user: req.session.user,
       success: req.flash( 'success' ).toString(),
       error: req.flash( 'error' ).toString()
     });
   });
 
   app.post( '/reg' , checkNotLogin);
   app.post( '/reg' , function(req,res){
     var name = req.body.name,
         password = req.body.password,
         password_re = req.body[ 'password-repeat' ];
     //检验用户两次输入的密码是否一致
     if (password_re != password){
       req.flash( 'error' , '两次输入的密码不一致!' );
       return res.redirect( '/reg' );
     }
     //生成密码的散列值
     var md5 = crypto.createHash( 'md5' ),
         password = md5.update(req.body.password).digest( 'hex' );
     var newUser = new User({
         name: req.body.name,
         password: password,
         email: req.body.email
     });
     //检查用户名是否已经存在
     User.get(newUser.name, function(err, user){
       if (user){
         err = '用户已存在!' ;
       }
       if (err){
         req.flash( 'error' , err);
         return res.redirect( '/reg' );
       }
       //如果不存在则新增用户
       newUser.save(function(err){
         if (err){
           req.flash( 'error' ,err);
           return res.redirect( '/reg' );
         }
         req.session.user = newUser; //用户信息存入session
         req.flash( 'success' , '注册成功!' );
         res.redirect( '/' );
       });
     });
   });
 
   app.get( '/login' , checkNotLogin);
   app.get( '/login' , function(req, res){
     res.render( 'login' ,{
       title: '登录' ,
       user: req.session.user,
       success: req.flash( 'success' ).toString(),
       error: req.flash( 'error' ).toString()
     });
   });
 
   app.post( '/login' , checkNotLogin);
   app.post( '/login' , function(req, res){
     //生成密码的散列值
     var md5 = crypto.createHash( 'md5' ),
         password = md5.update(req.body.password).digest( 'hex' );
     //检查用户是否存在
     User.get(req.body.name, function(err, user){
       if (!user){
         req.flash( 'error' , '用户不存在!' );
         return res.redirect( '/login' );
       }
       //检查密码是否一致
       if (user.password != password){
         req.flash( 'error' , '密码错误!' );
         return res.redirect( '/login' );
       }
       //用户名密码都匹配后,将用户信息存入 session
       req.session.user = user;
       req.flash( 'success' , '登陆成功!' );
       res.redirect( '/' );
     });
   });
 
   app.get( '/post' , checkLogin);
   app.get( '/post' ,function(req,res){
     res.render( 'post' , {
       title: '发表' ,
       user: req.session.user,
       success: req.flash( 'success' ).toString(),
       error: req.flash( 'error' ).toString()
     });
   });
 
   app.post( '/post' , checkLogin);
   app.post( '/post' ,function(req,res){
   });
 
   app.get( '/logout' , checkLogin);
   app.get( '/logout' , function(req, res){
     req.session.user = null ;
     req.flash( 'success' , '登出成功!' );
     res.redirect( '/' );
   });
};
 
function checkLogin(req, res, next){
   if (!req.session.user){
     req.flash( 'error' , '未登录!' );
     return res.redirect( '/login' );
   }
   next();
}
 
function checkNotLogin(req,res,next){
   if (req.session.user){
     req.flash( 'error' , '已登录!' );
     return res.redirect( '/' );
   }
   next();
}

注意:为了维护用户状态和 flash 通知功能的使用,我们在 app.get('/reg)' 和 app.get('/login) 和 app.get('/post') 里添加了以下代码:

?
1
2
3
user: req.session.user,
success: req.flash( 'success' ).toString(),
error: req.flash( 'error' ).toString()

发表文章

现在我们的博客已经具备了用户注册、登陆、页面权限控制的功能,接下来我们完成博客最核心的部分——发表文章。在这一节,我们将会实现发表文章的功能,完成整个博客的设计。

页面设计

我们先来完成发表页的页面设计。在 views 文件夹下新建 post.ejs ,添加如下代码:

?
1
2
3
4
5
6
7
8
9
<%- include header %>
<form method= "post" >
   标题:<br />
   <input type= "text" name= "title" /><br />
   正文:<br />
   <textarea name= "post" rows= "20" cols= "100" ></textarea><br />
   <input type= "submit" value= "发表" />
</form>
<%- include footer %>

文章模型

仿照用户模型,我们将文章模型命名为 Post 对象,它拥有与 User 相似的接口,分别是 Post.get 和 Post.prototype.save。Post.get 的功能是从数据库中获取文章,可以按指定用户获取,也可以获取全部的内容。Post.prototype.save 是 Post 对象原型的方法,用来将文章保存到数据库。 
在 models 文件夹下新建 post.js ,添加如下代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
var mongodb = require( './db' );
 
function Post(name, title, post) {
   this .name = name;
   this .title= title;
   this .post = post;
}
 
module.exports = Post;
 
Post.prototype.save = function(callback) { //存储一篇文章及其相关信息
   var date = new Date();
   //存储各种时间格式,方便以后扩展
   var time = {
       date: date,
       year : date.getFullYear(),
       month : date.getFullYear() + "-" + (date.getMonth()+ 1 ),
       day : date.getFullYear() + "-" + (date.getMonth()+ 1 ) + "-" + date.getDate(),
       minute : date.getFullYear() + "-" + (date.getMonth()+ 1 ) + "-" + date.getDate() + " " + date.getHours() + ":" + date.getMinutes()
   }
   //要存入数据库的文档
   var post = {
       name: this .name,
       time: time,
       title: this .title,
       post: this .post
   };
   //打开数据库
   mongodb.open(function (err, db) {
     if (err) {
       return callback(err);
     }
     //读取 posts 集合
     db.collection( 'posts' , function (err, collection) {
       if (err) {
         mongodb.close();
         return callback(err);
       }
       //将文档插入 posts 集合
       collection.insert(post, {
         safe: true
       }, function (err,post) {
         mongodb.close();
         callback( null );
       });
     });
   });
};
 
Post.get = function(name, callback) { //读取文章及其相关信息
   //打开数据库
   mongodb.open(function (err, db) {
     if (err) {
       return callback(err);
     }
     //读取 posts 集合
     db.collection( 'posts' , function(err, collection) {
       if (err) {
         mongodb.close();
         return callback(err);
       }
       var query = {};
       if (name) {
         query.name = name;
       }
       //根据 query 对象查询文章
       collection.find(query).sort({
         time: - 1
       }).toArray(function (err, docs) {
         mongodb.close();
         if (err) {
           callback(err, null ); //失败!返回 null
         }
         callback( null , docs); //成功!以数组形式返回查询的结果
       });
     });
   });
};

发表响应

接下来我们给发表文章注册响应,打开 index.js ,在 User = require('../models/user.js') 后添加一行代码:

?
1
Post = require( '../models/post.js' );

修改 app.post('/post') 如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
app.post( '/post' , function(req, res){
   var currentUser = req.session.user,
       post = new Post(currentUser.name, req.body.title, req.body.post);
   post.save(function(err){
     if (err){
       req.flash( 'error' , err);
       return res.redirect( '/' );
     }
     req.flash( 'success' , '发布成功!' );
     res.redirect( '/' );
   });
});

最后,我们修改 index.ejs ,让主页右侧显示发表过的文章及其相关信息。 
打开 index.ejs ,修改如下:

?
1
2
3
4
5
6
7
8
9
10
<%- include header %>
<% posts.forEach(function(post, index){ %>
   <p><h2><a href= "#" ><%= post.title %></a></h2></p>
   <p class = "info" >
     作者:<a href= "#" ><%= post.name %></a> |
     日期:<%= post.time.minute %>
   </p>
   <p><%- post.post %></p>
<% }) %>
<%- include footer %>

打开 index.js ,修改 app.get('/') 如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.get( '/' , function(req,res){
   Post.get( null , function(err, posts){
     if (err){
       posts = [];
     }
     res.render( 'index' ,{
       title: '主页' ,
       user: req.session.user,
       posts: posts,
       success: req.flash( 'success' ).toString(),
       error: req.flash( 'error' ).toString()
     });
   });
});

至此,我们的博客就建成了。



转自:http://my.oschina.net/gejiawen0913/blog/136273#OSC_h2_1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值