基于阿里egg框架搭建博客(3)——注冊與登錄

相關(guān)文章

基于阿里egg框架搭建博客(1)——開發(fā)準(zhǔn)備
基于阿里egg框架搭建博客(2)——Hello World
基于阿里egg框架搭建博客(3)——注冊與登錄
基于阿里egg框架搭建博客(4)——權(quán)限控制
基于阿里egg框架搭建博客(5)——置頂導(dǎo)航條
基于阿里egg框架搭建博客(6)——瀏覽悴势、發(fā)表文章
基于阿里egg框架搭建博客(7)——編輯文章

git

https://github.com/ZzzSimon/egg-example
喜歡就點(diǎn)個贊吧郑象!

正文

俗話說萬事開頭難歼跟,此章節(jié)涉及大量的知識點(diǎn)漩符,在每個代碼后面都有解釋,需要大家查看官方文檔木西。

user表設(shè)計

簡單來說乐埠,注冊與登錄就是對user表的讀寫。所以我們首先對user表進(jìn)行設(shè)計:


字段說明

字段 解釋
id uuid
username 用戶名
password 密碼
phone 手機(jī)號
create_time 創(chuàng)建時間
update_time 更新時間
avatar_url 頭像url

sql腳本

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` varchar(20) NOT NULL,
  `username` varchar(18) NOT NULL,
  `password` varchar(20) NOT NULL,
  `phone` varchar(11) DEFAULT NULL,
  `create_time` datetime NOT NULL,
  `update_time` datetime NOT NULL,
  `avatar_url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

注冊

頁面設(shè)計


頁面設(shè)計如上圖所示禽篱,需要用戶輸入用戶名畜伐,密碼,手機(jī)號躺率,還可以上傳自己的頭像玛界。

前端代碼

官方文檔推薦我們使用nunjucks作為模板。

使用方法:https://eggjs.org/zh-cn/intro/quickstart.html#%E6%A8%A1%E6%9D%BF%E6%B8%B2%E6%9F%93

我們在app\view\home目錄下創(chuàng)建register.tpl文件:

<html>
<head>
    <title>注冊</title>
    <link rel="stylesheet" href="/public/bootstrap/css/bootstrap.css">
    <script type="text/javascript" src="/public/js/jquery.min.js"></script>
    <script type="text/javascript" src="/public/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container ">
    <form class="center-block" style="width: 50%;margin-top: 10%"
          method="POST" action="/user/register?_csrf={{ ctx.csrf | safe }}" enctype="multipart/form-data">
        <div class="form-group ">
            <img src="/public/avatar/default.jpg" id="avatarPic" class="img-circle center-block" style="width: 64px;">
            <input type="file" id="avatarBtn" name="file" style="visibility: hidden">
            <p class="text-center help-block">點(diǎn)擊頭像更改悼吱,只支持jpg,png格式慎框,大小≤ 200 kb</p>
        </div>
        <div class="form-group">
            <label for="username">賬號</label>
            <input type="text" class="form-control" id="username" placeholder="用戶名" name="username">
        </div>
        <div class="form-group">
            <label for="password">密碼</label>
            <input type="password" class="form-control" id="password" placeholder="密碼" name="password">
        </div>
        <div class="form-group">
            <label for="phone">手機(jī)號</label>
            <input type="text" class="form-control" id="phone" placeholder="手機(jī)號" name="phone">
        </div>
        <div class="form-group">
            <button type="submit" class="btn btn-info pull-right">注冊</button>
        </div>
    </form>
</div>
<script>
    $('#avatarPic').bind('click', function () {
        $('#avatarBtn').click();
    });
    $('#avatarBtn').bind('change',function (e) {
        if (window.FileReader) {
            var reader = new FileReader();
            reader.readAsDataURL(e.target.files[0]);
            //監(jiān)聽文件讀取結(jié)束后事件
            reader.onloadend = function (e) {
                $('#avatarPic').attr("src",e.target.result);    //e.target.result就是最后的路徑地址
            };
        }
    });
