nodejs用expressjs框架搭建多人博客(一)

本文涉及的产品
云数据库 MongoDB,通用型 2核4GB
简介: 想学习点新东西就是开手写,就写了个简单的实现,利用node实现一个博客。主要的内容就在首页也能看到了。

想学习点新东西就是开手写,就写了个简单的实现,利用node实现一个博客。主要的内容就在首页也能看到了。



话不多说,expressjs怎么创建项目选择ejs模板,之前的文章都写过了。

首先从用户注册开始,有了用户才能根据id查找文章。




<%- include("../layouts/header", {cssAry: ['/style/reg/index.css']}) %>
<div class="body flex center">
    <div class="index-main">
        <div class="index-header">
            <h1 class="logo"></h1>
            <p class="describe">一条大河波浪宽</p>
        </div>
        <div class="form-name flex center"> 
            <a class="form-active" href="/reg">注册</a>
            <a href="/login">登录</a>
        </div>
        <div class="index-form">
            <form method="post" action="/reg">
                <input type="hidden" name="_csrf" value="<%= csrf %>">
                <div class="form-item">
                     <input type="input" name="email" placeholder="邮箱"/>
                </div>
                <div class="form-item">
                    <input type="password" name="pwd" placeholder="密码(大于六位)">
                </div>
                <div class="form-item">
                    <input type="password" name="repeatpwd" placeholder="确定密码">
                </div>
                <div class="button-item">
                    <input class="btn btn-sure" type="submit" value="注册">
                </div>
            </form>
        </div>
    </div>
</div>
<%- include("../layouts/footer", {jsAry: []}) %>
include 传递参数时就把需要的css 和js 传递到head 和foot模块里

header.ejs

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="renderer" content="webkit">
    <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <link rel="shortcut icon" type="image/x-icon" href="/images/favicon3.ico">
    <meta name="csrf-token" content="<%= csrf %>">
    <title><%= title %></title>
    <link rel='stylesheet' href='/style/main.css' />
    <% for(var i = 0, item; item = cssAry[i++]; ) {%>
      <link rel='stylesheet' href='<%= item %>'/>
    <% } %>
  </head>
  <body>
footer.ejs

    <script type="text/javascript" src="http://lib.sinaapp.com/js/jquery/2.2.4/jquery-2.2.4.min.js"></script>   
    <script type="text/javascript" src="/javascripts/main.js"></script>    
    <% for(var i = 0, item; item = jsAry[i++]; ) {%>
      <script type="text/javascript" src="/<%= item %>"></script>
    <% } %>
  </body>
</html>
route路由,编写对应再到reg.ejs的页面

//注册
router.get('/reg', function(req, res) {
    res.render('reg/index', {
        title: '注册',
        pwd_err: req.session.pwd_err
    });
});

//注册提交
router.post('/reg', function(req, res) {
    if(!util.isEqual(req.body.pwd, req.body.repeatpwd)) {
        req.session.pwd_err = true;
        return res.redirect('/reg');
    }
    
    let pwd = util.mix(req.body.pwd);
    let User = {
        email: req.body.email,
        pwd: pwd
    }

    userDao.setUser(User, function(err){
        if (err) {
            return res.redirect('/reg');
        }
        req.session.email =  req.body.email;
        return res.redirect('/login');
    });
});

同时需要user和数据库的交互创建userDao,并且继承自baseDao


import { ObjectID } from 'mongodb';

import connect from '../../config/connect';
import BaseDao from './BaseDao';
import User from '../models/User';
import util  from '../lib/util';

//继承Dao
class UserDao extends BaseDao {

    //获取用户信息 登录等
    getUser (user, callback) {
        this.query(user, 'users', callback);
    }

    //用户注册 玩家 和管理员
    saveUser (user, col, callback) {
        let _that = this;
        let model = Object.assign(JSON.parse(User), user);
        this.query(model, col, function(err, u) {
            //用户已经存在
            if (u !== null) {
                err = 'notnull';
                return callback(err);
            }
            _that.save(model, col, callback);
        });
    };

    //普通用户注册
    setUser (user, callback) { 
        this.saveUser(user, 'users', callback);
    }

    updateUserOne(userUpdate, callback) {
        let ID = {};
        if (util.isString(userUpdate.id) ) {
            ID = {_id: new ObjectID(userUpdate.id)};
        } else {
            ID = userUpdate.id;
        }
        this.updateOne('users', ID,  {$set: userUpdate.field}, callback);
    }
    // 加入postId
    updatePostId(userPost, callback) {
        let _that = this;
        this.updatePromise(userPost.id, 'postId').then(function(result) {
            let postId = result.data || [];
            postId.push(userPost.postId);
            let userUpdate = {
                 id: result.id,
                 field: {
                     postId: postId
                 }
            };
            _that.updateUserOne(userUpdate, callback);
        }, function(err) {
            return err;
        });
    }

