Egg常用的插件
egg-mysql
安装与配置
首先安装egg-mysql的包
$ npm i --save egg-mysql
然后在plugin.ts开启插件
mysql: {
enable: true,
package: 'egg-mysql',
},
再然后在config中配置数据库信息
单数据库
// mysql数据库
config.mysql = {
client: { // 单数据库信息配置
host: 'localhost', // host
port: '3306', // 端口号
user: 'liuhao', // 用户名
password: '123456', // 密码
database: 'ssmbuild', // 数据库名
},
app: true, // 是否加载到 app 上,默认开启
agent: false, // 是否加载到 agent 上,默认关闭
};
使用方法
await app.mysql.query(sql, values);
多数据库
// mysql数据库
config.mysql = {
clients: { // 多据库信息配置
db1: {
host: 'localhost', // host
port: '3306', // 端口号
user: 'liuhao', // 用户名
password: '123456', // 密码
database: 'ssmbuild', // 数据库名
},
db2: {
host: 'mysql.com', // host
port: '3306', // 端口号
user: 'root', // 用户名
password: '123456', // 密码
database: 'test', // 数据库名
}
},
app: true, // 是否加载到 app 上,默认开启
agent: false, // 是否加载到 agent 上,默认关闭
};
使用方法
const client1 = app.mysql.get('db1');
await client1.query(sql, values);
const client2 = app.mysql.get('db2');
await client2.query(sql, values);
注意
egg-mysql没有index.d.ts,所以编译不过,在typings/index.d.ts中添加配置
import 'egg';
declare module 'egg' {
interface mysql {
get(tableName: String, find: {}): Promise<Any>
insert(tableName: String, find: {}): Promise<Any>
update(tableName: String, find: {}): Promise<Any>
delete(tableName: String, find: {}): Promise<Any>
query(sql: String, values: Any[]): Promise<Any>
}
interface Application {
mysql: mysql;
}
}
详见寒风傲天博文
CRUD
Create
使用insert方法插入一条记录,插入成功判定 result.affectedRows === 1
// controller
public async insert() {
const { ctx } = this;
const book = {
bookName: 'js',
bookCounts: 2,
detail: 'test',
};
const result = await ctx.service.book.create(book);
if (result.affectedRows === 1) {
ctx.body = '插入成功';
} else {
ctx.body = '插入失败';
}
}
// service
public async create(book) {
const { app } = this;
const result = await app.mysql.insert('books', book);
return result;
}
结果如图
Read
使用 get
方法或 select
方法获取一条或多条记录。select
方法支持条件查询与结果的定制。
// controller
public async index() {
const { ctx } = this;
const result = await ctx.service.book.select();
await ctx.render('book.ejs', {
result,
});
}
// service
public async select(options?: object) {
const { app } = this;
const result = await app.mysql.select('books', options);
return result;
}
来看下效果
定制查询
Update&Delete
使用 update
方法更新数据库记录
const result = await app.mysql.update('posts', row, options);
// 参数: 表名,更新的数据,查询参数
const updateSuccess = result.affectedRows === 1;
// 插入成功判定
使用 delete
方法删除数据库记录
const result = await app.mysql.delete('posts', options);
直接执行sql查询
使用 query
执行sql 语句
const id = 1;
cosnt count = 4;
const results = await app.mysql.query('select * from books where id = ? and bookCounts > ?', [id, count]);
事务
代码
代码已上传码云#2部分
egg-sequelize
安装与配置
安装egg-sequelize和mysql2
$ npm install --save egg-sequelize mysql2
在plugin中开启插件
sequelize: {
enable: true,
package: 'egg-sequelize',
},
在config中配置sequelize
// 配置sequellize
config.sequelize = {
dialect: 'mysql', // 数据库类型
database: 'ssmbuild', // 数据库名称
host: '127.0.0.1', // 数据库ip地址
port: 3306, // 数据库端口
username: 'liuhao', // 数据库用户名
password: '123456', // 数据库密码
};
经测试这种方式可以进行操作,但是,不能自动生成表,需要手动建表
配置sequelize-cli
安装
$ npm install --save-dev sequelize-cli
在项目根目录下新建一个 .sequelizerc
配置文件
'use strict';
const path = require('path');
module.exports = {
config: path.join(__dirname, 'database/config.json'),
'migrations-path': path.join(__dirname, 'database/migrations'),
'seeders-path': path.join(__dirname, 'database/seeders'),
'models-path': path.join(__dirname, 'app/model'),
};
然后运行命令初始化配置文件和目录
$ npx sequelize init:config
$ npx sequelize init:migrations
根目录下会生成数据库相关文件夹
将config.js中的数据库配置改为自己的配置
然后运行命令生成表,以user为例
$ npx sequelize migration:generate --name=users
可以看到多了一个users.js文件,开始配置user模型
module.exports = {
// 在执行数据库升级时调用的函数,创建 users 表
up: async (queryInterface, Sequelize) => {
/**
* Add altering commands here.
*
* Example:
* await queryInterface.createTable('users', { id: Sequelize.INTEGER });
*/
const { INTEGER, STRING } = Sequelize;
await queryInterface.createTable('users', {
id: {
type: INTEGER, // INTEGER就是mysql中的int
primaryKey: true,
autoIncrement: true, // 自动增长
},
name: STRING(20),
url: STRING(100),
country: STRING(10),
}, {
// timestamps: false, // 去除createAt updateAt
createdAt: false, // 表示不启用created_at
updatedAt: false, // 表示不启用updated_at
freezeTableName: true, // 使用自定义表名
// 使用自定义表名之后上面写的users就直接就是你的表名,如果不加的话,你就可以写user,但是自己的表名为users,程序会自动将s加上
tableName: 'users', // 自定义的表名,也可以不写,直接用define后面的也可以
// 只要你使用了freezeTableName,程序就不会自动给你加上s了
});
},
// 在执行数据库降级时调用的函数,删除 users 表
down: async queryInterface => {
/**
* Add reverting commands here.
*
* Example:
* await queryInterface.dropTable('users');
*/
await queryInterface.dropTable('users');
},
};
然后变更数据库
# 升级数据库
$ npx sequelize db:migrate
# 如果有问题需要回滚,可以通过 `db:migrate:undo` 回退一个变更
# npx sequelize db:migrate:undo
# 可以通过 `db:migrate:undo:all` 回退到初始状态
# npx sequelize db:migrate:undo:all
可以看到数据库中已经有了users表
编码
OK,配置已经完成,现在开始进行愉快的编码
首先在app目录下新建一个model文件夹,将user模型添加到model下面,然后就可以通过app.model.User
或者 ctx.model.User
访问了,以insert为例
// controller
public async add() {
const { ctx } = this;
const result = await ctx.service.user.createUser({
name: 'zhangsan',
url: 'http://zhangsan.com',
country: 'CN',
});
ctx.body = result;
}
// service
public async createUser(user: object) {
const res = await this.ctx.model.User.create(user);
return res;
}
然后就可以看到已经插入数据成功
更多sequelize使用方法不多赘述,见之前的博文,传送门
代码
代码已上传码云#3部分
egg-passport
这里用微博的第三登录做为示范
安装及配置
首先运行命令安装passport支持
$ npm i --save egg-passport
$ npm i --save egg-passport-weibo
然后开启插件
passport: {
enable: true,
package: 'egg-passport',
},
passportWeibo: {
enable: true,
package: 'egg-passport-weibo',
},
然后在config.default中配置申请到的身份码
// weibo登录
config.passportWeibo = {
key: 'your key',
secret: 'your secret',
};
编码
首先在router中挂载路由,这里使用的是插件提供的语法糖(这里挂载的时候可能会报错,因为没有index.d.ts文件,这个问题解决办法上面说过,这里不再赘述)
export default (app: Application) => {
const { controller, router } = app;
router.get('/', controller.home.index);
app.passport.mount('weibo');
// 相当于
// const github = app.passport.authenticate('github', {});
// router.get('/passport/github', github);
// router.get('/passport/github/callback', github);
};
然后在controller中编写登录操作
public async index() {
// const { ctx } = this;
// // ctx.body = await ctx.service.test.sayHi('egg');
// await ctx.render('login.ejs');
const { ctx } = this;
if (ctx.isAuthenticated()) {
ctx.body = `<div>
<h2>${ctx.path}</h2>
<hr>
Logined user: <img src="${ctx.user.photo}"> ${ctx.user.displayName} / ${ctx.user.id} | <a href="/logout">Logout</a>
<pre><code>${JSON.stringify(ctx.user, null, 2)}</code></pre>
<hr>
<a href="/">Home</a> | <a href="/user">User</a>
</div>`;
} else {
ctx.session.returnTo = ctx.path;
ctx.body = `
<div>
<h2>${ctx.path}</h2>
<hr>
Login with
<a href="/passport/weibo">Weibo</a>
<hr>
<a href="/">Home</a> | <a href="/user">User</a>
</div>
`;
}
}
由于我的服务器已经过期,没有可用的网站来审核,所以申请到的oauth码不能使用,如果通过审核,这就可以使用了
![QQ录屏20200817150434 00_00_00-00_00_30](https://cdn.easyremember.cn/img/QQ录屏20200817150434 00_00_00-00_00_30.gif)
此外
API
ctx.user
- 获取当前已登录的用户信息ctx.isAuthenticated()
- 检查该请求是否已授权ctx.login(user, [options])
- 为用户启动一个登录的 sessionctx.logout()
- 退出,将用户信息从 session 中清除ctx.session.returnTo=
- 在跳转验证前设置,可以指定成功后的 redirect 地址
还提供了 API:
app.passport.verify(async (ctx, user) => {})
- 校验用户app.passport.serializeUser(async (ctx, user) => {})
- 序列化用户信息后存储进 sessionapp.passport.deserializeUser(async (ctx, user) => {})
- 反序列化后取出用户信息app.passport.authenticate(strategy, options)
- 生成指定的鉴权中间件
options.successRedirect
- 指定鉴权成功后的 redirect 地址options.loginURL
- 跳转登录地址,默认为/passport/${strategy}
options.callbackURL
- 授权后回调地址,默认为/passport/${strategy}/callback
app.passport.mount(strategy, options)
- 语法糖,方便开发者配置路由
代码
代码已上传码云#4部分
egg-jwt
jwt(jsonwebtoken)是我认为潜力最大的一种用户状态验证方式
JWT原理
三部分组成,用.分割
- header: 一个json对象,描述JWT的元数据
- payload: 一个 JSON 对象,用来存放实际需要传递的数据
- signature: 对header和payload两部分的签名,防止数据被篡改
安装与配置
运行命令安装
$ npm i egg-jwt --save
开启插件
jwt: {
enable: true,
package: 'egg-jwt',
}
在config.default中配置秘钥
// JWT秘钥
config.jwt = {
secret: 'xiaofeng',
};
编码
生成
登录页使用post方法提交表单不再列出,主要给出生成token的操作(同样jwt没有index.d.ts,需要手动配置,见前文)
public async add() {
const { ctx, app } = this;
ctx.cookies.set('username', ctx.request.body.username);
const token = app.jwt.sign({
username: ctx.request.body.username,
}, app.config.jwt.secret);
ctx.set({ authorization: token }); //设置headers
ctx.body = token;
}
效果如图
![QQ录屏20200817161322 00_00_00-00_00_30](https://cdn.easyremember.cn/img/QQ录屏20200817161322 00_00_00-00_00_30.gif)
验证
import { Context } from 'egg';
// 自定义的中间件:验证token
export default function jwtAuth(options: any): any {
return async (ctx: Context, next: () => Promise<any>) => {
const token = ctx.request.header.authorization;
let decode = '';
if (token) {
try {
// 解码token
decode = await ctx.app.jwt.verify(token, options.secret);
console.log('decode======>', decode);
await next();
} catch (error) {
ctx.status = 401;
ctx.body = {
message: error.message,
};
return;
}
} else {
ctx.status = 401;
ctx.body = {
message: '没有token',
};
return;
}
};
}
自定义验证token中间件,在路由文件中添加需要token才能访问的路由
import { Application } from 'egg';
export default (app: Application) => {
const { controller, router, middleware } = app;
const token = middleware.jwtAuth(app.config.jwt);
router.get('/news', token, controller.news.index);
};
没有获得token访问需要登录的路由时会提示没有token
使用接口测试工具绑定一个已经生成的token
可以看到成功解析了token
代码
代码已上传码云#5部分,验证部分在#8
egg-validate
egg-validate用于对参数进行检验,比如检验一个用户名是不是字符串ctx.validate({ userName: 'string' });
安装及配置
运行命令
$ npm i egg-validate --save
plugin中开启插件
validate: {
enable: true,
package: 'egg-validate',
},
config中配置
// validate, 参数校验器
config.validate = {
convert: true, // 对参数可以使用convertType规则进行类型转换
// validateRoot: false, // 限制被验证值必须是一个对象。
};
编码
编写一个post请求,接收一个对象作为参数
(validator需要手动挂载,使用什么方法添加什么规则)
import { Controller } from 'egg';
// 规则
const createRule = {
name: 'string',
age: {
type: 'number',
min: 0,
max: 100,
},
sex: [ 'man', 'woman' ],
};
export default class PostsController extends Controller {
public async index() {
const { ctx } = this;
ctx.body = 'posts index';
}
public async create() {
const { ctx, app } = this;
// ctx.validate(createRule, ctx.request.body); // 自动处理错误,422
const error = app.validator.validate(createRule, ctx.request.body); // 返回错误信息,开发者处理
console.log(error);
ctx.status = 201;
}
}
效果如图,当出现错误时返回错误,没有错误error为空,created是自动加的![QQ录屏20200821123806 00_00_00-00_00_30](https://cdn.easyremember.cn/img/QQ录屏20200821123806 00_00_00-00_00_30.gif)
更多规则
自定义规则
使用app.validator.addRule
添加更多自定义规则(需要手动挂载)
例如:
app.validator.addRule('newRule', (rule, value)=>{
if (value.length < 3 || value.length > 10) {
console.log("用户名的长度应该在3-10之间");
}
});
自定义规则需要单独在一个文件中添加
代码
代码已上传码云#6部分
egg-cors
请求跨域是我们在日常开发中经常碰到的问题
egg解决跨域也是非常的简单,只要开启egg-cors就行了
安装及配置
运行命令
$ npm i egg-cors --save
plugin中开启插件
cors: {
enable: true,
package: 'egg-cors',
},
config中配置
// 关闭csrf
config.security = {
csrf: {
enable: false, // 关闭csrf
},
domainWhiteList: [ '*' ], // 白名单
};
// CORS
config.cors = {
origin: '*',
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH',
};
然后就可以了
代码
代码已上传码云#7部分