iliuyt / blog

1 stars 0 forks source link

koa 源码分析 #19

Open iliuyt opened 6 years ago

iliuyt commented 6 years ago

koa源码之ctx.body

koa源码之 ctx.status ctx.respond ctx.writable

在koa框架中使用exceljs执行流输出遇到的坑

execljs只执行流输出时候,需要调用workbook.xlsx.write方法,参数需要为一个stream,我们可以res返回流传入进去,但是使用koa框架时,传入ctx.res会出现很多坑,网上也有些使用ctx.body=stream的方法来输出流,这就需要额外声明一个转换流来让exceljs写入,让ctx.res读取,总觉得很别扭,所以索性翻开koa的源码和exceljs的源码来看一下如何实现exceljs直接写入res流中

坑1

koa默认在中间件执行完成后,根据body的类型不同做处理,在stream中采用了ctx.body.pipe(res),所以要使用koa返回只能多设置一个流。但是respond方法中有一个ctx.respond===false return的代码,那么koa是允许不执行respond,所以我们设置respond=false,进行自定义返回

坑2

在请求进来时候,默认设置了ctx.status=404,并且在设置ctx.body时,如果没有设置status,会默认设置status为200,所以如果我们没有设置ctx.status=200的话,那么默认的status为404,一样拿不到数据,所以一定要设置status为200

代码实现

const Koa = require('koa');
const execl = require('exceljs');

let app = new Koa();

app.use(async ctx => {
    let workbook = getWorkbook();

    ctx.set('Content-Type', 'application/vnd.openxmlformats');
    ctx.attachment('hello.xlsx');

    ctx.respond = false;
    ctx.status = 200;
    workbook.xlsx.write(ctx.res);

});
app.listen(9001);

function getWorkbook() {
    let workbook = new execl.Workbook();
    let ws = workbook.addWorksheet('二货列表');
    let data = {
        name: '二货',
        sex: '男',
        age: 22,
        address: '上海市长宁区'
    };
    let vals = [];

    for (let key in data) {
        vals.push(data[ key ]);
    }
    ws.addRow(Object.keys(data));

    let i = 0;

    do {
        i++;
        ws.addRow(vals);
    }
    while (i < 1000);
    return workbook;
}

ee-first

同时监听多个事件,如果触发了其中一个事件,释放其他所有事件

on-finished

on-finished通过onFinished方法进行绑定回调,可以多次绑定, onFinished方法会在res上挂载一个单例onFinished,单例onFinished是一个回调函数,onFinished有一个属性queue用于存储通过onFinished设置的回调函数 onFinished通过监听res的end或finish方法,或者是socket的close或error方法来获取请求是否结束,请求结束后,调用单例res上的单例onFinished,清理单例onFinished,并循环调用onFinished上queue的回调方法

koa-compose

将多个中间件function合并,每个中间件的第二个参数为下一个要执行的中间件,中间件为一个promise

koa执行流程

application.js->http.createServer->this.callback->this.createContext->this.handleRequest->fnMiddleware->respond

application文件中的listen中通过http.createServer创建服务,在回调方法中首先创建了ctx,然后执行中间件,最后执行respond方法结束。

创建ctx是通过Object.assign来创建新的context,request,response实例,然后绑定、代理、重写res,req的属性和方法,具体可以看context.js request.js response.js