相关文章
基于阿里egg框架搭建博客(1)——开发准备
基于阿里egg框架搭建博客(2)——Hello World
基于阿里egg框架搭建博客(3)——注册与登录
基于阿里egg框架搭建博客(4)——权限控制
基于阿里egg框架搭建博客(5)——置顶导航条
基于阿里egg框架搭建博客(6)——浏览、发表文章
基于阿里egg框架搭建博客(7)——编辑文章
git
https://github.com/ZzzSimon/e...
喜欢就点个赞吧!
正文
俗话说万事开头难,此章节涉及大量的知识点,在每个代码后面都有解释,需要大家查看官方文档。
user表设计
简单来说,注册与登录就是对user表的读写。所以我们首先对user表进行设计:
字段说明
字段 | 解释 |
---|---|
id | uuid |
username | 用户名 |
password | 密码 |
phone | 手机号 |
create_time | 创建时间 |
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;
注册
页面设计
页面设计如上图所示,需要用户输入用户名,密码,手机号,还可以上传自己的头像。
前端代码
官方文档推荐我们使用nunjucks
作为模板。
使用方法:https://eggjs.org/zh-cn/intro...
我们在app\view\home
目录下创建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">点击头像更改,只支持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">手机号</label> <input type="text" class="form-control" id="phone" placeholder="手机号" 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]); //监听文件读取结束后事件 reader.onloadend = function (e) { $('#avatarPic').attr("src",e.target.result); //e.target.result就是最后的路径地址 }; } });</script></body></html>
有3点需要注意:
- 静态文件的引用路径
/public
官方文档:https://eggjs.org/zh-cn/intro...
bootstrap自行搜索并下载,也可以用cdn。
- form表单请求url中的
?_csrf={{ ctx.csrf | safe }}
部分
官方文档:https://eggjs.org/zh-cn/core/...
- 最后一段js代码的作用:
由于bootstrap没有input type=file
标签样式,原版实在丑的不行,于是我把原版的隐藏了,在头像图片的点击事件监听里面,手动触发input type=file
文件上传按钮的click事件。
然后当用户选完头像后,通过window.FileReader
实时把图片显示出来。
后端代码
HomeController
我们在app\controller\
目录下创建home.js
文件:
该controller主要返回主页、登录页、注册页等基础页面。
const Controller = require('egg').Controller;class HomeController extends Controller { async register(){ await this.ctx.render('home/register.tpl') }}module.exports = HomeController;
UserController
我们在app\controller\
目录下创建user.js
文件:
//app/controller/user.jsconst 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]; //默认头像 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){ // 设置 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点需要注意:
- controller获取上传的文件
官方文档:https://eggjs.org/zh-cn/basic...
- cookie与session
官方文档:https://eggjs.org/zh-cn/core/...
- 重定向
官方文档:https://eggjs.org/zh-cn/basic...
- uuid工具类,helper的使用
官方文档:https://eggjs.org/zh-cn/basic...
附上我自己实现的uuid生成方法:
moment组件请自行npm安装,官方:http://momentjs.cn/
const moment = require('moment');//uuid格式:年月日时分秒3位毫秒+3位随机数,共20位 ===> 20190312162455043167exports.uuid = function uuid() { let uuid = moment().format("YYYYMMDDHHmmssSSS"); uuid += (Array(3).join(0) + Math.random()*100).slice(-3); return uuid;};
UserService
我们在app\service\
目录下创建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点需要注意:
- mysql的使用
官方文档:https://eggjs.org/zh-cn/tutor...
router.js
我们在app\
目录下的router.js
文件中添加以下内容:
module.exports = app => { const { router, controller } = app; //页面 router.get('/register.htm', controller.home.register); //接口 router.post('/user/register',controller.user.register);};
登录
页面设计
前端代码
我们在app\view\home
目录下创建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">记住登录状态 </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
文件中添加以下内容:
async login(){ await this.ctx.render('home/login.tpl'); }
UserController
我们在app\controller\user.js
文件中添加以下内容:
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 { // 设置 Session ctx.session.user = {username:user.username}; ctx.cookies.set('avatarUrl',user.avatar_url,{httpOnly:false}); // 如果用户勾选了 `记住我`,设置 的过期时间 if (rememberMe) ctx.session.maxAge = this.config.rememberMe; ctx.body = { successFlag:'Y', errorMsg:'登录成功!' }; ctx.redirect('/') } }
有1点需要注意:
- 用户勾选'记住我'后的session过期时间读取了配置文件中的值。配置:
官方文档:https://eggjs.org/zh-cn/basic...
UserService
我们在app\service\user.js
文件中添加以下内容:
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
文件中添加以下内容:
router.get('/login.htm',controller.home.login); router.post('/user/login',controller.user.login);
配置文件参考
config.default.js
参考官方文档后,你的配置文件应该如下(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 : { // 单数据库信息配置 client: { // host host: 'localhost', // 端口号 port: '3306', // 用户名 user: '数据库账号', // 密码 password: '数据库密码', // 数据库名 database: '你的数据库名', }, // 是否加载到 app 上,默认开启 app: true, // 是否加载到 agent 上,默认关闭 agent: false, }, };};
plugin.js
exports.nunjucks = { enable: true, package: 'egg-view-nunjucks'};exports.mysql = { enable: true, package: 'egg-mysql',};
结尾
如果看完觉得有用,请给作者一个喜欢吧!谢谢啦!