使用koa2写接口的一般步骤

807 阅读4分钟

1、前言

这记录的是我使用koa2写后端接口的一些步骤。

前期简单安装一些依赖(根据需要再添加)

  • npm init -y

  • npm install koa

  • npm install nodemon -D--开发依赖

  • npm install dotenv,可加载根目录下的.env文件,将里面的直接加载到环境变量里面process.env

  • npm install koa-router路由库

  • npm install koa-bodyparser:中间件,解析json数据

  • npm install mysql2连接数据库

2、文件目录拆分

  • src

    • app:主体部分
    • constants:放一些常量
    • controller:控制器,写一些逻辑操作,比如,获取我们输入的信息,再调用service中操作数据库的方法
    • middleware:中间件,比如验证授权、验证输入的账号密码是否正确/存在、验证token是否已过期等
    • router:路由层,在这个文件夹中写路由
    • serviceservice层,就是操作数据库的一些方法
    • utils:放一些可以用到的方法,比如读取文件的方法
    • main.js:程序的入口,这个入口,越简单越好
  • .env:一些不能写死的环境变量,比如,服务端口port,数据库的一些信息,抽离到这个文件中

3、写接口的步骤

  • 习惯性依次的创建xxx.router.jsxxx.middleware.jsxxx.controller.jsxxx.service.js这几个文件,当然xxx.middleware.js这个可根据需求决定是否需要创建,或者引用已经创建好的中间件文件

  • 事例如下:

(1)在router文件夹中创建文件,如:department.router.js

// 请求路径 中间件处理的 映射
const Router = require('koa-router');

// 中间件

// 具体的处理逻辑 增删改查
const {
  list,
  create,
  update,
  remove
} = require('../controller/department.controller');

// 前缀:
const deportmentRouter = new Router({
  prefix: '/api/department'
});

deportmentRouter.post('/', create);
deportmentRouter.get('/list', list);
deportmentRouter.patch('/:departmentId', update);
deportmentRouter.delete('/:departmentId', remove);

// 导出
module.exports = deportmentRouter;

(2)在middleware文件夹下创建auth.middleware.js文件

  • 未进行鉴权

(3)在controller文件夹中创建文件,如:department.controller.js

const departmentService = require('../service/department.service')

class DepartmentController {

  async list(ctx, next) {
    // 1.获取数据(offset/size)
    const {
      offset,
      size
    } = ctx.query;

    // 2.查询列表
    const {
      result,
      count
    } = await departmentService.getDepartmentList(offset, size);

    ctx.body = {
      code: 200,
      data: result,
      totalCount: count[0].total
    };
  }

  async create(ctx, next) {
    // 获取用户请求传递的参数
    const department = ctx.request.body;

    // 查询数据库 -> 抽离到service
    const result = await departmentService.create(department);

    // 返回结果
    ctx.body = {
      code: 200,
      data: result
    };
  }

  async update(ctx, next) {
    const {
      departmentId
    } = ctx.params;
    const {
      departmentName,
      principal,
      adviser,
      departmentTotal
    } = ctx.request.body;

    const result = await departmentService.update(departmentName, principal, adviser, departmentTotal, departmentId);
    ctx.body = {
      code: 200,
      data: result
    };
  }

  async remove(ctx, next) {
    // 1.
    const {
      departmentId
    } = ctx.params;

    // 2.删除内容
    const result = await departmentService.remove(departmentId);

    ctx.body = {
      code: 200,
      data: result
    };
  }
}

module.exports = new DepartmentController();

(4)在service文件夹下创建department.service.js文件

const connection = require('../app/database');

class DeportmentService {
  async getDepartmentList(offset, size) {
    const statement = `
      SELECT 
        d.id id, d.department_name departmentName, d.principal principal, d.adviser adviser, d.department_total departmentTotal,  d.createAt createAt, d.updateAt updateAt
      FROM department d
      LIMIT ?, ?;
    `;

    const state = `SELECT COUNT(*) total FROM department`;
    const [result] = await connection.execute(statement, [offset, size]);

    const [count] = await connection.execute(state);

    return {
      result,
      count
    };

  }

