共计 8144 个字符,预计需要花费 21 分钟才能阅读完成。
相关文章
基于阿里 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.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];
// 默认头像
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 位 ===> 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\
目录下创建 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',
};
结尾
如果看完觉得有用,请给作者一个喜欢吧!谢谢啦!