项目实战-心情日记

【例16.1】制作网站—心情日记。(实例位置:资源包\源码\16\01)

本节将根据上面所学的相关内容,制作一个简单的网站—心情日记,该网站可以完成日记的编写、展示、修改和删除功能,另外还实现了登录、退出的功能,如图 16.31 所示。

image 2024 04 17 23 23 22 267
Figure 1. 图16.31 心情日记主页

由于篇幅有限,书中主要对网站功能的核心代码进行重点讲解,其他功能代码,可以查看资源包中的源代码。

Node.js中的mongojs模块

mongojs 是一个出色小巧的 Node.js 包,使用它可以很方便地在 Node.js 应用中访问 MongoDB 数据库。要使用它,首先需要使用下面命令进行安装:

npm install mongojs
bash

安装 mongojs 模块后,如果要使用它,需要用 require() 方法引入,代码如下:

var mongojs=require('mongojs')
javascript

mongojs 模块中提供了 connect(databaseUrl, collections) 方法来创建数据库连接对象。其中,databaseUrl 参数用来设置要连接的 MongoDB 数据库名称,collections 参数用来指定 MongoDB 数据库中的集合。

使用 connect() 方法创建的数据库连接对象可以调用 MongoDB 数据库的操作方法实现相应功能。比如,下面代码在 Node.js 程序中使用 mongojs 模块查询名称为 “mingrisoft” 的 MongoDB 数据库的 products 集合中的所有信息:

var mongojs=require('mongojs');
var db=mongojs.connect('mingrisoft', ['products']);
db.products.find()
javascript

初始化数据

心情日记项目的名称为 diary,其项目结构如图 16.32 所示。

image 2024 04 17 23 26 13 299
Figure 2. 图16.32 项目的文件结构

在运行本程序时,如果提示找不到模块,需要使用 npm install 命令安装 package.json 文件中的模块,另外,需要注意的是,安装的模块的版本要与 package.json 中要求的版本一致。

打开系统的 “命令提示符” 对话框,通过 mongo 命令进入 MongoDB 数据库,创建 blog 数据库,并创建 postuser 两个集合,同时向 user 集合中添加默认的账户,账户名和密码分别是 adminadmin。具体代码如下:

//创建数据库,添加管理员
use blog
db.createCollection('post')
db.createCollection('user')
db.user.save({ user: 'admin', pass: 'admin'})
javascript

MongoDB 中执行以上 4 条语句的效果如图 16.33 所示。

image 2024 04 17 23 28 05 827
Figure 3. 图16.33 初始化数据

主页的实现

心情日记网站的主要功能是在 app.js 文件中实现的,该文件中首先引入相应的模块,并指定使用 MongoDB 数据库,代码如下:

var express = require('express')
     , gzippo = require('gzippo')
     , routes = require('./routes')
     , crypto = require('crypto')
     , moment = require('moment')
     , cluster = require('cluster')
     ,path = require('path')
     , os = require('os');
var mongojs=require('mongojs');
var db=mongojs('blog', ['post', 'user']);
javascript

在路由配置部分,当用户输入监听地址进行访问时,会自动链接到 index.jade 文件。代码如下:

//配置路由
app.get('/', function(req, res) {
     var fields = { subject: 1, body: 1, tags: 1, created: 1, author: 1 };
     db.post.find({ state: 'published'}, fields).sort({ created: -1}, function(err, posts) {
          if (!err && posts) {
               res.render('index.jade', { title: '心情日记', postList: posts });
          }
     });
});
javascript

index.jade 文件中使用 jade 模块语法,主要显示导航栏和所有的日记信息。代码如下:

mixin blogPost(post)
  div.span6
    a(href="/post/#{post._id}")
      h3 #{post.subject}
    p #{post.body.substr(0, 250) + '...'}
    p#info
      div.tags
        for tag in post.tags
          strong
            a(href="#") #{tag}
      div.post-time
        em #{moment(post.created).format('YYYY-MM-DD HH:mm:ss')}
    p
      a(class="btn btn-small",href="/post/#{post._id}") 阅读更多 »


div.hero-unit
  h1 心情日记
  p 欢迎来到我的心情日记,这里有我最近的动态、想法和心情......

!=partial('alert', flash)

div
  - for (var i = 0; i < postList.length; i++)
    div.row
      mixin blogPost(postList[i])
      - if (i + 1 < postList.length)
        mixin blogPost(postList[++i])
bash

添加日记

app.js 文件中实现添加日记功能时,首先通过 get() 方法监听 url/post/add 的路径,当用户访问该路径时,将 add.jade 文件返回给客户端,并使用 insert() 方法将用户提交的信息添加到数据库中。关键代码如下:

app.get('/post/add', isUser, function(req, res) {
     res.render('add.jade', { title: '添加新的日记 '});
});
app.post('/post/add', isUser, function(req, res) {
     var values = {
          subject: req.body.subject
          , body: req.body.body
          , tags: req.body.tags.split(',')
          , state: 'published'
          , created: new Date()
          , modified: new Date()
          , comments: []
          , author: {
               username: req.session.user.user
          }
     };
     db.post.insert(values, function(err, post) {
          console.log(err, post);
          res.redirect('/');
     });
});
javascript

add.jade 文件是客户端添加日记页面。在 add.jade 文件中,使用 jade 语法将添加日记的表单信息显示出来,并且其访问请求方式为 post 方式,因此在单击 “添加” 按钮时,会将表单中输入的信息提交服务器。add.jade 文件中的代码如下:

