Egg.js框架使用文档

创建项目

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

修改项目目录

cd app/
mkdir service middleware schedule extend
cd ../config/
touch config.prod.js config.unittest.js config.local.js
cd ../test/app/
mkdir service extend

修改项目的端口号

修改端口号要在指定的配置文件中修改,修改的文件名称是config.[env].js。例如本地开发时将端口号改为9100,则打开config.local.js文件,添加下面的代码:

// edit port
  config.cluster = {
    listen: {
      path: '',
      port: 9100,
      hostname: '0.0.0.0',
    },
  };

运行项目,在浏览器中访问http://localhost:9100/,有内容输出表示修改成功。

配置mysql数据库

安装mysql插件:

npm i --save egg-mysql

开启插件:

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

配置单个数据库:

// config/config.${env}.js
config.mysql = {
  // 单数据库信息配置
  client: {
    // host
    host: 'mysql.com',
    // 端口号
    port: '3306',
    // 用户名
    user: 'test_user',
    // 密码
    password: 'test_password',
    // 数据库名
    database: 'test',
  },
  // 是否加载到 app 上,默认开启
  app: true,
  // 是否加载到 agent 上,默认关闭
  agent: false,
};

测试数据连接是否成功:

// app/service/user.js
const Service = require('egg').Service;

class UserService extends Service {
  async getUserInfo(uid) {
    const { app } = this;
    try {
      // 相当于 select * from user where id=1 limit 0,1;
      const user = await app.mysql.get('user', { id: uid });
      return user;
    } catch (e) {
      return null;
    }
  }
}

module.exports = UserService;
// app/controller/user.js

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

class UserController extends Controller {
  async getUserInfo() {
    const { ctx, service } = this;
    // 获取路由上面预定义的参数
    const uid = ctx.params.uid || '';
    const result = await service.user.getUserInfo(uid);
    if (result) {
      ctx.body = JSON.stringify(result);
    } else {
      ctx.body = 'can not find user!';
    }
  }
}

module.exports = UserController;
// app/router.js
module.exports = app => {
  const { router, controller } = app;
  router.get('/user/find/:uid', controller.user.getUserInfo);
};

打开浏览器输入http://localhost:9100/user/find/1,页面输出数据库的数据则表示正常。

格式化HTTP响应内容

使用自定义中间件实现接口返回格式的同一整理。

创建辅助方法:

// app/extend/context.js
function formatResponse(code = 200, data = null, err_msg = '', tips = '') {
  return {
    code,
    data,
    err_msg,
    tips,
  };
}

module.exports = {
  formatResponse,
};

创建中间件:

//app/middleware/formatResponse.js
module.exports = () => {
  return async function formatResponse(ctx, next) {
    await next();
    if (ctx.status === 200) {
      ctx.body = ctx.formatResponse(ctx.status, ctx.body);
    } else {
      ctx.body = ctx.formatResponse(ctx.status, null, ctx.body);
    }
  };
};

在配置文件中添加中间件:

//config/config.default.js
......
config.middleware = [ 'formatResponse' ];
......

数据加密

安装插件:

npm i bcrypt

使用插件:

// app/extend/encryption.js
const bcrypt = require('bcrypt');
const saltRounds = 10;

/**
 * 获取加密后的密码
 * @param {String} pwdText 密码字符串
 */
exports.getBcryptPWD = (pwdText = '') => bcrypt.hashSync(pwdText, saltRounds);

/**
 * 比较密码和数据库中的加密密码
 * @param {String} pwdText 密码字符串
 * @param {String} hash 加密密码
 */
exports.comparePWD = (pwdText = '', hash = '') => {
  return bcrypt.compareSync(pwdText, hash);
};
// app/extend/helper.js
const { getBcryptPWD, comparePWD } = require('./encryption.js');

module.exports = {
  getBcryptPWD,
  comparePWD,
};

使用插件:

// app/controller/user.js
const Controller = require('egg').Controller;

class UserController extends Controller {
  async addUser() {
    const { app, ctx, service } = this;
    const { name, password } = ctx.request.body;
    try {
      const result = await service.user.addUser(name, ctx.helper.getBcryptPWD(password));
      ctx.body = `Bearer ${app.jwt.sign({
        id: result.insertId,
        name,
        password,
      }, app.config.jwt.secret)}`;
    } catch (err) {
      ctx.status = 500;
      ctx.body = '用户创建失败';
    }
  }

  async verifyUser() {
    const { ctx, service } = this;
    const { name: postName = '', password: postPWD = '' } = ctx.request.body;
    try {
      const user = await service.user.findUser(postName);
      if (ctx.helper.comparePWD(postPWD, user.password)) {
        ctx.body = 'ok';
      } else {
        ctx.status = 500;
        ctx.body = '用户不存在';
      }
    } catch (err) {
      ctx.status = 500;
      ctx.body = '用户名重复';
    }
  }
}

module.exports = UserController;

注意

  1. 创建数据库时用户名是唯一的
  2. 创建用户时密码进行加密
  3. 查询时根据用户名查询且验证加密的密码和传入的密码是否相等。

Token应用

使用token对用户进行验证;

安装插件:

npm install egg-jwt --save

配置插件:

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

配置文件设置参数:

// config/config.default.js
// config jwt
config.jwt = {
    secret: appInfo.name, // 自定义加密使用的字符串
    expiresIn: '7d', // 定义jwt的有效时间
};

配置需要token验证的路由:

// app/router.js
module.exports = app => {
  const { router, controller, jwt } = app;
  router.post('/user/add', controller.user.addUser);
  router.post('/user/verify', jwt, controller.user.verifyUser);
};

使用jwt:

// app/controller/user.js
......
async addUser() {
    const { app, ctx, service } = this;
    const { name, password } = ctx.request.body;
    try {
      const result = await service.user.addUser(name, ctx.helper.getBcryptPWD(password));
      ctx.body = `Bearer ${app.jwt.sign({
        id: result.insertId,
        name,
        password,
      }, app.config.jwt.secret)}`;
    } catch (err) {
      ctx.status = 500;
      ctx.body = '用户创建失败';
    }
 }
......

创建测试

测试service接口

// test/app/service/user.test.js
'use strict';

const { app, assert } = require('egg-mock/bootstrap');
const randomNum = Math.floor(Math.random() * 10000);

describe('test/app/service/user.test.js', () => {
  // 向数据库查询数据
  it('should insert user', async () => {
    const ctx = app.mockContext();
    try {
      const result = await ctx.service.user.addUser(`王${randomNum}`, ctx.helper.getBcryptPWD(`${randomNum}`));
      assert.ok(result);
      console.log(`insert result: ${JSON.stringify(result)}`);
    } catch (err) {
      assert.ok(false);
    }
  });

  // 测试数据库查询
  it('should find user', async () => {
    const ctx = app.mockContext();
    const user = await ctx.service.user.findUser(`王${randomNum}`);
    console.log(`find user: ${JSON.stringify(user)}`);
    assert.ok(user);
  });
});

运行测试:

npm run test-local

常见错误

post请求提示‘missing csrf token’

在配置文件中关闭csrf防护

// config/config.default.js
// close csrf protect
  config.security = {
    csrf: {
      enable: false,
    },
  };

你可能感兴趣的:(程序员笔记,web开发笔记)