KOA初体验

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nzyalj/article/details/78010968

起因

最近在看《第一行代码Android》,看到网络请求那章的时候需要服务器相应android应用发出的请求,书中使用的Apache服务器,但是呢,作为nodejs的粉丝,为什么不用nodejs呢,正好久闻KOA的大名,就准备拿来玩玩


本文使用的第三方模块版本

这里写图片描述


第一个应用!

首先,使用npm安装koa

npm install --save koa
const Koa = require('koa');

const app = new Koa();

const runningPort = 3000;

app.use(ctx => {
  ctx.body = 'Hello world!';
});

app.listen(runningPort, function () {
  console.log(`Server running on ${runningPort}`);
});

这里写图片描述

这里的ctx 封装了node中的requestresponse,也包含KOA自己的requestresponse对象,使用 ctx.reqctx.res 调用前两者,使用 koa.requestkoa.response 调用后两者

注意,不支持 ctx.res.statusCodectx.res.writeHead()ctx.res.write()ctx.res.end()

更多详细的api见KOA官网


使用 koa-router 进行路由

注意观察的话可以看到,koa使用use方法来处理,但是并没有指定特定的路由,那么对于上面的第一个应用,不管域名后跟什么路径,浏览器上显示的都是 ‘hello world’

这里写图片描述

当然可以使用 ctx.request.url 来进行判断,但是还有是个选择,使用插件 koa-router 来管理路由

npm install --save koa koa-router
const Koa = require('koa');

const app = new Koa();
const router = new require('koa-router')();

const runningPort = 3000;

// 使用 koa-router,并添加处理404以及服务器内部错误的逻辑
app
  .use(router.routes())
  .use(router.allowedMethods())
  .use(async (ctx, next) => {
    try {
      await next();
// 处理404
      if (ctx.status === 404) {
        ctx.body = "Hello 404 error!";
      }
    } catch (err) {
// 处理500
        ctx.response.status = 500;
        console.error(err);
    }
  });

// ctx.body 相当于是 ctx.response.body 的简写
router.get('/', ctx => {
  ctx.body = 'Hello world!';
});

router.get('/index', ctx => {
  ctx.body = 'Hello index!';
});

// 可以读取url中的数据
router.get('/name/:username', ctx => {
  ctx.body = `Hello ${ctx.params.username}`;
});


app.listen(runningPort, function () {
  console.log(`Server running on ${runningPort}`);
});

这里写图片描述

从这里可以发现,koa-router的功能还是比较强大的,当然,这也只是一部分,它可以处理各种http请求,更全的api使用指南请见alexmingoia/koa-router


使用koa-router 配合 koa-body 处理POST数据

服务器总归是要接受数据的,那么给如何处理呢,使用上面的 koa-router 是一个选择,配合着插件 koa-body 可以很好地处理表单提交的数据

npm install -save koa koa-body koa-router
const Koa = require('koa');

const app = new Koa();
const router = new require('koa-router')();
const koaBody = require('koa-body')();

const runningPort = 3000;

app
  .use(router.routes())
  .use(router.allowedMethods())
  .use(async (ctx, next) => {
    try {
      await next();
      if (ctx.status === 404) {
        ctx.body = "Hello 404 error!";
      }
    } catch (err) {
        ctx.response.status = 500;
        console.error(err);
    }
  });

// 加入 koa-body 插件
router.post('/api/userinfo', koaBody, (ctx) => {
  console.log("get post!");
// 显示POST提交数据
  console.log(ctx.request.body);
// 返回给数据发送方
  ctx.body = `data return from server: ${JSON.stringify(ctx.request.body)}`;
});


app.listen(runningPort, function () {
  console.log(`Server running on ${runningPort}`);
});

由于是为Android的http学习准备的代码,这里就不用Android应用做测试了,所有就用bash中的 curl 来模拟POST请求

curl -H "Content-Type: application/json" -X POST -d '{"username":"xyz","password":"xyz"}' http://localhost:3000/api/userinfo | more

可以看到服务端显示出提交的数据,并返回数据给数据发送方

这里写图片描述


是用KOA发送本地JSON/XML文件

数据是不会一开始就存在内存中的,我们需要从本地进行文件读取并发送,KOA可以很好地处理数据文件,需要使用到 async/await 来处理异步操作,使用 Promise 来包装异步操作

在应用文件所在目录中创建 data 文件夹,准备一份 data.json 与一份 data.xml 文件用于读取测试

