相关文章
基于阿里egg框架搭建博客(1)——开发准备
基于阿里egg框架搭建博客(2)——Hello World
基于阿里egg框架搭建博客(3)——注册与登录
基于阿里egg框架搭建博客(4)——权限控制
基于阿里egg框架搭建博客(5)——置顶导航条
基于阿里egg框架搭建博客(6)——浏览、发表文章
基于阿里egg框架搭建博客(7)——编辑文章
git
https://github.com/ZzzSimon/egg-example
喜欢就点个赞吧!
正文
俗话说万事开头难,此章节涉及大量的知识点,在每个代码后面都有解释,需要大家查看官方文档。
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/quickstart.html#%E6%A8%A1%E6%9D%BF%E6%B8%B2%E6%9F%93
我们在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/quickstart.html#%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90
bootstrap自行搜索并下载,也可以用cdn。
- form表单请求url中的
?_csrf={{ ctx.csrf | safe }}
部分
- 最后一段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获取上传的文件
- cookie与session
- 重定向
官方文档:https://eggjs.org/zh-cn/basics/controller.html#%E9%87%8D%E5%AE%9A%E5%90%91
- uuid工具类,helper的使用
附上我自己实现的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的使用
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过期时间读取了配置文件中的值。配置:
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',
};
结尾
如果看完觉得有用,请给作者一个喜欢吧!谢谢啦!