</script>
</body>
</html>

有3點(diǎn)需要注意:

  1. 靜態(tài)文件的引用路徑/public

官方文檔:https://eggjs.org/zh-cn/intro/quickstart.html#%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90

bootstrap自行搜索并下載,也可以用cdn后添。

  1. form表單請求url中的?_csrf={{ ctx.csrf | safe }}部分

官方文檔:https://eggjs.org/zh-cn/core/security.html#%E5%AE%89%E5%85%A8%E5%A8%81%E8%83%81-csrf-%E7%9A%84%E9%98%B2%E8%8C%83

  1. 最后一段js代碼的作用:
    由于bootstrap沒有input type=file標(biāo)簽樣式笨枯,原版實(shí)在丑的不行,于是我把原版的隱藏了吕朵,在頭像圖片的點(diǎn)擊事件監(jiān)聽里面猎醇,手動觸發(fā)input type=file文件上傳按鈕的click事件。
    然后當(dāng)用戶選完頭像后努溃,通過window.FileReader實(shí)時把圖片顯示出來硫嘶。

后端代碼

HomeController

我們在app\controller\目錄下創(chuàng)建home.js文件:
該controller主要返回主頁、登錄頁梧税、注冊頁等基礎(chǔ)頁面沦疾。

const Controller = require('egg').Controller;

class HomeController extends Controller {
    async register(){
        await this.ctx.render('home/register.tpl')
    }
}
module.exports = HomeController;

UserController

我們在app\controller\目錄下創(chuàng)建user.js文件:

//app/controller/user.js
const Controller = require('egg').Controller;
const fs = require('mz/fs');

class UserController extends Controller{
    async register(){
        const ctx = this.ctx;
        const { username, password, phone } = ctx.request.body;
        const avatar = ctx.request.files[0];
        //默認(rèn)頭像
        let filepathNew = this.config.baseDir+'\\app\\public\\avatar\\default.jpg';
        //如果用戶上傳了頭像
        if (avatar) {
            console.log('file:%j', avatar);
            let filenameNew = ctx.helper.uuid() +'.'+  avatar.filename.split('.').pop();
            filepathNew = this.config.baseDir+'\\app\\public\\avatar\\'+filenameNew;
            //把臨時文件剪切到新目錄去
            await fs.rename(avatar.filepath, filepathNew);
        }

        const nowTime = new Date();
        const userNew = {
            id : ctx.helper.uuid(),
            username : username,
            password : password,
            phone : phone,
            avatar_url : filepathNew.split("\\app")[1],
            create_time : nowTime,
            update_time : nowTime
        };
        const flag = await ctx.service.user.save(userNew);
        if (flag){
            // 設(shè)置 Session
            ctx.session.user = {username:username};
            ctx.cookies.set('avatarUrl',userNew.avatar_url,{httpOnly:false});
            ctx.body = {
                successFlag:'Y',
                errorMsg:'登錄成功称近!'
            };
            ctx.redirect('/')
        }else {
            ctx.body = {
                successFlag:'N',
                errorMsg:'用戶名已存在!'
            }
        }
    }
}

module.exports = UserController;

有4點(diǎn)需要注意:

  1. controller獲取上傳的文件

官方文檔:https://eggjs.org/zh-cn/basics/controller.html#%E8%8E%B7%E5%8F%96%E4%B8%8A%E4%BC%A0%E7%9A%84%E6%96%87%E4%BB%B6

  1. cookie與session

官方文檔:https://eggjs.org/zh-cn/core/cookie-and-session.html

  1. 重定向

官方文檔:https://eggjs.org/zh-cn/basics/controller.html#%E9%87%8D%E5%AE%9A%E5%90%91

  1. uuid工具類哮塞,helper的使用

官方文檔:https://eggjs.org/zh-cn/basics/objects.html#helper

附上我自己實(shí)現(xiàn)的uuid生成方法:

