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_at
        timestamps: 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过来
    //首先获取id
    let 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 = {
    // 关闭 csrf
    csrf: {
      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.js

  valparams : {
    enable : true,
    package: 'egg-valparams'
  },


//app/config/config.default.js

config.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);

ParamTypeDescriptionExample
reqObjectrequest 对象,这里我们就是取相应的三种请求的参数进行参数验证{params, query, body}
paramsObject参数的格式配置 { 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].aliasString参数别名,可以使用该参数指定前端使用的参数名称
params[pname].typeString参数类型常用可选类型有 int, string, json 等,其他具体可见下文或用 Valparams.vType 进行查询
params[pname].requiredBoolean是否必须
params[pname].rangeObject参数范围控制{min: '112.80.248.10', max: '112.80.248.72'}
params[pname].range.minALL最小值、最短、最早(不同 type 参数 含义有所差异)
params[pname].range.maxALL最大值、最长、最晚(不同 type 参数 含义有所差异)
params[pname].range.inArray在XX中,指定参数必须为其中的值
params[pname].range.regRegExp正则判断,参数需要符合正则
params[pname].range.schemaObjectjsonSchema,针对JSON类型参数有效,使用ajv对参数进行格式控制
params[pname].defValueALL默认值,没传参数或参数验证出错时生效,此时会将该值赋值到相应参数上
params[pname].trimBoolean是否去掉参数前后空格字符,默认false
params[pname].allowEmptyStrBoolean是否允许接受空字符串,默认false
params[pname].descString参数含义描述
optionsObject参数关系配置
options.choicesArray参数挑选规则[{fields: ['p22', 'p23', 'p24'], count: 2, force: true}] 表示'p22', 'p23', 'p24' 参数三选二
options.choices[].fieldsArray涉及的参数
options.choices[].countNumber需要至少传 ${count} 个
options.choices[].forceBoolean默认 false,为 true 时,涉及的参数中只能传 ${count} 个, 为 false 时,可以多于 ${count} 个
options.equalsArray参数相等[['p20', 'p21'], ['p22', 'p23']] 表示 'p20', 'p21' 两个值需要相等,'p22', 'p23' 两个值需要相等
options.equals[]Array涉及的参数(涉及的参数的值需要是相等的)
options.comparesArray参数大小关系[['p25', 'p26', 'p27']] 表示 'p25', 'p26', 'p27' 必须符合 'p25' <= 'p26' <= 'p27'
options.compares[]Array涉及的参数(涉及的参数的值需要是按顺序从小到大的)
options.casesObject参数条件判断[{when: ['p30'], then: ['p31'], not: ['p32']}] 表示 当传了 p30 就必须传 p31 ,同时不能传p32
options.cases.whenArray条件
options.cases.when[]String涉及的参数,(字符串)只要接收到的参数有这个字段即为真
options.cases.when[].field涉及的参数的名(对象)---
options.cases.when[].value涉及的参数的值(对象)需要参数的值与该值相等才为真---
options.cases.thenArray符合when条件时,需要必传的参数
options.cases.notArray符合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  关闭跨域

  • 31
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值