zhanghaoran
文章 文章详情

eggjs笔记: 资源导航

阅读:40 分类:Node.js 发布时间:12天前

创建项目

mkdir egg-example && cd egg-example
npm init egg --type=simple
npm i

用户鉴权中间件

指定路由使用方式

创建鉴权中间件
// app/middleware/isAdmin.js

module.exports = (options, app) => {
    return async function isAdminMiddleware(ctx, next){
        // 获取请求头token
        if (!ctx.req.headers.authorization) {
            ctx.status = 403;
            ctx.body = '请登录!'
        }
        else {
            // 拿到token去验证
            const result = await ctx.curl("验证URL", {
                method: 'get',
                headers:{//自定义header
                    authorization: ctx.req.headers.authorization
                },
            });
            let data = JSON.parse(result.data.toString('utf8'));
            if(data.code != 0){
                ctx.status = data.code;
                ctx.body = '您没有权限访问!';
            }else {
                await next();
            }
        }
    }
};
在需要的路由处加入中间件
'use strict';

module.exports = app => {
  const { router, controller } = app;
  const isAdmin = app.middleware.isAdmin(); // 先赋值给常量
  router.get('/api/site_type', controller.siteType.index);
  router.post('/api/site_type', isAdmin, controller.siteType.create);   // 哪个路由需要给哪个
};

全局使用中间件

详见官方文档:https://eggjs.org/zh-cn/basics/middleware.html

连接MySQL

安装插件 egg-mysql

npm i egg-mysql -S

开启插件

// config/plugin.js
exports.sequelize = {
    enable: true,
    package: 'egg-sequelize',
};

配置连接信息

// config/config.default.js  
config.sequelize = {
    dialect: 'mysql',       
    database: 'siteApi',        // 数据库名称
    host: '192.168.0.104',  // 数据库地址
    port: 3306,
    username: 'root',               // 账号
    password: 'root',               // 密码
    define: {
      // 使用自定义的表名
      freezeTableName: true,
      // 自动生成时间戳 -小驼峰式
      timestamps: true,
      // 表名小驼峰
      underscored: false,
    },
  };

同步数据库配置

// app/router.js
module.exports = app => {
  // 同步数据库
  app.beforeStart(async () => {
    //force  false 为不覆盖 true会删除再创建; alter true可以 添加或删除字段;
    await app.model.sync({ alter: true });
  });
  }

创建model文件夹,并创建数据表

创建model文件夹
// app/
mkdir model/site.js
写入orm创建表
// app/model/siteList.js
'use strict';

module.exports = app => {
    const { STRING, INTEGER, DATE, BOOLEAN } = app.Sequelize; // 获取数据类型
    const SiteList = app.model.define(
        'siteList',
        {
            id: { type: INTEGER, primaryKey: true, autoIncrement: true },
            name: { type: STRING, allowNull: false, comment: '网站名称'},
            url: { type: STRING, allowNull: false, comment: '网站地址'},
            icon: { type: STRING, allowNull: true, comment: '网站图标'},
            intro: { type: STRING, allowNull: true, comment: '网站描述'},
            order: { type: INTEGER, allowNull: true, defaultValue: 0, comment: '网站排序'},
            isShow: { type: BOOLEAN, allowNull: true, defaultValue: true },
            createdAt: { type: DATE, defaultValue: app.Sequelize.NOW },
        },
        {
            freezeTableName: true, // Model 对应的表名将与model名相同
            timestamps: false,
        },
    );

    SiteList.associate = function (){
        // 与siteTYpe做一对多关联, siteId 做主键
        app.model.SiteList.belongsTo(app.model.SiteType, {foreignKey: 'siteId', targetKey: 'id'});
    };
    return SiteList;
};
// app/model/siteType.js
'use strict';

module.exports = app => {
    const { STRING, INTEGER, DATE, BOOLEAN } = app.Sequelize; // 获取数据类型
    const SiteType = app.model.define(
        'siteType',
        {
            id: { type: INTEGER, primaryKey: true, autoIncrement: true },
            name: { type: STRING, allowNull: false, comment: '分类名称',unique: true},
            order: { type: INTEGER, allowNull: true, comment: '分类排序', defaultValue: 0},
            isShow: { type: BOOLEAN, allowNull: true, defaultValue: true },
            createdAt: { type: DATE, defaultValue: app.Sequelize.NOW },
        },
        {
            freezeTableName: true, // Model 对应的表名将与model名相同
            timestamps: false,
        }
    );
    SiteType.associate = function (){
        // siteType与siteList是一对多关系,所以这里使用hasMany()
        app.model.SiteType.hasMany(app.model.SiteList, {foreignKey: 'siteId', targetKey: 'id'});
    }
    return SiteType;
};

增删改查

// app/service/siteList.js

const Service = require('egg').Service;

class SiteListService extends Service {
    // 关联查询
    async getAllSite(showIsReturn, order='desc') {
        const ctx = this.ctx;
        var options;
        if(!showIsReturn){
            options = {include: [{model: ctx.model.SiteList, required: false, order:[[ 'id', 'asc']] }], order: [[ 'order', order]]};
        }else {
            options = {include: [{model: ctx.model.SiteList, required: false, order:[[ 'id', 'asc']], where:{isShow:true}},],where:{isShow:true}, order: [['order', order]]};
        }
        let result = await ctx.model.SiteType.findAll(options);
        return result;
    }

