koa2搭建项目(一)

前言

本篇文章是笔者在搭建Node服务端基础架构时记录的,也是本系列第一篇文章,后续会更新更多笔者在项目中实现的一些想法。随笔者一起走进koa2的项目搭建吧!

环境准备

一、项目初始化

// 1. 确保已安装node.js
node -v
// 2. 确保已安装npm或yarn 
npm -v 或 yarn -v
// 3. 进入空项目文件koa2(项目名)中
cd koa2
// 4. 初始化package.json
yarn init
// 5. 安装koa
yarn add koa -s

二、测试最小系统

  1. 新建app.js
const Koa = require("koa");
const app = new Koa();

app.use(async (ctx) => {
  ctx.body = "hello koa2";
});

app.listen(3000);

console.log("服务器开启: http://localhost:3000/");
  1. 运行系统
node app.js
  1. 游览器打开
http://localhost:3000/

经过上面最小系统的测试,可以检验我们项目初始化是否有问题,可是我们在接下来的工作中将会减少错误。

三、安装中间件

  1. koa-onerror 错误信息
  2. koa-bodyparser 解析body中中的json请求数据
  3. koa-logger 日志
  4. koa-cors 跨域
  5. koa-static 静态资源
  6. koa-router 路由
  7. nodemon 热更新
  8. debug debug模式
yarn add koa-router koa-onerror koa-bodyparser koa-logger koa-cors koa-static nodemon debug --save

四、安装数据库

  1. 因为作者对mysql较为熟悉,数据库采用MySql
yarn add mysql --save

五、最终目录结构

  1. middelware 中间件
  2. controller、控制层 业务代码
  3. config 数据库等设置
  4. dbhelper sql语句
  5. routes 路由
  6. modal 对象层
│  app.js
│  package.json
│  REAAME.md
│  yarn.lock
├─app
│  ├─controller
│  │      users.js
│  │      
│  └─modal
│          users.js      
├─bin
│      www 
├─config
│      constant.js
│      database.js
│      dbPool.js
├─dbhelper
│      users.js
├─middlewares
│  │  index.js
│  └─middleware
│          message.js
├─public
│      favicon.ico
│      index.html
├─routes
│  │  index.js
│  └─route
│      users.js
└─utils
        autoLoadFile.js
        
基础配置

一、改写app.js

  1. 注册中间件
const Koa = require("koa");
const app = new Koa();

const onerror = require("koa-onerror");
const bodyparser = require("koa-bodyparser");
const logger = require("koa-logger");
const router = require("./routes/index");
const cors = require("koa-cors");

// 注册error
onerror(app);
// 注册bodyparser
app.use(bodyparser());
// 注册日志
app.use(logger());
// 注册静态资源
app.use(require("koa-static")(__dirname + "/public"));
// 注册跨域
app.use(cors());
// 注册自定义中间件
require('./middlewares/index')(app);
// 注册路由
app.use(router.routes(), router.allowedMethods());

// logger-handling
app.use(async (ctx, next) => {
  const start = new Date();
  await next();
  const ms = new Date() - start;
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});
// error-handling
app.on("error", (err, ctx) => {
  console.error("server error", err, ctx);
});

module.exports = app;
  1. koa-onerror、koa-bodyparser、koa-logger、koa-cors等中间件为koa常见中间件,这里不再过多介绍用法
  2. koa-router和middlewares做了一些自定义操作,下面会介绍到

二、添加项目入口

  1. 创建入口文件 bin/www
#!/usr/bin/env node