form(class="form-horizontal",name="add-post",method="post",action="/post/add")
     fieldset
          legend 添加新的日记
          div.control-group
               label.control-label 标题:
               div.controls
                     input(type="text",name="subject",class="input-xlarge")
          div.control-group
               label.control-label 内容:
          div.controls
               textarea(name="body", rows="10", cols="30")
          div.control-group
               label.control-label 标签:
               div.controls
                     input(type="text",name="tags",class="input-xlarge")
          div.form-actions
               input(type="submit",value="添加",name="post",class="btn btn-primary")
javascript

添加日记页面效果如图 16.34 所示。

image 2024 04 17 23 33 49 459
Figure 4. 图16.34 添加日记

修改日记

app.js 文件中实现修改日记功能时,使用 app 对象的 get 方法监听 url/post/edit/:postid 的路径地址,将 edit.jade 返回给客户端,然后在 app 对象的 post 方法中使用 update 方法更新数据库中对应 id 的数据。关键代码如下:

app.get('/post/edit/:postid', isUser, function(req, res) {
     res.render('edit.jade', { title: '修改日记', blogPost: req.post } );
});
app.post('/post/edit/:postid', isUser, function(req, res) {
     db.post.update({ _id: db.ObjectId(req.body.id) }, {
          $set: {
               subject: req.body.subject
               , body: req.body.body
               , tags: req.body.tags.split(',')
               , modified: new Date()
          }
     },
     function(err, post) {
          if (!err) {
               req.flash('info', '日记修改成功!');
          }
          res.redirect('/');
     });
});
javascript

edit.jade 文件是客户端的修改日记信息页面,该文件中使用 jade 语法根据指定 id 将日记相关的信息显示在相应的表单中,而其访问请求方式为 post 方式,因此在单击 “修改” 按钮时,会将表单中的信息提交服务器。edit.jade 文件中的代码如下:

form(class="form-horizontal",name="edit-post",method="post",action="/post/edit/#{blogPost._id}")
     fieldset
          legend 编辑日记 ##{blogPost._id}
          div.control-group
               label.control-label 标题:
               div.controls
                     input(type="text",name="subject",class="input-xlarge span6",value="#{blogPost.subject}")
          div.control-group
               label.control-label 内容:
               div.controls
                     textarea(name="body",rows="10",cols="30",class="span6") #{blogPost.body}
          div.control-group
               label.control-label 标签:
               div.controls
                     input(type="text",name="tags",class="input-xlarge",value="#{blogPost.tags.join(',')}")
          input(type="hidden",name="id",value="#{blogPost._id}")
               div.form-actions
                    input(type="submit",value="修改",name="edit",class="btn btn-primary")
javascript

运行程序,首先在心情日记网站首页单击某一条日记的标题,进入其详细信息页面,然后单击右下角的 “修改” 超链接,如图16.35所示,即可跳转到修改日记页面,如图 16.36 所示,在该页面中对日记信息进行修改后,单击 “修改” 按钮即可。

image 2024 04 17 23 36 10 155
Figure 5. 图16.35 单击“修改”超链接
image 2024 04 17 23 36 33 474
Figure 6. 图16.36 修改日记

删除日记

app.js 文件中实现删除日记功能时,使用 MongoDB 中的 remove 方法将数据库中对应 id 的数据删除即可。关键代码如下:

app.get('/post/delete/:postid', isUser, function(req, res) {
     db.post.remove({ _id: db.ObjectId(req.params.postid) }, function(err, field) {
          if (!err) {
               req.flash('error', '日记删除成功');
          }
          res.redirect('/');
     });
});
javascript

运行程序,首先在心情日记网站首页单击某一条日记的标题,进入其详细信息页面,如图 16.37 所示,然后单击右下角的 “删除” 超链接,即可删除指定的日记。

image 2024 04 17 23 37 43 612
Figure 7. 图16.37 删除日记

用户登录与退出

app.js 文件中实现登录与退出功能时,会提交 url/login 的地址,然后在通过 post 方法监听到该地址的请求后,会接收用户提交的用户名和密码信息,使用 mongojs 模块的 findOne 方法判断是否能够找到相应的用户名和密码,如果找到,则记录登录用户,并跳转到首页,否则,停留在登录页面。关键代码如下:

//登录
app.get('/login', function(req, res) {
     res.render('login.jade', {
          title: 'Login user'
     });
});
app.get('/logout', isUser, function(req, res) {
     req.session.destroy();
     res.redirect('/');
});
app.post('/login', function(req, res) {
     var select = {
          user: req.body.username
          , pass: req.body.password
     };
     db.user.findOne(select, function(err, user) {
          if (!err && user) {
               //判断用户登录的session
               req.session.user = user;
               res.redirect('/');
          } else {
               //如果未登录的话,则停留在登录页面
               res.redirect('/login');
          }
     });
});
javascript

login.jade 文件是客户端的用户登录页面,该页面中使用 form 表单提交用户的登录信息,提交方式为 post,提交 action/login,这样就可以在 app.jspost 方法中接收到用户提交的登录信息,进而判断是否登录成功。login.jade 文件中的代码如下:

form(class="form-horizontal",name="login-form",method="post",action="/login")
     fieldset
          legend 请输入登录信息
          div.control-group
               label.control-label 账户:
               div.controls
                     input(type="text",name="username",class="input-xlarge")
          div.control-group
               label.control-label 密码:
               div.controls
                     input(type="password",name="password",class="input-xlarge")
          div.form-actions
               input(type="submit",value="登录",name="login",class="btn btn-primary")
html

登录页面效果如图 16.38 所示。

image 2024 04 17 23 39 41 671
Figure 8. 图16.38 登录页面