    updatePromise(id, key) {
        let _that = this;
        let ID = {_id: new ObjectID(id)};
        
        return new Promise(function(resolve, reject) {
            _that.query(ID, 'users', function(err, rtn) {
                if (err) {
                    reject(err);
                } else {
                    let result = {data: rtn[key], id: ID};
                    resolve(result);
                }
            });
        });
    }
}

module.exports = UserDao;
import connect from '../../config/connect';
import { ObjectID } from 'mongodb';

class BaseDao {
    //查询 field查询字段, col 集合或表
    query(field, col, callback) {
        connect.open(function(err, db) {
            if (err) {
                return callback(err);
            }
            //要查找的集合
            db.collection(col, function(err, collection) {
                //要查找的字段
                collection.findOne(field, function(err, result){
                    connect.close();
                    if (err) {
                        return callback(err);
                    }
                    //成功
                    callback(null, result); 
                });
            });
        });
    }
    //查找排序 query={field: field, orderby: orderby, }
    querySort(query, col, callback) {
        connect.open(function(err, db) {
            if (err) return callback(err);
            db.collection(col, function(err, collection) {
                if (query.limit) {
                    collection.find(query.field).sort(query.orderby).limit(query.limit).toArray(function(err, result) {
                        connect.close();
                        if (err) return callback(err);
                        callback(null, result);
                    });
                } else {
                    collection.find(query.field).sort(query.orderby).toArray(function(err, result) {
                        connect.close();
                        if (err) return callback(err);
                        callback(null, result);
                    });
                }
            });
        });
    }
    //查询首页全部
    queryAll(col, query, callback) {
        connect.open(function(err, db) {
            if( err ) return callback(err);
            db.collection(col, function(err, collection) {
                collection.find().sort(query.sort).limit(query.limit).toArray(function(err, result) {
                    connect.close();
                    if (err) return callback(err);
                    callback(null, result);
                });
            });
        });
    }
    //保存 新建
    save(field, col, callback) {
        connect.open(function(err, db) {
            if (err) {
                return callback(err);
            }
            db.collection(col, function(err, collection) {
                //if(field._id) delete field._id;
                collection.insert(field, {
                    safe: true
                }, function(err, result) {
                    connect.close();
                    if (err) {
                        return callback(err);
                    }
                    callback(null, result);
                });
            });
        });
    }
    //只修改一个 根据Id查找
    updateOne(col, id, updateField, callback) {
        connect.open(function(err, db) {
            if (err) {
                return callback(err);
            }
            db.collection(col, function(err, collection) {
                collection.updateOne(id, updateField, function(err, result) {
                    connect.close();
                    if (err) {
                        return callback(err);
                    }
                    //成功
                    callback(null, result); 
                });
            });
        });
    }
    //更新
    updates(col, field, updateFields, callback) {
        connect.open(function(err, db) {
            if (err) {
                return callback(err);
            }
            db.collection(col, function(err, collection) {
                collection.update(field, updateFields, function(err, result) {
                    connect.close();
                    if (err) {
                        return callback(err);
                    }
                    //成功
                    callback(null, result); 
                });
            });
        });
    }
    //删除一个表
    removeOne(col, field, callback) {
        connect.open(function(err, db) {
            if (err) {
                return callback(err);
            }
            db.collection(col, function(err, collection) {
                collection.removeOne(field, function(err, result) {
                    connect.close();
                    if (err) {
                        return callback(err);
                    }
                    //成功
                    callback(null, result); 
                });
            });
        });
    }
}

module.exports = BaseDao;

工具类

import crypto from 'crypto';

class util {
    static isNull(str) {
        if (str.trim() == null || str.trim() == '') {
            //为空
            return true;
        }
    }

    //密码加密混淆
    static mix(str) {
        let sha1 = crypto.createHash('sha1');
        return sha1.update(str).digest('hex');
    }

    //字符串是否相等
    static isEqual(str, str2) {
        if(str.trim() === str2.trim()) {
            return true;
        } 
    }

    //未登录 1 
    static notLogin = function(req, res) {
        if(!req.session.user){
            req.flash('isLogin', '1');
            res.redirect('/login');
            return true;
        }
        return false;
    }

