前言
-
【音乐博客】上线啦!
-
为什么不继续使用Koa2?(以下情况是我个人开发koa遇到想解决的)
-
最近想把接口改为**
RESTful API**风格,而egg对RESTful API风格很友好的支持,当然koa也可以有写成这种风格,只是说人家egg集成好了,我们就没必要再造葫芦了 -
在接口返回的时候,我想返回HTTP响应码给前端,发现koa并不能获取到,需要手动设置响应状态**
response.status**,所以这也是我想转egg的第二个原因 -
koa只是一个基础的框架,开发过java的同学知道,**
MVC**架构对代码可观性及编写开发还是挺舒服的,而egg选择了koa作为其基础框架,进行了一些增强,也采用MVC架构进行开发 -
而egg的扩展、插件、代码风格相比koa,很明显增强了很多,是企业级应用的不二人选
-
而后期也慢慢将**【音乐博客】**的koa逐渐转向egg进行重构,若遇到坑,也会写文章总结,希望和各位爱好egg的同学共同学习!
四个方面
1. 安装egg
-
egg我们选择的是typescript版本安装开发的 -
npm init egg --type=ts -
搭建项目成功之后的目录如下:
-
-
可以看到app文件夹下的
MVC层次已经搭建成功 -
plugin.js用来定义插件;config.default.js用做基本配置,同时自己也可以新建文件来区分开发环境、测试环境以及线上环境 -
详情文件解释可移步到官方文档看详情
2. 数据库的选择与安装使用
2.1. egg-mysql
-
在 egg 项目中安装
egg-mysql -
npm i egg-mysql -
安装好之后,
npm i安装依赖,则可运行npm run dev -
在
{app_root}/config/plugin.js中启用 egg-mysql 插件: -
'use strict';
/** @type Egg.EggPlugin */ module.exports = { //mysql mysql:{ enable:true, package:'egg-mysql', } }; -
在配置文件中配置 mysql 数据库连接地址
{app_root}/config/config.default.js -
'use strict';
/** @mysql */ config.mysql = { //database configuration client:{ //host host:'localhost', //port port:'3306', //username user:'root', //password password:'123456', //database database:'qynbgl' }, //load into app,default is open //加载到应用程序,默认为打开 app:true, //load into agent,default is close //加载到代理中,默认值为“关闭” agent:false, };
到此我们的
egg-mysql就配置成功了,接下来对其进行使用
-
**egg-mysql 的使用 **
-
get 查找一条
-
let result = await this.app.mysql.get("user",{id:1}) -
get 查找多条数据
-
const page = 1, per = 10; //通过controller层传值过来service
const data = await (this.app as any).mysql.select(this.model, { // columns: ['id', 'name'], //查询字段,全部查询则不写,相当于查询* where: { // name: 'Jack' }, //查询条件 orders: [ ['id', 'asc'] //降序desc,升序asc ], limit: (page - 1) * per, //查询条数 offset: per //数据偏移量(分页查询使用) }) -
添加数据
-
let result = await this.app.mysql.insert("user",{username:"lisi",password:"1234"}) -
修改数据的第一种方式:根据主键修改(对象里面必须包含主键id)
-
let result = await this.app.mysql.update('user',{ id:2, username:'赵四' }); -
修改数据的第二种方式:通过 sql 来修改数据
-
let results=await this.app.mysql.query( 'update user set username = ? where id = ?',["王五",2] ); -
删除数据(根据id删除)
-
let result= await this.app.mysql.delete('user',{ id:3 }); -
执行sql
-
this.app.mysql.query(sql,values);
2.2. egg-sequelize
-
介绍
-
egg-mysql插件是底层操作数据库,所以性能是最好的
-
而在复杂的应用中,我们可能会需要一个 ORM 框架来帮助我们管理数据层的代码
-
sequelize 是一个广泛使用的 ORM 框架,它支持 MySQL、PostgreSQL、SQLite 和 MSSQL 等多个数据源。
-
安装
-
npm install --save egg-sequelize mysql2 -
在
config/plugin.js中引入 egg-sequelize 插件 -
exports.sequelize = { enable: true, package: 'egg-sequelize', }; -
在
config/config.default.js中编写 sequelize 配置 -
'use strict';
/** @sequelize */ config.sequelize = { dialect: 'mysql', host: '127.0.0.1', port: 3306, database: 'qynbgl', username: 'root', //数据库的用户名 password: '123456' };
这里注意,官网上
sequelize对象中有个坑没有写齐username、password配置
-
**初始化数据库和 Migrations **
-
我们可以通过mysql命令将表建好,但有个问题,如果结构发生改变,如何快速变更数据结构呢?
-
这时候我们就需要
Migrations来帮我们管理数据结构的变更了。 -
sequelize 提供了 sequelize-cli 工具来实现 Migrations
-
安装
sequelize-cli -
npm install --save-dev sequelize-cli -
在 egg 项目中,我们希望将所有数据库 Migrations 相关的内容都放在
database目录下,所以我们在项目根目录下新建一个.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'), }; -
初始化 Migrations 配置文件和目录
-
npx sequelize init:config npx sequelize init:migrations -
执行完后会生成
database/config.json文件和database/migrations目录,我们修改一下database/config.json中的内容,将其改成我们项目中使用的数据库配置: -
'use strict';
/** @config*/ { "development": { "username": "root", "password": "123456", "database": "qynbgl", "host": "127.0.0.1", "dialect": "mysql" }, "test": { ... }, "production": { "username": "root", "password": "123456", "database": "qynbgl", "host": "127.0.0.1", "dialect": "mysql" } } -
此时 sequelize-cli 和相关的配置也都初始化好了,我们可以开始编写项目的第一个 Migration 文件来创建我们的一个 users 表了。
-
npx sequelize migration:generate --name=init-users -
执行完后会在
database/migrations目录下生成一个 migration 文件(${timestamp}-init-users.js),我们修改它来处理初始化users表: -
'use strict';
module.exports = { // 在执行数据库升级时调用的函数,创建 users 表 up: async (queryInterface, Sequelize) => { const { INTEGER, DATE, STRING } = Sequelize; await queryInterface.createTable('users', { id: { type: INTEGER, primaryKey: true, autoIncrement: true }, name: STRING(30), age: INTEGER, created_at: DATE, updated_at: DATE, }); }, // 在执行数据库降级时调用的函数,删除 users 表 down: async queryInterface => { await queryInterface.dropTable('users'); }, }; -
执行 migrate 进行数据库变更
-
升级数据库 npx sequelize db:migrate 如果有问题需要回滚,可以通过
db:migrate:undo回退一个变更 # npx sequelize db:migrate:undo # 可以通过db:migrate:undo:all回退到初始状态 # npx sequelize db:migrate:undo:all
执行之后,我们的数据库初始化就完成了(打开Navicat查看表显示创建成功)。
-
编写代码
-
现在终于可以开始编写代码实现业务逻辑了,首先我们来在
app/model/目录下编写 user 这个 Model: -
'use strict';
module.exports = app => { const { STRING, INTEGER, DATE } = app.Sequelize; const User = app.model.define('user', { id: { type: INTEGER, primaryKey: true, autoIncrement: true }, name: STRING(30), age: INTEGER, created_at: DATE, updated_at: DATE, }); return User; }; -
这个 Model 就可以在 Controller 和 Service 中通过
app.model.User或者ctx.model.User访问到了,例如我们编写app/controller/users.js -
'use strict';
const Controller = require('egg').Controller; function toInt(str) { if (typeof str === 'number') return str; if (!str) return str; return parseInt(str, 10) || 0; } class UserController extends Controller { async index() { const ctx = this.ctx; const query = { limit: toInt(ctx.query.limit), offset: toInt(ctx.query.offset) }; ctx.body = await ctx.model.User.findAll(query); } async show() { const ctx = this.ctx; ctx.body = await ctx.model.User.findByPk(toInt(ctx.params.id)); } async create() { const ctx = this.ctx; const { name, age } = ctx.request.body; const user = await ctx.model.User.create({ name, age }); ctx.status = 201; ctx.body = user; } async update() { const ctx = this.ctx; const id = toInt(ctx.params.id); const user = await ctx.model.User.findByPk(id); if (!user) { ctx.status = 404; return; } const { name, age } = ctx.request.body; await user.update({ name, age }); ctx.body = user; } async destroy() { const ctx = this.ctx; const id = toInt(ctx.params.id); const user = await ctx.model.User.findByPk(id); if (!user) { ctx.status = 404; return; } await user.destroy(); ctx.status = 200; } } module.exports = UserController; -
最后我们将这个 controller 挂载到路由上:
// app/router.js
module.exports = app => { const { router, controller } = app; router.resources('users', '/users', controller.users); };
- 针对
users表的CURD操作的接口就开发完了,可通过Postman验证,通过GET请求http://127.0.0.1:8000/users可请求到数据
对数据库的操作尽量都在
service层上操作
对于mysql(简单、性能好)和sequelize(复杂)的选择,可根据项目进行选择使用哪种方式操作数据库
mysql常用的语句
(1)关联表查询(查询评价表,关联用户表头像和昵称)
select evaluate.*, user.name, user.figureurl from evaluate
left join user on evaluate.userId = user.id where cId = 123
(2)更新商品点赞+1,并且一次更新id为1,2,3三条记录
update commodity set praise = praise + 1 where id in (1,2,3)
3. RESTful API
-
'use strict';
/** @app/router.js*/ module.exports = app => { const { router, controller } = app; router.resources('posts', '/api/posts', controller.posts); router.resources('users', '/api/v1/users', controller.v1.users); // app/controller/v1/users.js }; -
上面代码就在 /posts 路径上部署了一组 CRUD 路径结构,对应的 Controller 为 `app/controller/posts.js` 接下来, 你只需要在 posts.js 里面实现对应的函数就可以了。
-
-
细心的朋友可以看到2.2. egg-sequelize的编写代码的app/controller/users.js文件里面的方法名字其实就是restful风格的命名
4. 编写中间件(对GET、post参数统一获取)
-
我们知道egg中获取参数
-
获取get的参数:
ctx.query -
获取post的参数:
ctx.request.body -
实现效果:我们现在想
ctx.params就可以拿到请求的参数,不管是GET、POST请求,下面手写一个中间件 -
编写中间件的几个步骤:
-
在app目录下新建middleware文件夹
-
在middleware里面新建params.js,内容如下
-
/** * 获取请求参数中间件 * 可以使用ctx.params获取get或post请求参数 */
module.exports = options => { return async function params(ctx, next) { ctx.params = { ...ctx.query, ...ctx.request.body } await next(); }; }; -
在/config/config.default.js里注入中间件
-
/** * appInfo */
'use strict'; module.exports = appInfo => { const config = exports = {}; // 注入中间件 config.middleware = [ 'params', ]; return config; }; -
使用
-
/** * 添加文章接口 */
'use strict'; const Service = require('egg').Service; class ArticleService extends Service { async add() { const { ctx } = this; // 获取请求参数 const { userId, title, content, } = ctx.params; const result = await ctx.model.Article.create({ userId, title, content, }); return result; } } module.exports = ArticleService;
原文地址
参考
Egg 中结合Egg-mysql操作MySQL :blog.csdn.net/weixin_4062…
egg使用总结 (restful):www.jianshu.com/p/322a8dd47…
eggjs添加获取get和post通用中间件 :www.jianshu.com/p/77f0ec461…
egg学习笔记(6)--egg+mysql(sequelize)+vue实现curd :segmentfault.com/a/119000001…