moment組件請自行npm安裝刨秆,官方:http://momentjs.cn/

const moment = require('moment');

//uuid格式:年月日時分秒3位毫秒+3位隨機(jī)數(shù),共20位  ===>   20190312162455043167
exports.uuid = function uuid() {
    let uuid = moment().format("YYYYMMDDHHmmssSSS");
    uuid += (Array(3).join(0) + Math.random()*100).slice(-3);
    return uuid;
};

UserService

我們在app\service\目錄下創(chuàng)建user.js文件:

const Service = require('egg').Service;

class UserService extends Service {
    async save(user){
        const userQ =await this.app.mysql.get('user', {username : user.username});
        if (userQ){
            return false
        }else {
            const result =await this.app.mysql.insert('user', user);
            // 判斷插入成功
            const insertSuccess = result.affectedRows === 1;
            if (insertSuccess) {
                return true
            }
        }
        return false
    }
}
module.exports = UserService;

有1點(diǎn)需要注意:

  1. mysql的使用

官方文檔:https://eggjs.org/zh-cn/tutorials/mysql.html

router.js

我們在app\目錄下的router.js文件中添加以下內(nèi)容:

module.exports = app => {
    const { router, controller } = app;

    //頁面
    router.get('/register.htm', controller.home.register);

    //接口
    router.post('/user/register',controller.user.register);
};

登錄

頁面設(shè)計

前端代碼

我們在app\view\home目錄下創(chuàng)建login.tpl文件:

<html>
<head>
    <title>登錄</title>
    <link rel="stylesheet" href="/public/bootstrap/css/bootstrap.css">
    <script type="text/javascript" src="/public/js/jquery.min.js"></script>
    <script type="text/javascript" src="/public/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container ">
    <form class="center-block" style="width: 50%;margin-top: 20%"
          method="POST" action="/user/login?_csrf={{ ctx.csrf | safe }}" enctype="application/x-www-form-urlencoded">
        <div class="form-group">
            <label for="username">賬號</label>
            <input type="text" class="form-control" id="username" placeholder="用戶名" name="username">
        </div>
        <div class="form-group">
            <label for="password">密碼</label>
            <input type="password" class="form-control" id="password" placeholder="密碼" name="password">
        </div>
        <div class="checkbox">
            <label>
                <input type="checkbox" name="rememberMe">記住登錄狀態(tài)
            </label>
        </div>
        <div class="form-group">
            <button type="submit" class="btn btn-info pull-right">登錄</button>
            <div>
                <a href="/register.htm" class="btn btn-link pull-right">注冊</a>
            </div>
        </div>
    </form>
</div>
</body>
</html>

后端代碼

HomeController

我們在app\controller\home.js文件中添加以下內(nèi)容:

    async login(){
        await this.ctx.render('home/login.tpl');
    }

UserController

我們在app\controller\user.js文件中添加以下內(nèi)容:

    async login(){
        const ctx = this.ctx;
        ctx.logger.info('req body:: %j',ctx.request.body);
        const { username, password, rememberMe } = ctx.request.body;
        const user = await ctx.service.user.loginAndGetUser(username, password);
        if (!user){
            ctx.body = {
                successFlag:'N',
                errorMsg:'用戶名或密碼錯誤忆畅!'
            }
        }else {
            // 設(shè)置 Session
            ctx.session.user = {username:user.username};
            ctx.cookies.set('avatarUrl',user.avatar_url,{httpOnly:false});
            // 如果用戶勾選了 `記住我`衡未,設(shè)置 的過期時間
            if (rememberMe) ctx.session.maxAge = this.config.rememberMe;
            ctx.body = {
                successFlag:'Y',
                errorMsg:'登錄成功!'
            };
            ctx.redirect('/')
        }
    }

有1點(diǎn)需要注意:

  1. 用戶勾選'記住我'后的session過期時間讀取了配置文件中的值家凯。配置:

官方文檔:https://eggjs.org/zh-cn/basics/config.html

