使用nodejs、Express和MongDB搭建个人博客

nodejs是目前前端开发中会使用的服务端语言,Express是基于nodejs产生的Web开发框架,MongoDB是一个使用较广泛的非关系型数据库,以键值对的形式存储内容。因为对express和MongoDB都不算熟悉,所以为练习和基础入门,参考网上点击打开链接及自己摸索,实现个人博客搭建,分享如下。

整个过程主要分为以下几步:

  1. nodejs安装Express并生成空项目框架
  2. nodejs安装MongoDB
  3. 规划页面
  4. 分析路由及后端方法
  5. 建立数据库操作model

1.Nodejs安装Express及空项目框架

安装完nodejs之后,使用包管理工具安装express模块,命令为npm install -g express,安装完成后为查看express的版本,需要安装独立的express版本管理模块,使用命令 npm install -g express-generator,最后使用express -V查看express的版本确认安装成功。
因为上面的安装里带有参数-g表明是全局安装,这时候进入node_global文件夹,就可以使用express命令创建新的app,命令如下:
$ express -e test
在创建的过程中,可能会有没有安装的模块,可以根据提示使用npm进行安装。

创建成功之后,进入项目根目录,命令:

node ./bin/www
默认端口为3000,打开浏览器访问localhost:3000就可以访问Express的index页面。

下面看一下test项目下的文件目录:



app.js是整个项目的引导文件,里面主要加载该项目用到的所有模块,同时持久化session,设置路由文件和基本的出错处理,最后将整个文件export出来。

package.json是项目的配置信息和依赖模块的配置信息,所有信息以键值对的形式定义。当需要新的模块的时候,都需要修改依赖包文件。

routes为项目的路由文件,主要存放请求的处理。public为公共文件夹,存放前台相关的JS,CSS和图片等一系列资源。node_modules为项目的依赖模块,包含所有在package.json里声明过的模块。models为操作数据库的文件,在后面会进行详细说明。

2.安装mongodb及nodejs的mongodb操作模块

直接下载mongodb的安装包,就可以在windows下安装,安装完成后,进入安装目录,新建一个文件夹,使用这个命令
mongod --dbpath=D:\mongodb\test
因为mongodb的默认端口是27017,可以通过本地访问localhost:27017直接确定数据库是否打开。
想要操作数据库的话,就重新打开一个命令行,然后输入mongo,可以操作数据库,常见操作见链接: mongodb常见命令

Nodejs里的mongodb主要是提供操作mongodb的方法的模块有两个

使用npm安装之后,在安装依赖模块的时候,建议先使用npm安装,安装完成后查看安装模块的版本,再把版本号写入package.json里,这样避免npm找不到你想要的版本的模块。

可以在项目的根目录下创建setting.js写入数据库的配置信息,也是以键值对的形式:

module.exports = {
    db: 'test',
    host: 'localhost',
    port: 27017
};
提醒一下,mongodb是默认可以进行CURD操作的,不需要用户名和密码,如果你自己需要就创建用户,具体操作不多说。

为了保证用户登录等的会话保持和写入持久层方便操作,引入express-session 和 connect-mongo 这两个第三方中间件,方便序列化操作session等会话信息,并保存在数据库中。

创建配置信息文件之后,在app.js里写入保存会话的代码,如下:

// session的持久化防止丢失
app.use(session({
  secret: settings.cookieSecret,
  key: settings.db,//cookie name
  cookie: {maxAge: 1000 * 60 * 60 * 24 * 30},//30 days
  resave: false,
  saveUninitialized: true,
  store: new MongoStore({
    url:('mongodb://localhost:27017/test'),
})
}));
注意,这里的存储路径写法如下,写入的是可以直接访问的路径。

3.规划页面

博客页面的功能主要包括首页,注册,登录,登出,发表博客和获取某个用户的发表过的文章。因为所有方法都需要有页面支持用户操作,所有方法都有get方法的获取,同时对于发布内容的例如注册、发表博客及登录功能,需要有post方法支持。

根据页面的布局,我们采用ejs作为模板文件,主要规划有header,footer,index,login,logout,reg和post这些页面,以index页面为例,文件如下:

<%- include header %>
<% posts.forEach(function (post, index) { %>

<%= post.title %>

作者:<%= post.name %> | 日期:<%= post.time.minute %>

<%- post.post %>

<% }) %> <%- include footer %>

4.分析路由及后端方法

所有对请求的操作及路由,都需要放在路由文件夹下的index文件里,因为此处方法不多,可以直接写到一个js里。在app.js里,只需要引入router下的文件保证路由可用。