  async create(deportment) {
    const {
      departmentName,
      principal,
      adviser,
      departmentTotal
    } = deportment;

    // 使用sql语句将user存储到数据库中
    const statement = `INSERT INTO department (department_name, principal, adviser, department_total) VALUES (?, ?, ?, ?);`;
    const result = await connection.execute(statement, [departmentName, principal, adviser, departmentTotal]);

    return result[0];
  }

  async update(departmentName, principal, adviser, departmentTotal, deportmentId) {
    const statement = `UPDATE department SET department_name = ?, principal = ?, adviser = ?, department_total = ? WHERE id = ?;`;
    const [result] = await connection.execute(statement, [departmentName, principal, adviser, departmentTotal, deportmentId]);
    return result;
  }

  async remove(deportmentId) {
    const statement = `DELETE FROM department WHERE id = ?;`;
    const [result] = await connection.execute(statement, [deportmentId]);
    return result;
  }
}

module.exports = new DeportmentService();

4、app文件夹中的一些内容

(1)index.js

const Koa = require('koa');
// 导入bodyParser中间件,解析json
const bodyParser = require('koa-bodyparser')
// 导入错误信息处置方法
const errorHandler = require('./error-handle');
// 导入路由 升级版 动态加载路由
const useRoutes = require('../router');

const app = new Koa();

app.use(bodyParser());

useRoutes(app);

app.on('error', errorHandler);

module.exports = app;

(2)database.js

const mysql = require('mysql2');

const config = require('./config');

// 创建连接池,与数据库进行连接
const connections = mysql.createPool({
  host: config.MYSQL_HOST,
  port: config.MYSQL_PORT,
  database: config.MYSQL_DATABASE,
  user: config.MYSQL_USER,
  password: config.MYSQL_PASSWORD
});

// 测试
connections.getConnection((err, conn) => {
  // err为空即为成功
  conn.connect((err) => {
    if (err) {
      console.log("数据库连接失败", err)
    } else {
      console.log("数据库连接成功")
    }
  })
});

// 通过promise操作数据库 
module.exports = connections.promise();

(3)config.js

// 可加载根目录下的.env文件,将里面的直接加载到环境变量里面
const dotenv = require('dotenv');
const fs = require('fs');
const path = require('path');

dotenv.config();

module.exports = {
  APP_HOST,
  APP_PORT,
  MYSQL_HOST,
  MYSQL_PORT,
  MYSQL_DATABASE,
  MYSQL_USER,
  MYSQL_PASSWORD,
} = process.env;

(4)error-handle.js

const errorType = require('../constants/error-types');

const errorHandler = (error, ctx) => {
  let status, message;

  switch (error.message) {
    case errorType.NAME_OR_PASSWORD_IS_REQUIRED:
      status = 400; // Bad Request
      message = "用户名或者密码不能为空!";
      break;
    case errorType.USER_ALREADY_EXISTS:
      status = 409; //  Conflict 冲突
      message = "用户名已存在!";
      break;
    case errorType.USER_DOES_NOT_EXISTS:
      status = 400; // 参数错误
      message = "用户名不存在!";
      break;
    case errorType.PASSWORD_IS_INCORRENT:
      status = 400; // 参数错误
      message = "密码错误!";
      break;
    case errorType.UNAUTHORIZATION:
      status = 401; // 参数错误
      message = "无效的token!";
      break;
    case errorType.UNPERMISSION:
      status = 401; // 参数错误
      message = "你不具备操作权限!";
      break;
    default:
      status = 404;
      message = "NOT FOUND";
  }

  ctx.status = status;
  ctx.body = message;
}

module.exports = errorHandler;

5、.env文件中的内容

APP_HOST = http://localhost
APP_PORT = 9999

MYSQL_HOST = localhost
MYSQL_PORT = 3306 
MYSQL_DATABASE = kylinKoa 
MYSQL_USER = root 
MYSQL_PASSWORD = root

6、读取文件的方法

// 读取文件的工具方法
const fs = require('fs')
module.exports.getFileJsonData = (filePath) => {
  // 根据文件的路径, 读取文件的内容
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, 'utf-8', (error, data) => {
      if(error) {
        // 读取文件失败
        reject(error)
      } else {
        // 读取文件成功
        resolve(data)
      }
    })
  })
}