UserService

我們在app\service\user.js文件中添加以下內(nèi)容:

    async loginAndGetUser(username, password) {
        const user =await this.app.mysql.get('user', {username : username});
        if (!user || user.password !== password){
            return false
        } else {
            return user;
        }
    }

router.js

我們在app\router.js文件中添加以下內(nèi)容:

    router.get('/login.htm',controller.home.login);
    router.post('/user/login',controller.user.login);

配置文件參考

config.default.js

參考官方文檔后缓醋,你的配置文件應(yīng)該如下(object 對象寫法):

const path = require('path');
module.exports = appInfo => {
    return {
        keys: "123456",
        rememberMe : 24 * 60 * 60 * 1000, //選擇記住我之后,session有效時長
        security : {
            domainWhiteList:['.127.0.0.1'],  // 安全白名單绊诲,以 . 開頭
        },
        multipart : {
            mode: 'file',
            tmpdir: path.join(appInfo.baseDir, 'app/public/temp'),
        },
        session : {
            key: 'EGG_SESS',
            maxAge: 10 * 1000, // 單位毫秒
            httpOnly: true,
            encrypt: true,
        },
        view : {
            defaultViewEngine: 'nunjucks',
            mapping: {
                '.tpl': 'nunjucks',
            },
        },
        mysql : {
            // 單數(shù)據(jù)庫信息配置
            client: {
                // host
                host: 'localhost',
                // 端口號
                port: '3306',
                // 用戶名
                user: '數(shù)據(jù)庫賬號',
                // 密碼
                password: '數(shù)據(jù)庫密碼',
                // 數(shù)據(jù)庫名
                database: '你的數(shù)據(jù)庫名',
            },
            // 是否加載到 app 上送粱,默認(rèn)開啟
            app: true,
            // 是否加載到 agent 上,默認(rèn)關(guān)閉
            agent: false,
        },
    };
};

plugin.js

exports.nunjucks = {
    enable: true,
    package: 'egg-view-nunjucks'
};

exports.mysql = {
    enable: true,
    package: 'egg-mysql',
};

結(jié)尾

如果看完覺得有用掂之,請給作者一個喜歡吧抗俄!謝謝啦!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末世舰,一起剝皮案震驚了整個濱河市动雹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冯乘,老刑警劉巖洽胶,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晒夹,死亡現(xiàn)場離奇詭異裆馒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)丐怯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進(jìn)店門喷好,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人读跷,你說我怎么就攤上這事梗搅。” “怎么了效览?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵无切,是天一觀的道長。 經(jīng)常有香客問我丐枉,道長哆键,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任瘦锹,我火速辦了婚禮籍嘹,結(jié)果婚禮上闪盔,老公的妹妹穿的比我還像新娘。我一直安慰自己辱士,他們只是感情好泪掀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著颂碘,像睡著了一般异赫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上头岔,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天祝辣,我揣著相機(jī)與錄音,去河邊找鬼切油。 笑死蝙斜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的澎胡。 我是一名探鬼主播孕荠,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼攻谁!你這毒婦竟也來了稚伍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤戚宦,失蹤者是張志新(化名)和其女友劉穎个曙,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體受楼,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡垦搬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了艳汽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猴贰。...
    茶點(diǎn)故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖河狐,靈堂內(nèi)的尸體忽然破棺而出米绕,到底是詐尸還是另有隱情,我是刑警寧澤馋艺,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布栅干,位于F島的核電站,受9級特大地震影響捐祠,放射性物質(zhì)發(fā)生泄漏碱鳞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一雏赦、第九天 我趴在偏房一處隱蔽的房頂上張望劫笙。 院中可真熱鬧芙扎,春花似錦、人聲如沸填大。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽允华。三九已至圈浇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間靴寂,已是汗流浹背磷蜀。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留百炬,地道東北人褐隆。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像剖踊,于是被迫代替她去往敵國和親庶弃。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評論 2 354