Koa2的基本使用

Koa2的基本使用

[TOC]

简介

  • koa2完全使用Promise并配合async来实现异步。但代码看起爱像是同步的。

入门程序

  • 创建app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
    const Koa = require('koa');
    // 创建一个Koa对象表示web app本身:
    const app = new Koa();
    // 对于任何请求,app将调用该异步函数处理请求:
    //参数ctx是由koa传入的封装了request和response的变量,我们可以通过它访问request和response,next是koa传入的将要处理的下一个异步函数。
    app.use(async (ctx, next) => {
    await next();
    ctx.response.type = 'text/html';
    ctx.response.body = '<h1>Hello, koa2!</h1>';
    });
    // 在端口3000监听:
    app.listen(3000);
    console.log('app started at port 3000...');

    async标记的函数称为异步函数,在异步函数中,可以用await调用另一个异步函数。

  • koa把很多async函数组成一个处理链,每个async函数都可以做一些自己的事情,然后用await next()来调用下一个async函数。我们把每个async函数称为middleware,这些middleware可以组合起来,完成很多有用的功能。app.use()的顺序决定了middleware的顺序。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    app.use(async (ctx, next) => {
    console.log(`${ctx.request.method} ${ctx.request.url}`); // 打印URL
    await next(); // 调用下一个middleware
    });
    app.use(async (ctx, next) => {
    const start = new Date().getTime(); // 当前时间
    await next(); // 调用下一个middleware
    const ms = new Date().getTime() - start; // 耗费时间
    console.log(`Time: ${ms}ms`); // 打印耗费时间
    });
    app.use(async (ctx, next) => {
    await next();
    ctx.response.type = 'text/html';
    ctx.response.body = '<h1>Hello, koa2!</h1>';
    });

    如果没有调用await next(); 那么后面的middleware都不会执行。(可以利用这个进行用户权限判定)

创建url映射

  • 将不同的url映射到不同的函数,通过koa-router实现。

  • 处理get请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    const Koa = require('koa');
    // 注意require('koa-router')返回的是函数:
    const router = require('koa-router')();
    const app = new Koa();
    // log request URL:
    app.use(async (ctx, next) => {
    console.log(`Process ${ctx.request.method} ${ctx.request.url}...`);
    await next();
    });
    // 注册一个get函数,并注明映射的url
    router.get('/hello/:name', async (ctx, next) => {
    var name = ctx.params.name;
    ctx.response.body = `<h1>Hello, ${name}!</h1>`;
    });
    router.get('/', async (ctx, next) => {
    ctx.response.body = '<h1>Index</h1>';
    });
    // add router middleware:
    app.use(router.routes());
    app.listen(3000);
    console.log('app started at port 3000...');
  • 处理post请求

    ​ post请求通常会发送一个表单,或者JSON,它作为request的body发送,但无论是Node.js提供的原始request对象,还是koa提供的request对象,都不提供解析request的body的功能!

    ​ 所以,我们又需要引入另一个middleware(koa-bodyparser)来解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中。

    1. 在package.json中添加依赖

      1
      "koa-bodyparser": "3.2.0"
    2. 解析post请求

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      //引入koa-bodyparser
      const bodyParser = require('koa-bodyparser');
      //koa-bodyparser必须在router之前被注册到app对象上。
      app.use(bodyParser());
      router.post('/signin', async (ctx, next) => {
      //如果该字段不存在,默认值设置为''。
      var
      name = ctx.request.body.name || '',
      password = ctx.request.body.password || '';
      console.log(`signin with name: ${name}, password: ${password}`);
      if (name === 'koa' && password === '12345') {
      ctx.response.body = `<h1>Welcome, ${name}!</h1>`;
      } else {
      ctx.response.body = `<h1>Login failed!</h1>
      <p><a href="/">Try again</a></p>`;
      }
      });
      app.use(router.routes());
      app.listen(3000);
      console.log('app started at port 3000...');
  • 封装一个简单的url映射框架

    我们将处理url映射的js按照功能划分,并统一放在controller包中。

    1. 新建controller包,在其内新建user.js

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      var fn_signin = async (ctx, next) => {
      var
      name = ctx.request.body.name || '',
      password = ctx.request.body.password || '';
      console.log(`signin with name: ${name}, password: ${password}`);
      if (name === 'koa' && password === '12345') {
      ctx.response.body = `<h1>Welcome, ${name}!</h1>`;
      } else {
      ctx.response.body = `<h1>Login failed!</h1>
      <p><a href="/">Try again</a></p>`;
      }
      };
      //将signin方法暴露出去
      module.exports = {
      'POST /signin': fn_signin
      };
    2. 新建controller.js负责扫描cintroller包下的js文件,并为其注册到router

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      const fs = require('fs');
      function addMapping(router, mapping) {
      for (var url in mapping) {
      if (url.startsWith('GET ')) {
      var path = url.substring(4);
      router.get(path, mapping[url]);
      console.log(`register URL mapping: GET ${path}`);
      } else if (url.startsWith('POST ')) {
      var path = url.substring(5);
      router.post(path, mapping[url]);
      console.log(`register URL mapping: POST ${path}`);
      } else {
      console.log(`invalid URL: ${url}`);
      }
      }
      }
      function addControllers(router,dir) {
      var files = fs.readdirSync(__dirname + dir+'');
      var js_files = files.filter((f) => {
      return f.endsWith('.js');
      });
      for (var f of js_files) {
      console.log(`process controller: ${f}...`);
      let mapping = require(__dirname + '/controllers/' + f);
      addMapping(router, mapping);
      }
      }
      module.exports = function (dir) {
      let
      controllers_dir = dir || 'controllers', // 如果不传参数,扫描目录默认为'controllers'
      router = require('koa-router')();
      addControllers(router, controllers_dir);
      return router.routes();
      };
    3. 新建app.js,调用controller.js提供的方法

      1
      2
      3
      4
      5
      6
      7
      // 导入controller middleware:
      const controller = require('./controller');
      ...
      // 使用middleware:
      app.use(controller());