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

想學習點新東西就是開手寫,就寫了個簡單的實現,利用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']}) %>

目錄結構




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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章