egg+vue后台完整版
一、创建安装egg脚手架
1.安装脚手架
npm init egg --type=simple
2.安装依赖
npm i
3.运行
npm run dev
二、配置数据库
1.安装数据库插件
npm install --save egg-sequelize mysql2
2.开启插件
config/plugin.js
中引入 egg-sequelize 插件
sequelize: {enable: true,package: 'egg-sequelize',
}
3.配置数据库
文件路径:config/config.default.js
config.sequelize = {dialect: 'mysql',host: '127.0.0.1',username: 'root',password: 'root',port: 3306,database: '数据库表名',// 中国时区timezone: '+08:00',define: {// 取消数据表名复数freezeTableName: true,// 自动写入时间戳 created_at updated_attimestamps: true,// 字段生成软删除时间戳 deleted_at// paranoid: true,createdAt: 'created_at',updatedAt: 'updated_at',// deletedAt: 'deleted_at',// 所有驼峰命名格式化underscored: true}
};
4.创建数据库(也可以自己手动创建)
用命令创建
1.安装插件
npm install --save-dev sequelize-cli
2.创建配置文件
路径:根目录下穿件.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'),
};
3.初始化 Migrations 配置文件和目录
以下两个命令在整个项目中只需要运行一次
npx sequelize init:config
npx sequelize init:migrations
运行完以上命令后根目录下会多出一个database文件夹
database文件夹下会有migarations目录和config.json文件
config.json文件中有三个对象
development:代表开发环境
test:代表测试环境
production:代表生产环境
4.配置以上三个环境中的数据库参数
{"development": {"username": "root","password": root,"database": "数据库名","host": "127.0.0.1","dialect": "mysql","timezone": "+08:00"}
//以下两个同上
}
5.创建数据库
npx sequelize db:create
这时候数据库中会多了一个数据库
库名为三个环境中database中配置的名称
6.创建数据表
创建数据表
npx sequelize migration:generate --name=init-user
执行完以上命令后migarations目录下会多出一个xxxxxx-init-user.js文件
7.配置数据表
database/migarations/xxxxxx-init-user.js文件
'use strict';module.exports = {async up (queryInterface, Sequelize) {const {INTEGER,STRING,DATE,ENUM} = Sequelize;await queryInterface.createTable('user',{id:{type:INTEGER(20).UNSIGNED, //UNSIGNED代表无符号primaryKey:true, //主键autoIncrement:true //自动递增},username:{type:STRING(30),allowNull:false, //是否允许为空defaultValue:'', //默认值为空comment:'用户名称', //备注unique:true //是否是唯一的},password:{type:STRING(200),allowNull:false,defaultValue:''},avatar_url:{type:STRING(200),allowNull:false,defaultValue:''},sex:{type:ENUM,values:['男','女','保密'],allowNull:true,defaultValue:'男',comment:'用户性别'},created_at:DATE,updated_at:DATE})},async down (queryInterface, Sequelize) {await queryInterface.dropTable('user')}
};
# 升级数据库
npx sequelize db:migrate
# 如果有问题需要回滚,可以通过 `db:migrate:undo` 回退一个变更
# npx sequelize db:migrate:undo
# 可以通过 `db:migrate:undo:all` 回退到初始状态
# npx sequelize db:migrate:undo:all
此时数据表已经创建完成。
8.创建数据模型
用途:某个表的增删改查
创建:app/model/表名.js
'use strict';
module.exports = app => {const { STRING,INTEGER, DATE, ENUM } = app.Sequelize;const User = app.model.define('user',{id:{type:INTEGER(20).UNSIGNED, //UNSIGNED代表无符号primaryKey:true, //主键autoIncrement:true //自动递增},username:{type:STRING(30),allowNull:false, //是否允许为空defaultValue:'', //默认值为空comment:'用户名称', //备注unique:true //是否是唯一的},password:{type:STRING(200),allowNull:false,defaultValue:'',/*** 修改器set(val){//可以在这对密码进行加密,然后let hash = 加密命令this.setDataValue('password',hash)}* */},avatar_url:{type:STRING(200),allowNull:false,defaultValue:''},sex:{type:ENUM,values:['男','女','保密'],allowNull:true,defaultValue:'男',comment:'用户性别'},created_at:{type:DATE,//转换成时间戳get(){const val = this.getDataValue('created_at');return (new Date(val)).getTime();}},updated_at:{type:DATE,//转换成时间戳get(){const val = this.getDataValue('updated_at');return (new Date(val)).getTime();}}});return User;
}
其他参数,随用随添加
//自定义表名
'freezeTableName':true,
'tableName':'xxxxxx',//是否需要增加createdAt、updatedAt、deletedAt字段
'timestamps':true,//不需要createdAt字段
'createdAt':false,//将updatedAt字段改革名
'updatedAt':'新名',//将deletedAt字段改名
//同时需要设置paranoid为true(此种模式下,删除数据时不会进行物理删除,二十设置deletedAt为当前时间)
'deletedAt':'dtimm',
'paranoid':true,//此参数需要配置在app/model/控制器/updated:DATE
// },{
//这里面
//}
9.增删改查
await this.app.model.User.方法名
增
//单个
create(obj)
//批量
bulkCreate(Array)
删
//删除单个
async delet(){let id = this.ctx.params.id ? parseInt(this.ctx.params.id) : 0;let data = await this.app.model.User.findByPk(id);if(!data){return this.ctx.body = {msg:'fail',data:"该数据不存在。"};}let res = await data.destroy();this.ctx.body = {msg:'ok',data:res }};//删除多个
async deletMore(){const Op = this.app.model.Sequelize.Op;let data = await this.app.model.User.destroy({where:{id:{[Op.lte]:7 //删除id小于7的数据}}});this.ctx.body = {msg:'ok',data:res }};
改
//连接中需要传要修改的id过来//首先获取idlet id = this.ctx.params.id ? parseInt(this.ctx.params.id) : 0;//获取指定的记录let data = await this.app.model.User.findByPk(id);let params = this.ctx.request.body;//判断数据是否存在if(!data){return this.ctx.body = {msg:"fail",data:"该记录不存在。"}}//let res = await data.update(params,{fielda:['username']}); //添加{fielda:['username']}标识仅仅修改username字段,其余的均不更改let res = await data.update(params);this.ctx.body = {msg:"ok",data:res}}
查
//单个,无条件
findByPk(parseInt(id)); //parseInt转换成int类型
//单个,加条件过滤
findOne({where:{id:2,sex:"女",条件....}
})const Op = this.app.Sequelize.Op;
findAndCountAll({where:{username:{[Op.like] : "%5%"},条件:{.....}}
});//全部
findAll()
可以加条件方法:
过滤参数
只显示数组中的参数:
attributes:['username','sex',...]
相反:
attributes:{exclude:['password'] //除了password不显示,其余均显示
}//排序,如果没作用,调换一下条件顺序试试
order:[['id','DESC'], //DESC降序,ASC升序条件...
]//分页
首先在连接中需要加入页码参数?page=1
在查询的开始就要拿到page的参数
let page = this.ctx.quer.page ? parseInt(this.ctx.quer.page) : 1; //页码
let limit = 5;
let offset = (page - 1) * 5; //计算偏移//以下写到findAll()方法中的一级和where是同级
offset, //偏移,从哪开始
limit //显示5条//查全部并计数
findAndCountAll()
Op参数
[Op.and]: {a: 5} // 且 (a = 5)
[Op.or]: [{a: 5}, {a: 6}] // (a = 5 或 a = 6)
[Op.gt]: 6, // id > 6
[Op.gte]: 6, // id >= 6
[Op.lt]: 10, // id < 10
[Op.lte]: 10, // id <= 10
[Op.ne]: 20, // id != 20
[Op.eq]: 3, // = 3
[Op.not]: true, // 不是 TRUE
[Op.between]: [6, 10], // 在 6 和 10 之间
[Op.notBetween]: [11, 15], // 不在 11 和 15 之间
[Op.in]: [1, 2], // 在 [1, 2] 之中
[Op.notIn]: [1, 2], // 不在 [1, 2] 之中
[Op.like]: '%hat', // 包含 '%hat'
[Op.notLike]: '%hat' // 不包含 '%hat'
[Op.iLike]: '%hat' // 包含 '%hat' (不区分大小写) (仅限 PG)
[Op.notILike]: '%hat' // 不包含 '%hat' (仅限 PG)
[Op.regexp]: '^[h|a|t]' // 匹配正则表达式/~ '^[h|a|t]' (仅限 MySQL/PG)
[Op.notRegexp]: '^[h|a|t]' // 不匹配正则表达式/!~ '^[h|a|t]' (仅限 MySQL/PG)
[Op.iRegexp]: '^[h|a|t]' // ~* '^[h|a|t]' (仅限 PG)
[Op.notIRegexp]: '^[h|a|t]' // !~* '^[h|a|t]' (仅限 PG)
[Op.like]: { [Op.any]: ['cat', 'hat']} // 包含任何数组['cat', 'hat'] - 同样适用于 iLike 和 notLike
[Op.overlap]: [1, 2] // && [1, 2] (PG数组重叠运算符)
[Op.contains]: [1, 2] // @> [1, 2] (PG数组包含运算符)
[Op.contained]: [1, 2] // <@ [1, 2] (PG数组包含于运算符)
[Op.any]: [2,3] // 任何数组[2, 3]::INTEGER (仅限PG)
[Op.col]: 'user.organization_id' // = 'user'.'organization_id', 使用数据库语言特定的列标识符, 本例使用 PG
三、关闭csrf开启跨域
1.安装save
npm i egg-cors --save
2.配置插件
文件路径:config/plugin.js
//跨域
cors:{enable: true,package: 'egg-cors',
},
3.关闭csrf开启跨域
文件路径:config/config.default.js
全局关闭
config.security = {// 关闭 csrfcsrf: {enable: false,},// 跨域白名单domainWhiteList: [],};// 允许跨域的方法config.cors = {origin: '*',allowMethods: 'GET, PUT, POST, DELETE, PATCH'};//写在这的上边
//return{
//...config,
//...xxxxx
//}
过滤关闭(这里表示凡是以/api开头的路由都不需要经过csrf验证)
config.security = {//关闭 csrf//api这个就不需要用csrf验证,所以用这段代码可以排除掉以/api开头的请求csrf: {headerName:'x-csrf-token',ignore:ctx => {return ctx.request.url.startsWith('/api')}},// 跨域白名单domainWhiteList: [],};// 允许跨域的方法config.cors = {origin: '*',allowMethods: 'GET, PUT, POST, DELETE, PATCH'};
四、中间件
错误异常处理
1.新建文件夹 app/middleware
2.新建中间件文件 app/middleware/error_hendler.js
module.exports = () => {return async function errorHandler(ctx,next){try {await next();}catch(err){ctx.app.emit('error',err,ctx);const status = err.status || 500;const error = status === 500 && ctx.app.config.env === 'prod'? 'Internal Server Error' : err.message;ctx.body = { error};if(status === 422){ctx.body.detail = err.errors;}ctx.status = status}};
};
4.开启中间件
app/config/config.default.js
// add your middleware config here
config.middleware = ['errorHendler'];
5.配置中间件
app/config/config.default.js
在config.middleware = ['errorHendler'];下继续写配置
config.errorHendler= {enable:true, //是否开启中间件match:["/user/list"] //设置哪些路由走中间件//ignore:["/user/list"] //设置哪些路由不走中间件/**1.match和ignore不能同时使用2.例如:match:["/user"],只要包含/user的任何页面都生效**///match和ignore支持多种类型配置方式:字符串、正则、函数(推荐)match(ctx){//只有ios设备才开启const res = /iphone|ipad|ipod/i;return res.test(ctx.get('user-agent'));}
};
五、参数验证
安装插件
npm i egg-valparams --save
配置插件
//config/plugin.jsvalparams : {enable : true,package: 'egg-valparams'},//app/config/config.default.jsconfig.valparams = {locale : 'zh-cn',throwError: true
};//使用
/*获取传过来的参数*/
let params = this.ctx.request.body;
this.ctx.validate({username:{type: 'string', //数据类型required: true, //是否必填desc: '用户名' //字段描述},password:{type: 'string', required: true, desc: '密码' },sex:{type:'string', required: false, defValue: '保密', //默认值desc: '性别' }});
//写入数据库
ValParams API 说明
参数验证处理
Valparams.setParams(req, params, options);
Param | Type | Description | Example |
---|---|---|---|
req | Object | request 对象,这里我们就是取相应的三种请求的参数进行参数验证 | {params, query, body} |
params | Object | 参数的格式配置 { pname: {alias, type, required, range: {in, min, max, reg, schema }, defValue, trim, allowEmptyStr, desc[, detail] } } | {sysID : {alias:'sid',type: 'int', required: true, desc: '所属系统id'}} |
params[pname] | String | 参数名 | |
params[pname].alias | String | 参数别名,可以使用该参数指定前端使用的参数名称 | |
params[pname].type | String | 参数类型 | 常用可选类型有 int, string, json 等,其他具体可见下文或用 Valparams.vType 进行查询 |
params[pname].required | Boolean | 是否必须 | |
params[pname].range | Object | 参数范围控制 | {min: '112.80.248.10', max: '112.80.248.72'} |
params[pname].range.min | ALL | 最小值、最短、最早(不同 type 参数 含义有所差异) | |
params[pname].range.max | ALL | 最大值、最长、最晚(不同 type 参数 含义有所差异) | |
params[pname].range.in | Array | 在XX中,指定参数必须为其中的值 | |
params[pname].range.reg | RegExp | 正则判断,参数需要符合正则 | |
params[pname].range.schema | Object | jsonSchema,针对JSON类型参数有效,使用ajv对参数进行格式控制 | |
params[pname].defValue | ALL | 默认值,没传参数或参数验证出错时生效,此时会将该值赋值到相应参数上 | |
params[pname].trim | Boolean | 是否去掉参数前后空格字符,默认false | |
params[pname].allowEmptyStr | Boolean | 是否允许接受空字符串,默认false | |
params[pname].desc | String | 参数含义描述 | |
options | Object | 参数关系配置 | |
options.choices | Array | 参数挑选规则 | [{fields: ['p22', 'p23', 'p24'], count: 2, force: true}] 表示'p22', 'p23', 'p24' 参数三选二 |
options.choices[].fields | Array | 涉及的参数 | |
options.choices[].count | Number | 需要至少传 ${count} 个 | |
options.choices[].force | Boolean | 默认 false,为 true 时,涉及的参数中只能传 ${count} 个, 为 false 时,可以多于 ${count} 个 | |
options.equals | Array | 参数相等 | [['p20', 'p21'], ['p22', 'p23']] 表示 'p20', 'p21' 两个值需要相等,'p22', 'p23' 两个值需要相等 |
options.equals[] | Array | 涉及的参数(涉及的参数的值需要是相等的) | |
options.compares | Array | 参数大小关系 | [['p25', 'p26', 'p27']] 表示 'p25', 'p26', 'p27' 必须符合 'p25' <= 'p26' <= 'p27' |
options.compares[] | Array | 涉及的参数(涉及的参数的值需要是按顺序从小到大的) | |
options.cases | Object | 参数条件判断 | [{when: ['p30'], then: ['p31'], not: ['p32']}] 表示 当传了 p30 就必须传 p31 ,同时不能传p32 |
options.cases.when | Array | 条件 | |
options.cases.when[] | String | 涉及的参数,(字符串)只要接收到的参数有这个字段即为真 | |
options.cases.when[].field | 涉及的参数的名(对象) | --- | |
options.cases.when[].value | 涉及的参数的值(对象)需要参数的值与该值相等才为真 | --- | |
options.cases.then | Array | 符合when条件时,需要必传的参数 | |
options.cases.not | Array | 符合when条件时,不能接收的参数 |
功能参数
1.获取get连接的数据
this.ctx.params.xx; 解释:xx代表的是参数
获取url的问号get传值参数
http://127.0.0.1/1?user=2&paw=3
获取1 this.ctx.query.路由名问号后面的参数名
获取2 this.ctx.query.user;
获取3 this.ctx.query.paw;
2.获取psot数据
this.ctx.request.body
3.从某个数组中拿到指定参数的数据
数组名.find(item => item.字段 == 参数); 解释:字段代表对象中的字段名 参数代表指定的参数
4.修改状态码
this.ctx.status = 201;
错误处理
1.ctx is not defined 某的地方缺少this,在ctx前加上this.
2.missing csrf token 关闭跨域
egg+vue后台完整版相关推荐
- 微信小程序电影订票系统+SpringBoot后台完整版
在网上有很多关于电影订票的小程序,但是很多都只是有前端页面,没有后台项目源码,还有的就是用别人家的数据,比如:仿猫眼的,然后说他们限制API接口的使用 最后,本人亲自独立写了一套完成的电影小程序源码, ...
- 前端学习 Vue笔记 完整版
1.1 搭建Vue开发环境 项目导入开发版本文件 <script type="text/javascript" src="../js/vue.js"> ...
- 微信小程序 小程序源码包括后台完整版分享
需要的留邮箱 免费发! 版权归作者所有,任何形式转载请联系作者. 作者:执波仔丶(csdn博客) 最新收集的60个微信小程序源码分享+开发视频教程最新收集的60个微信小程序源码分享+开发视频教程最新收 ...
- 手机移动端加载更多(表格首行首列固定vue + vant完整版)
直接看效果图和实现的代码 <van-list:offset="60"v-model="isLoading":finished="finished ...
- vue中组件之间传值的六种方式(完整版)
前言 组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用.一般来说,组件可以有以下几种关系: 如上图所示,A 和 B.B 和 C.B ...
- vue组件间通信六种方式(完整版)
前言 组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用.一般来说,组件可以有以下几种关系: 如上图所示 ...
- ktv管理系统_7天撸完KTV点歌系统,含后台管理系统(完整版)
最近手有点痒琢磨着做个啥,朝思暮想还是写个KTV点歌系统,模拟了一下KTV开户的思路,7天累死我了,不过技术点还挺多的,希望你可以看完(〜^㉨^)〜 用Node(Express)教你写KTV点歌系统, ...
- 星力+手游运营版本完整版打包下载 代理+服务端+后台+更新+APP
介绍: 星力+手游运营版本完整版打包下载 代理+服务端+后台+更新+APP 网盘下载地址: https://zijiewangpan.com/AM4tpRbz0L2 图片:
- 活动报名发布小程序源码 聚会活动报名小程序完整版前端+后台管理Think
活动报名发布小程序源码 聚会活动报名小程序完整版前端+后台管理Think下载链接:https://pan.baidu.com/s/16e3egizS_bjuJTv2YcA6Xg 提取码:6n11
最新文章
- 嵌套SQL语句訪问DB2中SQLCA的调用技巧
- python for in循环_Python傻瓜教程:跟我学for循环
- 【Python基础】Python画王者荣耀英雄能力雷达图
- 代码覆盖率测试工具:gcov和lcov的使用
- hdu 3887 Counting Offspring
- 136. 只出现一次的数字 golang
- Django之ORM对数据库操作
- dbcp连接池配置详解_JDBC第四篇【数据库连接池、DbUtils框架、分页】(修订版)
- 更适合Pythoner的标记语言Yaml学习总结
- linux json 写sql注入,sql注入之AJAX(SQL Injection (AJAX/JSON/jQuery))
- linux释放内存后设备起不来,Linux-Memory小记
- tablesorter,jquery
- 程序员玩游戏之四--娱网棋牌大连打滚子记牌器
- PHP 获取网页标题(title)、描述(description)、关键字(keywords)等meta信息
- 手机中.android_secure文件夹中的文件能删除吗,安卓手机里的各“文件夹”都是什么?能删吗?-手机相册在哪个文件夹...
- C# 键盘中的按键对应的KeyValue
- OCR应用:名片识别
- asp.net旅游网站系统VS开发sqlserver数据库web结构c#编程计算机网页项目
- js chrome 富文本 恢复光标的办法
- vue3+ant design vue+ts实战【ant-design-vue组件库引入】