// data/data.json
[
  {
    "id": "1",
    "name": "aaa",
    "version": "2.1"
  },
  {
    "id": "2",
    "name": "bbb",
    "version": "1.7"
  },
  {
    "id": "3",
    "name": "ccc",
    "version": "5.3"
  }
]
<!-- data/data.xml -->
<apps>
  <app>
            <id>1</id>
            <name>Google Maps</name>
            <version>1.0</version>
    </app>
    <app>
                <id>2</id>
                <name>Chrome</name>
                <version>2.1</version>
    </app>
    <app>
                <id>3</id>
                <name>Google Play</name>
                <version>2.3</version>
    </app>
</apps>
// 引入node核心模块
const fs = require('fs');
const path = require('path');

const Koa = require('koa');

const app = new Koa();
const router = new require('koa-router')();
const koaBody = require('koa-body')();

const runningPort = 3000;

app
  .use(router.routes())
  .use(router.allowedMethods())
  .use(async (ctx, next) => {
    try {
      await next();
      if (ctx.status === 404) {
        ctx.body = "Hello 404 error!";
      }
    } catch (err) {
        ctx.response.status = 500;
        console.error(err);
    }
  });

// 根据变量 type 类型返回不同的文件
// KOA的一大特色是 async/await 的集成用于处理异步操作
router.get("/api/get_data/:type", async ctx => {
  await new Promise((resolve, reject) => {
    let filetype = "";
        // 判断type的数值
    switch (ctx.params.type) {
      case "xml":
        filetype = "xml";
        break;
      case "json":
        filetype = "json";
        break;
      default:
        ctx.status = 404;
                ctx.body = 'No such file type!';
        resolve();
    }
        // 获取指定文件类型
    fs.readFile(path.join(__dirname, `data/data.${filetype}`), 'utf8', (err, data) => {
      if (err) {
        ctx.status = 500;
        reject(err);
      }
            // 设置返回数据的类型,相当于 ctx.response.set()
      ctx.set('Content-Type', `application/${filetype}`);
      ctx.body = data;
      resolve();
    })
  });
});

app.listen(runningPort, function () {
  console.log(`Server running on ${runningPort}`);
});

这里写图片描述


使用KOA的进行大文件下载

在《第一行》中第十章的最佳实践为下载文件,那用KOA如何提供api给android进行下载呢?

可以用node中的stream

使用node核心模块 fscreateReadStream 读取文件,并将其赋给 ctx.response.body,因为参考api文档,其值可以为一下几个中的一种:

  • string written
  • Buffer written
  • Stream piped
  • Object || Array json-stringified
  • null no content response

准备一个视频文件(越大越好),命名为 test.mp4,放置于 data 目录中

const fs = require('fs');
const path = require('path');

const Koa = require('koa');

const app = new Koa();
const router = new require('koa-router')();
const koaBody = require('koa-body')();

const runningPort = 3000;

app
  .use(router.routes())
  .use(router.allowedMethods())
  .use(async (ctx, next) => {
    try {
      await next();
      if (ctx.status === 404) {
        ctx.body = "Hello 404 error!";
      }
    } catch (err) {
        ctx.response.status = 500;
        console.error(err);
    }
  });

// 设置下载文件的路径
router.get('/api/download/test.mp4', async ctx => {
  await new Promise((resolve, reject) => {
        // 设置文件的名字以及路径
    const FILE_NAME = 'test.mp4';
    const FILE_PATH = path.join(__dirname, `data/${FILE_NAME}`);

        // 新建读取流
        // highWaterMark 参数用于设置buffer的大小,默认为 64*1024 byte
        // 详见 [这里](https://stackoverflow.com/questions/27641571/changing-readstream-chunksize)
    const readStream = fs.createReadStream(FILE_PATH, {highWaterMark: 128 * 1024});
    let size = 0;

        // 进行赋值
    ctx.response.body = readStream;
        // 设置body的总长度,便于前端下载时显示总数量
    ctx.response.length = fs.statSync(FILE_PATH).size;
        //  强制浏览器下载,而不是播放
    ctx.response.set('Content-disposition', `attachment; filename= ${FILE_NAME}`);
    resolve();

        // 每次有数据读入时显示进度
    ctx.response.body.on('data', (chunk) => {
      size += chunk.length / 1024.0 / 1024.0;
      console.log(`${size} MB`)
    });
        // 读取结束后输出 'end'
    ctx.response.body.on('end', () => {
      console.log('end');
    });
  })
});

app.listen(runningPort, function () {
  console.log(`Server running on ${runningPort}`);
});

由于在本地(localhost)下载速度极快,看不出进度,所有选择在远程服务器上进行部署,修改监听端口为80,进行演示

这里写图片描述


总结

其实写上述代码的目的是为了满足对《第一行代码Android》第九,十章代码测试的需要,顺便也学习了KOA框架,觉得这个框架很新颖,和Express的用法有很大的不同,有空再好好把玩把玩

猜你喜欢

转载自blog.csdn.net/nzyalj/article/details/78010968
今日推荐