koa中间件 ——— koa-body原理

961 阅读3分钟

koa-body是一个解析请求体的插件,它能帮我们处理post方式的json请求体、form表单请求体、文件流请求体等形式。业务应用之所以需要这个中间件,是因为原生的NodeJS或Koa应用它们仅能解析get请求参数,无法解析post请求体参数。

流程概览

koa-body解析请求体主要分为如下流程:

  1. 从request流中读取请求体数据
  2. 对不同格式的请求体使用对应的解析器parse(json请求体、form表单请求体、普通文本请求体、分片请求)
  3. 将parse完的请求数据挂载到ctx.req.body上,供业务层使用

接下来我们结合源码一一讲解下这3个流程,koa-body是如何做的

1. 从request流中读取请求体数据

从request中读取请求体数据是借用raw-body这个插件实现的,入口在getRawBody方法中,主要实现在readStream中。我们用如下伪代码诠释它主要做的工作

    function getRawBody(req, options) {
        return new Promise((resolve, reject) => {
            let buffer = ''
            req.on('data', (chunk) => {
                buffer += decoder.write(chunk);
            });
            req.on('end', () => {
                resolve(buffer);
            })
        })
    }

如上可以看到实际流程较简单,就是监听request对象的data事件,将接收到的流分片数据拼接,在接收完成触发end事件是,将拼接完的数据resolve返回。

但这里我们注意到在拼接流分片数据前,数据会经过decoder处理,这里decoder其实就是utf-8编码解码处理,因为不同系统数据编码方式是不一样的,用utf-8抹平操作系统差异解码出人类可读的数据。

这里可能某些对NodeJS原生请求流处理不太熟悉的同学会问,为什么req上监听data事件就能拿到请求体的流数据呢?
好问题,其实这里的req(ctx.req)就是NodeJS原生http模块的request对象,koa框架只是对应该对象做了一些扩展,而http模块的request对象继承于原生Stream对象,实现了Stream的一切功能

image.png

image.pngrequest又是一个可读流,所以我们自然可以操作这个流去读取数据。

2. 对不同格式的请求体使用对应的解析器parse(json请求体、form表单请求体、普通文本请求体、分片请求)

从中间件的入口

image.png 我们可以看到,会通过koa的is工具类判断当前请求的类型,然后if/else if匹配对应类型格式,使用相应的解析器对应处理。
这里我们拿最通用的json解析器来看: 解析器是由co-body提供的

image.png 而json解析则位于json.js文件中,用伪代码解释其整体运作如下

    async function jsonParser(req, opts) {
        // 前面说过的通过raw-body读取到的请求流数据
        const str = await raw(req, opts);
        return JSON.parse(str);
    }

其实就是用我们熟知的json反序列化工具JSON.parse将请求体处理成json object,这样就方便后续的业务层去使用啦~
至此,一个request请求体的解析器解读完成,接下来我们看看koa-body是将parse完的请求数据放到了哪

3. 将parse完的请求数据挂载到ctx.req.body上,供业务层使用

image.png 可以看到如果opts.patchKoa为true的话,我们的json请求体则会被挂载到ctx.req.body上,ctx.req则是贯穿当前koa单次请求的全局对象,这样再适合不过了,既可以让业务层全局读取,又不会污染到其他请求,因为每次请求对应的req对象都是唯一的。

细心的小伙伴会看到前面说过了会有如果这个opts.patchKoa为true前提,继续翻阅入口函数,我们发现

image.png koa-body对于用户的options配置做了一些列的初始化,而pathKoa默认是true的,所以我们不用做任何处理,koa-body就默认帮我们把解析完的请求体挂载到了ctx.req上!