/**
 * Module dependencies.
 */

 var app = require('../app');
 var debug = require('debug')('demo:server');
 var http = require('http');
 
 /**
  * Get port from environment and store in Express.
  */
 
 var port = normalizePort('3000');
 // app.set('port', port);
 
 /**
  * Create HTTP server.
  */
 
 var server = http.createServer(app.callback());
 
 /**
  * Listen on provided port, on all network interfaces.
  */
 
 server.listen(port);
 server.on('error', onError);
 server.on('listening', onListening);
 
 /**
  * Normalize a port into a number, string, or false.
  */
 
 function normalizePort(val) {
   var port = parseInt(val, 10);
 
   if (isNaN(port)) {
     // named pipe
     return val;
   }
 
   if (port >= 0) {
     // port number
     return port;
   }
 
   return false;
 }
 
 /**
  * Event listener for HTTP server "error" event.
  */
 
 function onError(error) {
   if (error.syscall !== 'listen') {
     throw error;
   }
 
   var bind = typeof port === 'string'
     ? 'Pipe ' + port
     : 'Port ' + port;
 
   // handle specific listen errors with friendly messages
   switch (error.code) {
     case 'EACCES':
       console.error(bind + ' requires elevated privileges');
      //  process.exit(1);
       break;
     case 'EADDRINUSE':
       console.error(bind + ' is already in use');
      //  process.exit(1);
       break;
     default:
       throw error;
   }
 }
 
 /**
  * Event listener for HTTP server "listening" event.
  */
 
 function onListening() {
   var addr = server.address();
   var bind = typeof addr === 'string'
     ? 'pipe ' + addr
     : 'port ' + addr.port;
   debug('Listening on ' + bind);
 }
 

三、改写package.json

  1. 添加启动脚本
"scripts": {
    "start": "node bin/www",
    "dev": "nodemon bin/www" // 热更新
},
  1. 完整package.json
// package.json
{
  "name": "koa2",
  "version": "1.0.0",
  "description": "koa2+mysql",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "node bin/www",
    "dev": "nodemon bin/www"
  },
  "dependencies": {
    "debug": "^4.3.3",
    "koa": "^2.13.4",
    "koa-bodyparser": "^4.3.0",
    "koa-cors": "^0.0.16",
    "koa-logger": "^3.2.1",
    "koa-onerror": "^4.1.0",
    "koa-router": "^10.1.1",
    "koa-static": "^5.0.0",
    "mysql": "^2.18.1",
    "nodemon": "^2.0.15"
  }
}

四、添加静态页面

  1. 在app.js中注册静态资源时我们知道将静态资源放在了public文件夹下
  2. 创建index.html页面
<!DOCTYPE html>
<html lang="en">
    ...
</html>
  1. 添加favicon.ico图标

五、启动项目

  1. 启动项目
// yarn dev 为热更新
yarn dev
  1. 游览器打开
http://localhost:3000/
  • 通过koa-static静态资源处理,根目录默认打开/public/index.html
  • 访问时不需要加上public,即http://localhost:3000/文件名 这种形式即可访问。

路由配置
  1. 将路由按模块拆分,方便路由解耦.
  2. 将注册路由与业务层解耦,方便业务逻辑复用.
  3. 在路由模块添加路由前缀,使路由更易懂易读.
  • 使用过webpack的人应该知道, 通过require.context可以拿到符合条件的上下文,今天我们也模拟context实现一个自动加载文件的方法。

一、实现自动加载文件函数

  1. 创建文件 utils/autoLoadFile.js
#!/usr/bin/env node
const path = require("path");
const fs = require("fs");
const getPathInfo = (p) => path.parse(p);

/**
 * @description // 递归读取文件,类似于webpack的require.context()
 * @param {String} directory 文件目录
 * @param {Boolean} useSubdirectories 是否查询子目录,默认false
 * @param {array} extList 查询文件后缀,默认 ['.js']
 */
const autoLoadFile = (
  directory,
  useSubdirectories = false,
  extList = [".js"]
) => {
  const filesList = [];
  // 递归读取文件
  function readFileList(directory, useSubdirectories, extList) {
    const files = fs.readdirSync(directory);
    files.forEach((item) => {
      const fullPath = path.join(directory, item);
      const stat = fs.statSync(fullPath);
      if (stat.isDirectory() && useSubdirectories) {
        readFileList(path.join(directory, item), useSubdirectories, extList);
      } else {
        const info = getPathInfo(fullPath);
        extList.includes(info.ext) && filesList.push(fullPath);
      }
    });
  }
  readFileList(directory, useSubdirectories, extList);
  // 生成需要的对象
  const res = filesList.map((item) => ({
    path: item,
    data: require(item),
    ...getPathInfo(item),
  }));
  return res;
};

