koa入门

115 阅读4分钟

node-koa实践

参考:juejin.cn/post/695266…

主要目的是,让node作为中间层去请求第三方接口,避开浏览器的跨域问题。

完成后的为解决的问题,第三方接口做了ip限制,除非给本服务接入ip池,因此此解决方案并未使用。

框架&使用

const Koa = require('koa');
const app = new Koa();
       
const port = '8082'
const host = '0.0.0.0'
       
app.use(async ctx => {
    ctx.body = 'Hello World';
});
       
app.listen(port, host, () => {
    console.log(`API server listening on ${host}:${port}`);
 });
       

通过yarn start,启动服务,在浏览器打开就会显示'Hello World',表示可以正常响应。

nodemon使用

  1. 在之前我们启动应用服务采用的方式都是node app.js,但我们每次修改完node代码之后都需要重启服务器即是重新运行命令才能完成修改。
  2. 使用nodemon替代node在开发环境下启动服务.nodemon将监视启动目录中的文件,如果有任何文件更改,nodemon将自动重新启动node应用程序。
  3. nodemon不需要对代码或开发方式进行任何更改。 nodemon只是简单的包装你的node应用程序,并监控任何已经改变的文件。nodemon只是node的替换包,只是在运行脚本时将其替换命令行上的node。
  4. 在node服务中开启debugger模式,

改造路由

  1. 不同路由对应不同的需求
          
          const app = new Koa();
          
          const port = '8082'
          const host = '0.0.0.0'
          
          app.use(async ctx => {
            const { path } = ctx
            if (path === '/a') {
              // 功能 A
              ctx.body = 'a';
            } else if (path === '/b') {
              // 功能 B
              ctx.body = 'b';
            } else if (path === '/c') {
              // 功能 C
              ctx.body = 'c';
            } else {
              ctx.body = 'hello world'
            }
          });
          
          app.listen(port, host, () => {
            console.log(`API server listening on ${host}:${port}`);
          });

当项目功能越来越多时,将路由和路由处理函数堆在一起,后期会难以维护。因此我们拆分一下路由和路由处理函数。
1. 建一个router目录,并使用路由管理插件koa-router管理路由。
2. 在router/index中编写路由代码

         const koaRouter = require('koa-router');
         const router = new koaRouter();
         
         router.get('/a', ctx => {
           ctx.body = 'a'
         });
         
         router.get('/b', ctx => {
           ctx.body = 'b'
         });
         
         router.get('/c', ctx => {
           ctx.body = 'c'
         });
         
         module.exports = router;

3. 修改app/index

//加载路由中间件
app.use(router.routes());
app.use(router.allowedMethods());
  1. 拆分业务和路由
    //app/router/routes.js 路由列表文件
    //app/contronllers/index.js 业务处理统一导出
    //app/contronllers/demo.js 业务处理文件

  2. 具体的拆分实现看代码...

get&post

  1. get请求不带参数没啥问题
  2. get和post请求存在拿不到数据,需要加一个参数解析的中间件。
    1. 我们将参数解析部分拆分出来新建app/middlewares const router = require('../router');

      /**
       * 路由处理
       */
      const mdRoute = router.routes();
      const mdRouterAllowed = router.allowedMethods();
      
      module.exports = [
        mdRoute,
        mdRouterAllowed
      ];
      
    2. 修改app.js .../ const compose = require('koa-compose'); const MD = require('./middlewares/');

      const app = new Koa();
      
      const port = '8082'
      const host = '0.0.0.0'
      
      app.use(compose(MD));
      
      app.listen(port, host, () => {
        console.log(`API server listening on ${host}:${port}`);
      });
      

      koa-compose简化引用中间件的写法.koa-compose串联中间件可以继续了解下原理(洋葱模型) www.cnblogs.com/qiqiloved/p…

  3. 参数校验
    1. 安装koa-bodyparser处理请求中的参数,修改middlewares/index const koaBody = require('koa-bodyparser');

      const router = require('../router');
      
      /**
       * 参数解析
       * https://github.com/koajs/bodyparser
       */
      const mdKoaBody = koaBody({
        enableTypes: [ 'json', 'form', 'text', 'xml' ],
          //解析器仅在请求类型命中 enableTypes 时才会解析,支持json/form/text/xml,默认为['json', 'form']
        formLimit: '56kb',
          //urlencoded身体的限制。如果正文最终大于此限制,则返回 413 错误代码。默认为56kb。
        jsonLimit: '1mb',
        textLimit: '1mb',
        xmlLimit: '1mb',
        strict: true
          //当设置为 true 时,JSON 解析器将只接受数组和对象。默认为true。在严格模式下,ctx.request.body将始终是一个对象(或数组),这避免了大量的类型判断。但文本正文将始终返回字符串类型。
      });
      
      /**
       * 路由处理
       */
      const mdRoute = router.routes();
      const mdRouterAllowed = router.allowedMethods();
      
      module.exports = [
        mdKoaBody,
        mdRoute,
        mdRouterAllowed
      ];
      

不同环境下的配置

image.png

日志配置

const log4js = require('log4js');
const { outDir, flag, level } = require('../config').logConfig;

log4js.configure({
    appenders: { 
        out: { type: 'stdout' },
        cheese: { type: 'file', filename: `${outDir}/receive.log` }
     },
    categories: { default: { appenders: ['cheese'], level: 'info' } },
    // pm2: true
});

const logger = log4js.getLogger();
logger.level = level;

module.exports = () => {
    return async (ctx, next) => {
        const { method, path, origin, query, body, headers, ip } = ctx.request;
        const data = {
            method,
            path,
            origin,
            query,
            body,
            ip,
            headers
        };
        await next();
        if (flag) {
            const { status, params } = ctx;
            data.status = status;
            data.params = params;
            data.result = ctx.body || 'no content';
            if(data.status!==200){
                logger.error(JSON.stringify(data));
                return
            }
            if (ctx.body.code !== 0) {
                logger.error(JSON.stringify(data));
            } else {
                logger.info(JSON.stringify(data));
            }
        }
    };
};

跨域配置

/**
 * 跨域资源共享
 */
const mdCors = cors({
  origin: "*",
  credentials: true,
  allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"],
});

统一返回格式&报错处理

image.png

image.png

参数校验

image.png

整体框架

image.png

  1. common 工具
  2. config 环境配置
  3. controllers 具体的操作
  4. logInfo 日志打印
  5. middlewares 中间层
  6. router 路由控制
  7. verifyParams 参数校验 缺少service层,接入数据库和对数据做具体的处理。

用到的中间件总结

  1. koa-router //路由
  2. koa-compose //洋葱模型*
  3. koa-koarequest //请求接口
  4. koa-bodyparser //参数解析
  5. @hapi/joi //参数校验
  6. @koa/cors //跨域