  // 创建
    async addSite(obj) {
        const ctx = this.ctx;
        obj.order = obj.order ? obj.order : 0;
        const result = await ctx.model.SiteList.create(obj);
        return result;
    }
    // 修改
    async updateSite(id, obj) {
        const ctx = this.ctx;
        const siteType = await ctx.model.SiteList.findByPk(id);
        if (!siteType) {
            this.ctx.throw(404, 'site not found');
        }
        obj.order = obj.order ? obj.order : 0;
        return siteType.update(obj);
    }

    // 删除
    async removeSite(id) {
        const ctx = this.ctx;
        const site = await ctx.model.SiteList.findByPk(id);
        if (!site) {
            this.ctx.throw(404);
        }
        return site.destroy();
    }
}
module.exports = SiteListService;

部署

将代码上传到服务器

安装依赖库

npm install --production

安装egg-script

npm i egg-scripts --save

执行 npm run start

// package.json 
{
  "scripts": {
    "start": "egg-scripts start --port 8073 --daemon --title=egg-server-siteAPI",
    "stop": "egg-scripts stop --title=egg-server-siteAPI",
  },
}

nginx路由转发和静态文件转发配置

server{
        listen 80;
        server_name domain.com;
        client_max_body_size 10M;
        gzip on;
        gzip_types      text/plain application/xml text/css application/javascript;
        gzip_min_length 1000;
        location / {
                proxy_redirect                      off;
                proxy_set_header Host               $host;
                proxy_set_header X-Real-IP          $remote_addr;
                proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto  $scheme;
                proxy_read_timeout          1m;
                proxy_connect_timeout       1m;
                proxy_pass                          http://127.0.0.1:8073;  # egg运行端口
        }
        # 静态资源(跨域)访问设置
        location /public {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Credentials' 'true';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';

                alias /home/fanyijiang/resoure_link/siteAPI/app/public;     # 静态文件绝对路径
        }
}

参数校验模块 egg-validate

安装

npm install --save egg-validate

启用

// config/plugin.js
exports.validate = {
  enable: true,
  package: 'egg-validate',
};

使用

// config/controoler
// 定义创建接口的请求参数规则
const createRule = {
    name: {type: 'string', required : true },
    url: {type: 'string', required : true },
    icon: {type: 'string', required : false },
    intro: {type: 'string', required : false },
    order: {type: 'number', required: false},
    isShow: {type: 'boolean', required: false},
};

class SitelistController extends Controller {
    async create(){
    const { ctx } = this;
    // 验证请求体参数符合规则不
    ctx.validate(createRule, ctx.request.body);
  }
}

取消csrf-token

  // config/config.default.js
  config.security = {
    csrf: {
      enable: false,    // 只写这一个也可以
      ignoreJSON: true  // 取消csrf_token非必选
    },
    domainWhiteList: ['*']  // 取消csrf_token非必选
  };

跨域

安装

npm i egg-cors -S

启用插件

// config/plugin.js
exports.cors = {
    enable: true,
    package: 'egg-cors',
};

跨域全局默认配置

// config/config.default.js
config.security = {
  csrf: {
    enable: false,
    ignoreJSON: true
  },
  domainWhiteList: ['*']
};
config.cors = {
  origin:'*',
  allowMethods: 'GET,PUT,POST,DELETE,PATCH'
};
// 有些参数不太明白, 跨域和取消CSRF_TOKEN 携程这样没问题

关于post、put我为什么接收不到formdata传值???

最后:贴一个我保存图片的代码

const fs = require('fs');
const crypto = require('crypto');

function backDta(code=0, data=[]) {
    return {code: code,data: data}
}

async create(){
  const { ctx } = this;
  if(!ctx.request.body.siteId){
    ctx.throw(502, 'siteId NOT FOUND');
  }
  ctx.validate(createRule, ctx.request.body);

  var imgData = ctx.request.body.icon;
  if(imgData && imgData.substring(0, 7) != '/public'){
    //过滤data:URL
    var base64Data = imgData.replace(/^data:image\/\w+;base64,/, "");
    let fileType = imgData.substring(11, 14);
    let allowType = ['png', 'jpg', 'jpeg', 'gif'];
    var isAllow = true;
    if(allowType.indexOf(fileType) < 0){
        if(fileType == 'jpe') {fileType = 'jpeg'}
        else{
        isAllow = false; 
        ctx.status=200; 
        ctx.body=backDta(500, {error: '不受支持的文件'})
    }
    return
  }
  if(isAllow) {
    let fileName = crypto.createHash('md5').update(base64Data).digest('hex');
    let filePath = `app/public/images/${fileName}.${fileType}`;
    var dataBuffer = new Buffer(base64Data, 'base64');
    fs.writeFile(filePath, dataBuffer, function (err) {
    if (err) {
        console.log(err)
    } else {
        console.log('ok')
    }
  })
    ctx.request.body.icon = ctx.app.config.imageHost + fileName + '.' + fileType;
  }
  }
  const res = await ctx.service.siteList.addSite(ctx.request.body);
  ctx.status = 200;
  ctx.body = backDta(0, res);
}

项目地址: http://www.qidulp.com/resources_link/