module.exports = autoLoadFile;

  1. 使用示例
const context = require("../utils/autoLoadFile");
const fileList = context(path.join(__dirname, "./route"), true);

二、路由自注册

  1. 创建路由自执行文件 routes/index.js
const router = require("koa-router")();
const path = require("path");
const context = require("../utils/autoLoadFile");

/**
 * @param {Array} arr 需要注册路由的文件列表
 */
function importAll(arr) {
  arr.forEach((key) => {
    // 这种方式为嵌套路由
    router.use("/api", key.data.routes(), key.data.allowedMethods());
  });
}
importAll(context(path.join(__dirname, "./route"), false));

module.exports = router;

三、注册路由

  1. app.js
// 引入路由自执行文件
const router = require("./routes/index");
// 注册路由
app.use(router.routes(), router.allowedMethods());

四、创建路由模块

  1. 基础搭建已经好了,创建一个模块试试
// routes/route/users.js

const router = require("koa-router")();
// 模块路由前缀
router.prefix("/users");

router.post("/", function (ctx, next) {
	ctx.body = "this a users response!";
});

/**
 * 用户登录接口
 * @param {username} 用户名
 * @param {password} 用户密码
 */
router.post("/login", async (ctx) => {
	const request = ctx.request.body;
	const { username, password } = request;
	if(username && password) {
	    ctx.body = {
	        "code":200,
            "msg":"success",
            "data": "登录成功"
	    }
	}
});

module.exports = router;

  1. 最终路由path: localhost:3000/api/users/login

    • /api 为路由前缀
    • /users 为模块路由前缀
    • /login 具体接口
  2. 结果

中间件配置

一、中间件自注册

  1. 创建自执行文件 middlewares/index.js
const path = require("path");
const context = require("../utils/autoLoadFile");

/**
 * @param {Array} arr 需要注册中间件的文件列表
 */
const install = (app) => {
  context(path.join(__dirname, "./middleware"), false).forEach((key) => {
    app.use(key.data);
  });
};

module.exports = install;

二、中间件注册

  1. app.js
// 注册自定义中间件
require('./middlewares/index')(app);

三、响应体中间件

  1. 创建封装响应体的中间件 message.js
// middlewares/middleware/message.js

module.exports = async (ctx, next) => {
  ctx.res.$success = (data, code = 200) => {
    const _data = {
      code,
    };
    if (typeof data === "object") {
      _data.msg = "success";
      _data.data = data;
    } else {
      _data.msg = data;
    }
    ctx.body = _data;
  };

  ctx.res.$error = (err, code = 500) => {
    const _data = {
      code,
    };
    if (typeof err === "object") {
      _data.msg = "error";
      _data.data = JSON.stringify(err);
    } else {
      _data.msg = err;
    }
    ctx.body = _data;
  };

  await next();
};

四、响应体中间件使用示例

 router.post('/login', async (ctx) => {
  const request = ctx.request.body;
  const { username, password } = request;
  if (username&&password) {
    ctx.res.$success('登录成功');
  } else {
    ctx.res.$error("请求失败", 403);
  }
})

五、响应体中间件结果

// 成功
{
    "code":200,
    "msg":"success",
    "data":"登录成功"
}

// 失败
{
    "code":403,
    "msg":"error",
    "data":"请求失败"
}
参考资料
  1. Koa官网 https://koajs.com/
  2. Koa中间件使用之koa-router https://www.jianshu.com/p/f169c342b4d5
  3. koa-router官方文档 https://wohugb.gitbooks.io/koajs/content/route/koa-router.html
  4. Koa-bodyparser 源码 https://github.com/koajs/bodyparser
  5. Koa-error 源码 https://github.com/koajs/error#readme
  6. koa-logger源码 https://github.com/koajs/logger
总结

本文从项目初始化角度出发,搭建了一个比较简单的项目架构,实现了路由的自动注册、中间件的自动注册。

后续会从构建REST风格的 API、登录token验证、权限管理等角度继续更新文章。

我是前端小溪,欢迎感兴趣的同学关注下前端小溪公众号,也欢迎加我微信wxl-15153496335

gitee仓库地址 求个star 感谢!!!

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值