    //已经登录 0 
    static login(req, res) {
        if(req.session.user){
            if(req.session.user['_id'] === req.params._id) {
                req.flash('isLogin', '0');
                return true;
            } else {
                return false;
            }
        }
        return false;
    }
    //json里出去空值
    static mergeJson(basedata, newdata) {
        let merge = {};

        for (let key1 in basedata) {
            merge[key1] = basedata[key1];
        }
        for (let key in newdata) {
            if ( newdata[key] !== '' && newdata !== null) {
                merge[key] = newdata[key];
            }
        }
        return merge;
    }
    //字符串
    static isString(str) {
        if(typeof str === 'string' && str.constructor === String )  return true;
    }
    //图片路径
    static getPath(str, aim) {
        var reg = new RegExp(aim + '\\/(\\S*)');
        return str.match(reg)[1];
    }
    //时间格式化
    static format(str, mat) {
        let d, date = new Date(str),
            year = date.getFullYear(),
            month = date.getMonth() + 1,
            day = date.getDate();
        switch(mat){
            case 'year':
                d = year
            break;
            case 'month':
                d = month;
            break 
            case 'day':
                d = day;
            default:
                d =  year+'-'+month+'-'+day;
        }
        return d;
    }
    
}

module.exports = util;


做到此发觉痛点是和数据库mongodb交互用mongodb = require('mongodb'),很别扭,应该用mongoose,不过既然都这么用了,就都应该学习下。

接下来进入登录界面



登录的路由

//进入登录页面
router.get('/login', function(req, res) {
    let error_name = req.flash('error_name');
    res.render('login/index', {
        title: '登录',
        error_name: req.flash('error_name'),
        error_pwd: req.flash('error_pwd'),
        isLogin: req.flash('isLogin'),
        email: req.session.email,
    });
});
//登录
router.post('/login', function(req, res) {
    if (util.isNull(req.body.email)) {
        req.flash('error_name', 'error_name');
        return res.redirect('/login');
    }
    if (util.isNull(req.body.pwd)) {
        req.flash('error_pwd', false);
        return res.redirect('/login');
    }

    //用户查找
    let pwd = util.mix(req.body.pwd);
    let User = {
        email: req.body.email,
        pwd: pwd
    }
    userDao.getUser(User, function(err, result) {
        if (err) {
            return res.redirect('/login');
        }
        if (result === null) {
            return res.redirect('/reg');
        }
        if (req.session.user && req.session.user['email'] === req.params.email) {
            delete req.session.user;
        }
        req.session.user = {
            _id: result._id,
            email: result.email,
        };
        return res.redirect('/personal/'+ result._id);
    });
});


<%- include("../layouts/header", {cssAry: ['/style/login/index.css']}) %>
<div class="body flex center">
    <div class="index-main">
        <div class="index-header">
            <h1 class="logo"></h1>
            <p class="describe">一条大河波浪宽</p>
        </div>
        <div class="form-name flex center"> 
            <a class="form-active" href="/login">登录</a>
            <a href="/reg">注册</a>
        </div>
        <div class="index-form">
            <form method="post" action="/login">
                <input type="hidden" name="_csrf" value="<%= csrf %>">
                <div class="form-item">
                     <input type="input" name="email" placeholder="邮箱"/>
                </div>
                <div class="form-item">
                    <input type="password" name="pwd" placeholder="请输入密码">
                </div>
                <div class="button-item">
                    <input class="btn btn-sure" type="submit" value="注册">
                </div>
            </form>
        </div>
    </div>
</div>
<%- include("../layouts/footer", {jsAry: []}) %>




登录成功后进入个人中心页面personal,根据id进入同时用到

import async from 'async';
当然不用的话可以用es6的promise(resolve,reject),  resolve就是成功后的参数传递,reject就是错误异常时。

//个人中心
router.get('/personal/:_id', function(req, res) {
    let ID = req.params._id;
    let boo =  util.login(req, res);
    
    async.waterfall([function(callback){
        userDao.getUser({_id: new ObjectID(ID)}, function(err, result) {
            if(err) return false;
            callback(null, result)
        });
    }, function(arg1, callback) {
        let options = {
            field: {id: ID}, 
            orderby: {time: -1}
        }
        postDao.queryPostSort(options, function(err, rtn) {
            let json = {
                title: '个人中心',
                id: ID,
                usermsg: arg1.usermsg,
                postId: arg1.postId,
                postSize: arg1['postId'].length,
                postList: rtn,
                login: boo
            }
            callback(null, json);
        });
    }], function(err, result) {
        res.render('personal/index', result);
    });
});

用户主要的字段信息有如下

let user = {
    "email" : "",
    "pwd" : "",
    "postId" :[],
    "usermsg" : {
        "username" : "",
        "userwork" : "",
        "userdegree" : "",
        "userarea" : "",
        "usersex" : "",
        "userintr" : "",
        "userhead": "",
    },
    "postclass":{
        0: "博文"
    }, //作者文章分类
}