var routes = require('./routes/index');
app.use('/', routes);
在routes下的index.js里,需要写入所有的路由方法,具体如下:
var express = require('express');
var router = express.Router();
var crypto = require('crypto');
var User = require('../models/user.js');
var Post = require('../models/post.js')


/* GET home page. */
router.get('/', function(req, res, next) {
    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()
        });
    });
});
router.get('/login',checkNotLogin);
router.get('/login',function(req,res){
    res.render('login', {
        title: '登录',
        user: req.session.user,
        success: req.flash('success').toString(),
        error: req.flash('error').toString()});
});
router.post('/login',checkNotLogin);
router.post('/login', function (req, res) {
    //生成密码的 md5 值
    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('/');//登陆成功后跳转到主页
    });
});
router.post('/reg',checkNotLogin);
router.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');//返回注册页
    }
    //生成密码的 md5 值
    var md5 = crypto.createHash('md5'),
        password = md5.update(req.body.password).digest('hex');
    var newUser = new User({
        name: name,
        password: password,
        email: req.body.email
    });
    //检查用户名是否已经存在
    User.get(newUser.name, function (err, user) {
        if (err) {
            req.flash('error', err);
            return res.redirect('/');
        }
        if (user) {
            req.flash('error', '用户已存在!');
            return res.redirect('/reg');//返回注册页
        }
        //如果不存在则新增用户
        newUser.save(function (err, user) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/reg');//注册失败返回主册页
            }
            req.session.user = newUser;//用户信息存入 session
            req.flash('success', '注册成功!');
            res.redirect('/');//注册成功后返回主页
        });
    });
});
router.get('/reg',checkNotLogin);
router.get('/reg', function (req, res) {
    res.render('reg', {
        title: '注册',
        user: req.session.user,
        success: req.flash('success').toString(),
        error: req.flash('error').toString()
    });
});


router.get('/post',checkLogin);
router.get('/post',function(req,res){
    res.render('post',{
        title:'发表',
        user: req.session.user,
        success: req.flash('success').toString(),
        error: req.flash('error').toString()
    });
});

router.post('/post',checkLogin);
router.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('/');//发表成功跳转到主页
    });
});

router.get('/logout',checkLogin);
router.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', '未登录!');
        res.redirect('/login');
    }
    next();
}

function checkNotLogin(req, res, next) {
    if (req.session.user) {
        req.flash('error', '已登录!');
        res.redirect('back');//返回之前的页面
    }
    next();
}
module.exports = router;
在所有的方法中,都将session中的user输出至页面上,同时,在查询时将查询出的数据输出出去,在view文件夹下的对应模板文件里,可以直接使用
<%= name %>
的方法显示数据。同时,针对所有页面的权限,进行了权限检查,针对注册页面和登录界面,已经登录的用户不能查看,直接使用redirect重定向至首页,对于发布博客页面,未登录用户不能查看。

5. 数据库操作方法

在model文件夹下,是所有对数据库操作的文件。首先是db.js作为基础文件链接数据库,具体代码如下:
var settings = require('../setting.js'),
    Db = require('mongodb').Db,
    Connection = require('mongodb').Connection,
    Server = require('mongodb').Server;
module.exports = new Db(settings.db, new Server(settings.host, settings.port),
    {safe: true});
这里以用户为例子,主要有创建用户和用户登录两个方法,具体代码如下:
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);//错误,返回 err 信息
        }
        //读取 users 集合
        db.collection('users', function (err, collection) {
            if (err) {
                mongodb.close();
                return callback(err);//错误,返回 err 信息
            }
            //将用户数据插入 users 集合
            collection.insert(user, {
                safe: true
            }, function (err, user) {
                mongodb.close();
                if (err) {
                    return callback(err);//错误,返回 err 信息
                }
                callback(null, user[0]);//成功!err 为 null,并返回存储后的用户文档
            });
        });
    });
};

//读取用户信息
User.get = function(name, callback) {
    //打开数据库
    mongodb.open(function (err, db) {
        if (err) {
            return callback(err);//错误,返回 err 信息
        }
        //读取 users 集合
        db.collection('users', function (err, collection) {
            if (err) {
                mongodb.close();
                return callback(err);//错误,返回 err 信息
            }
            //查找用户名(name键)值为 name 一个文档
            collection.findOne({
                name: name
            }, function (err, user) {
                mongodb.close();
                if (err) {
                    return callback(err);//失败!返回 err 信息
                }
                callback(null, user);//成功!返回查询的用户信息
            });
        });
    });
};





你可能感兴趣的:(前端)