当点击资料编辑时,进入资料编辑页面


//个人资料修改
router.get('/personal/edit/:_id', function(req, res) {
    let boo = util.notLogin(req, res);
    if(boo) return false;
    req.session.article = false;
    let ID = req.session.user['_id'];
    
    async.parallel([function(callback) {
        userDao.getUser({_id: new ObjectID(ID)}, function(err, result) {
            if(err) return false;
             callback(null, result.usermsg);
        });
    }, function(callback) {
        //头像上传
        mkdirs('/var/www/near/public/images/users/' + req.session.user['_id'] + '/avatar/', '0777');
        callback(null, 'ok');
    }], function(err, results){
        res.render('personal/edit', {
            title: '个人资料修改',
            id: ID,
            usermsg: results[0],
            login: true
        });
    });
});

router.post('/personal/edit/:_id', function (req, res) {
    let boo = util.notLogin(req, res);
    if(boo) return false;
    if(req.session.user['_id'] !== req.params._id) return res.redirect('login');

    let Upload =  upload.single("userhead"); //头像图片上传,未把头像图片单独做出来,选中后ajax提交
    Upload(req, res, function(err) {
        if (err) {
            return ;
        }
        let id = req.session.user['_id'];

        userDao.updatePromise(id, 'usermsg').then(function(result) {
            let data = util.mergeJson(result.data, req.body);
                data['userhead'] = req.session.postcover;
                req.session['postcover'] = null;
            let userUpdate = {
                id: result.id,
                field: {usermsg: data}
            };
            userDao.updateUserOne(userUpdate, function() {
                return res.redirect('/personal/'+ id);
            })
        }, function(err) {
            return res.redirect('/reg');
        });
    });
import upload from '../lib/multer.config';
import multer from 'multer';
import fs from 'fs' ;

var storage = multer.diskStorage({
    destination: function (req, file, cb) {
        let newDestination = '/var/www/near/public/images/users/' + req.session.user['_id'];
            req.session.article ?  (newDestination += '/posts/') : (newDestination += '/avatar/');
        cb(null, newDestination);
    },
    filename: function (req, file, cb) {
        let originalname = file.originalname,
            start =  originalname.lastIndexOf('.'),
            len = originalname.length,
            type = originalname.substring(start+1, len),
            filename = Date.now() + '.' + type;
            req.session.postcover = filename;
            cb(null, filename);
    }
});

let upload = multer(
    { 
        limits: {
            fieldNameSize: 100,
            fileSize: 60000000
        },
        storage: storage
    }
);

module.exports = upload;
ejs
<%- include("../layouts/header", {cssAry: ['/style/personal/edit.css']}) %>

<div class="body">
    <%- include("../layouts/main_head", {publishBtn: true}) %>
    <div class="main">  
        <form action="/personal/edit/<%= id %>?_csrf=<%= csrf %>" method="POST" id="userform" enctype="multipart/form-data">
            <div class="form-item useravatar" id="useravatar">
                <img src="/<%= usermsg.userhead ? ('images/users/' + id +'/avatar/' + usermsg.userhead) : 'images/default_avatar.jpg' %>">
                <em class="cover" id="cover"></em>
                <input class="input-file item-val" type="file" id="input-file" name="userhead">
            </div>
            <div class="form-item">
                <div class="flex center">
                    <span class="profile-name"><%= usermsg.username || "暂未填写" %></span>
                    <a class="modify-btn" data-type="center" href="javascript:;">修改</a>
                </div>
                <div class="modify-content flex-center-h">
                    <input class="modify-name item-val" type="text" placeholder="请填写昵称" name="username">
                    <a href="javascript:;" class="btn btn-cancel">取消</a>
                    <a href="javascript:;" class="btn btn-sure">确定</a>
                </div>
            </div>
            <div class="form-item flex">
                <div class="item-name">职业</div>
                <div>
                    <p class="modify-msg flex">
                        <span class="modify-file"><%= usermsg.userwork || "暂未填写" %></span>
                        <a class="modify-btn" data-type="com" href="javascript:;">修改</a>
                    </p>
                    <div class="modify-content flex-center-h">
                        <input class="modify-name item-val" type="text" placeholder="请输入职业" name="userwork">
                        <a href="javascript:;" class="btn btn-cancel">取消</a>
                        <a href="javascript:;" class="btn btn-sure">确定</a>
                    </div>
                </div>
            </div>
            <div class="form-item flex">
                <div class="item-name">学位</div>
                <div>
                    <p class="modify-msg flex">
                        <span class="modify-file"><%= usermsg.userdegree || "暂未填写" %></span>
                        <a class="modify-btn" data-type="com" href="javascript:;">修改</a>
                    </p>
                    <div class="modify-content flex-center-h">
                        <input class="modify-name item-val" type="text" placeholder="请输入学位" name="userdegree">
                        <a href="javascript:;" class="btn btn-cancel">取消</a>
                        <a href="javascript:;" class="btn btn-sure">确定</a>
                    </div>
                </div>
            </div>
            <div class="form-item flex">
                <div class="item-name">地区</div>
                <div>
                    <p class="modify-msg flex">
                        <span class="modify-file"><%= usermsg.userarea || "暂未填写" %></span>
                        <a class="modify-btn" data-type="com" href="javascript:;">修改</a>
                    </p>
                    <div class="modify-content flex-center-h">
                        <input class="modify-name item-val" type="text" placeholder="请填写地区" name="userarea">
                        <a href="javascript:;" class="btn btn-cancel">取消</a>
                        <a href="javascript:;" class="btn btn-sure">确定</a>
                    </div>
                </div>
            </div>
            <div class="form-item flex">
                <div class="item-name">性别</div>
                <div>
                    <p class="modify-msg flex">
                        <span class="modify-file"><% if(usermsg.usersex) { %>
                                <%= usermsg.usersex == 0 ? "女" : "男" %>
                            <% } else { %>
                                <%= "暂未填写" %>
                            <% } %></span>
                        <a class="modify-btn" data-type="radio" href="javascript:;">修改</a>
                    </p>
                    <div class="modify-content flex-center-h">
                        <label><input class="item-val" type="radio" name="usersex" value="1"> <em>男</em></label>
                        <label><input class="item-val" type="radio" name="usersex" value="0"> <em>女</em></label>
                        <a href="javascript:;" class="btn btn-cancel">取消</a>
                        <a href="javascript:;" class="btn btn-sure">确定</a>
                    </div>
                </div>
            </div>
            <div class="form-item flex">
                <div class="item-name">介绍自己</div>
                <div>
                    <p class="modify-msg flex">
                        <span class="modify-file"><%= usermsg.userintr || "暂未填写"  %></span>
                        <a class="modify-btn" data-type="com" href="javascript:;">修改</a>
                    </p>
                    <div class="modify-content modify-content-ta">
                        <textarea class="modify-name item-val" placeholder="请简单的介绍自己" name="userintr"></textarea>
                        <a href="javascript:;" class="btn btn-cancel">取消</a>
                        <a href="javascript:;" class="btn btn-sure">确定</a>
                    </div>
                </div>
            </div>
        </form>
    </div>
</div>

<%- include("../layouts/footer", {jsAry: ['javascripts/personal/edit.js']}) %>

目录结构




有需要的交流的可以加个好友


相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
3月前
|
存储 JavaScript 数据库
nodejs中express框架实现增删改查接口
nodejs中express框架实现增删改查接口
|
4月前
|
开发框架 JSON JavaScript
Node.js教程-express框架
Node.js教程-express框架
39 1
|
7月前
|
JSON JavaScript 中间件
node.js中Express框架路由,中间件
node.js中Express框架路由,中间件
|
7月前
|
域名解析 监控 JavaScript
宝塔面板pm2管理器部署node.js(express框架)sever文件,可以使用域名访问你的后端项目
宝塔面板pm2管理器部署node.js(express框架)sever文件,可以使用域名访问你的后端项目
451 0
|
14天前
|
开发框架 JavaScript 前端开发
【Node系列】Express 框架
Express.js 是一个基于 Node.js 平台的极简、灵活的 web 应用开发框架,提供一系列强大的特性来帮助你创建各种 web 和移动设备应用。
33 2
|
6月前
|
JavaScript 前端开发 架构师
Node框架 【Egg-企业级框架】
Node框架 【Egg-企业级框架】
160 0
|
2月前
|
开发框架 JavaScript 前端开发
比较两个突出的node.js框架:koa和express
接上文讲述了 koa框架,这边文章比较一下这两个突出的node.js框架:koa和express
|
3月前
|
JavaScript 前端开发 NoSQL
2022 年值得使用的 Node.js 框架
2022 年值得使用的 Node.js 框架
|
8月前
|
JavaScript Shell Linux
Node.js中的express框架,修改内容后自动更新(免重启),express热更新
Node.js中的express框架,修改内容后自动更新(免重启),express热更新
|
4月前
【Node学习】—Express框架的安装
【Node学习】—Express框架的安装